Code Climate's engineers write about Ruby and Rails, covering refactoring, security vulnerabilities, ActiveRecord design and tooling.
HN commenters cite its fat ActiveRecord model refactoring post as mind-opening and its YAML and Rails vulnerability posts when discussing data formats.
The sibling comment alludes to parsing calendar events (possibly .ical files). In general you need it for any kind of (de)serialization -- and as the recent java deserialization bug[1] (along with similar bugs in python, ruby) illustrates -- even in a mature language, "common practice" might not be "best practice" (everyone should've known that the various uses of blind deserialization was a bad idea -- but that doesn't really matter after the fact -- lots of high-profile libraries were completely broken). So "just use a library" might not be possible.
Any time you take native objects/data and persist them, or marshal them, you need a parser -- or a parser library. That might mean reading some CSV, parsing some XML, reading some YAML, JSON or what-not.
Then there's configuration that does more than set some variables, like for Nginx, Apache (web server), etc.
One might want to have a short-hand for defining templates -- either for general meta-programming, or just to generate a layout-language (eg: html, css). If you're doing markdown (or markdown-like) processing, you might want to have some assurances that that's all that code can do -- generate html. Or you might want do meta-programming on some higher level, like generating interfaces for some java-beans based on a CSV structure. For more on meta-programming in Java, I recommend (the now slightly dated: http://www.amazon.com/Program-Generators-Java-Craig-Cleaveland/dp/0130258784 ). It's a shame he didn't name it as he…
I see this assertion increasingly frequently, and it puzzles me: that if we're not currently using an object to encapsulate state, we should prefer class methods. What's the justification? Other than four characters saved (".new"), what's the benefit in this approach?
State encapsulation is just one feature of objects. We may not be using it right now, but the only thing you achieve with the above code is removing future flexibility. It costs us nothing to allow for the future possibility of encapsulated state, so why rule it out? Adding complexity when you don't yet need it, fine, I quite understand objecting to that; but here you're actually putting in effort to make a future modification more difficult.
Funnily enough, Code Climate's previous blog entry was on precisely this topic, and is worth a read:
http://blog.codeclimate.com/blog/2012/11/14/why-ruby-class-methods-resist-refactoring/
For what it's worth, I don't see the point in instantiating a whole new spam checker for every piece of content, so I'd probably change the OP's example to read:
class UserContentSpamChecker
TRIGGER_KEYWORDS = %w(viagra acne adult loans xrated).to_set
def is_spam?(content)
flagged_words(content).present?
end
protected
def flagged_words(content)
TRIGGER_KEYWORDS & content.split
end
end
If I really really wanted access to a default spam checker via a global constant I can always add the following:
class…
My analysis: layering. YAML doesn't in itself execute untrusted code, but it has a bunch of semi-experimental features most people don't know about or use, and one of those features (deserializing arbitrary classes) has a side effect that sets values on certain other classes -- not written by the people who wrote the YAML parser -- that then, often later on in their lifecycle, execute this untrusted code. I'm not saying this as an excuse -- I have long mistrusted YAML's blasé complexity, not to mention Rails' anarchic pass-HTTP-params-directly-into-the-DB pattern -- but as an explanation of how they missed it.
Also, more than other communities, Ruby has a cultural gap between the people developing the language and core libraries and the people using it to write web apps and frameworks.
Here's two good technical writeups of the exploit as it applies to Rails apps: http://blog.codeclimate.com/blog/2013/01/10/rails-remote-code-execution-vulnerability-explained/ http://ronin-ruby.github.com/blog/2013/01/09/rails-pocs.html
Rails didn't bastardize MVC, users of Rails bastardized MVC. Over the years we've seen the progressions of fat controllers to skinny controllers and fat models. Now, we're seeing the progression of using models only for responsibilities related to persisting the object. This leads to extracting additional domain logic into Presenters, Value, Service objects, etc.
Avdi Grimm has a great book about this called Objects on Rails[1] and Bryan Helmkamp from Code Climate has a good introductory blog post on it as well[2].
Rails implementation of MVC is perfectly fine. It's when people lean on MVC for everything without applying "classic" Object-Oriented design. That's when things become a mess.
1 - http://objectsonrails.com/
2 - http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Have you read the CodeClimate post '7 Patterns to Refactor Fat ActiveRecord Models'? (http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/). This book is a whole lot more awesome.
It covers ground that we otherwise learn over a few years of painful experience. To mention a few: namespaces to organize code, extensive coverage of ActiveRecord bloat and how to deal with it, and controllers: consistently thin. And to top it off all: a whole chapter dedicated to managing CSS using BEM.
The book has a lot of code examples and conventions. There is also a chapter titled "Building Applications To Last" that has some great recommendations on how to approach changing fashions, on using gems and thoughts on tests. It is also quite a short book, under 150 pages.
Thanks. I added the workaround on Day 1, and have tested it using the following:
http://blog.codeclimate.com/blog/2013/01/10/rails-remote-code-execution-vulnerability-explained/
I've just had this itching question about whether or not using a cloud host (ala Heroku) is limiting my ability to use a patchfile the way I otherwise would on a dedicated host. I'm imagining a nightmare scenario of another vulnerability without such an easy workaround, requiring use of the patchfile.
I've been searching/reading constantly since my initial post and have learned about "rails:freeze:gems" and "rake gems:unpack" which I guess would serve as patchable, as long as Heroku uses them when told to.