Ken MayerKen Mayer
From customer requirements to releasable gem
edit Posted by Ken Mayer on Sunday May 13, 2012 at 11:42AM

One of the many pleasures of working at Pivotal Labs is that we are encouraged to release some of our work as open source. Often during the course of our engagements, we write code that might have wide-spread use. Due to the nature of our contracts, we can not unilaterally release such code. Those rights belong to the client. And rightly so. So, it is an even greater pleasure when one of our clients believes in "giving back" to the community, as well.

One such example is this modest gem, attribute_access_controllable which allows you to set read-only access at the attribute level, on a per-instance basis. For example, let's say that you have a model Person with an attribute birthday, which, for security purposes, cannot be changed once this attribute is set (except, perhaps, by an administrator with extraordinary privileges). Any future attempts to change this attribute will result in a validation error.

e.g.

> alice = Person.new(:birthday => '12/12/12')
=> #<Person id: nil, attr1: nil, created_at: nil, updated_at: nil, read_only_attributes: nil, birthday: "0012-12-12"> 
> alice.attr_read_only(:birthday)
=> #<Set: {"birthday"}> 
> alice.save!
=> true
> alice.birthday = "2012-12-12"
=> "2012-12-12" 
> alice.save!
ActiveRecord::RecordInvalid: Validation failed: Birthday is invalid, Birthday is read_only
> alice.save!(:skip_read_only => true)
=> true

Setting this up is trivial, thanks to a Rails generator which does most of the heavy lifting for you.

rails generate attribute_access Person

After that, you need only know about one new method added to your class:

#attr_read_only(*attributes) # Marks attributes as read-only

There are a few others, but this one, plus the new functionality added to #save and #save! will get you quite far.

And if that's all that you were looking for when you stumbled across this article, then there's no need to read any further. Go install the gem and have fun (and may your tests be green when you expect them to be).

Ken MayerKen Mayer
TDD Action Caching in Rails 3
edit Posted by Ken Mayer on Wednesday March 28, 2012 at 08:10PM

On my current project, we needed to prove that an action cache was working as expected. Alas, the blogosphere had either out-of-date or unhelpful information. So, after many experiments, we came up with an RSpec test that does what we want. It seems ugly to me, and I hope there's a better way. The names have been changed to protect the guilty. Any resemblances to actual classes and methods are purely coincidental.

Ken MayerKen Mayer
Dry DevOps with heroku_san
edit Posted by Ken Mayer on Sunday March 25, 2012 at 02:23PM

Quiz time!

  1. How many times (each day) have you typed this at your console? git push heroku master and then forgotten to run heroku run rake db:migrate --app yellow-snow-3141 or heroku ps:restart
  2. Does your script support a multi-stage environments?
  3. Do you remember how to get to the application's console process?
  4. Is your application's configuration consistent across all stages?
  5. Are you deploy scripts tested?

Stephan HagemannStephan Hagemann
Never use shared examples groups! Usually.
edit Posted by Stephan Hagemann on Friday March 09, 2012 at 12:45PM

Shared example groups are a feature of rspec that allow specifying common behavior in a reusable group of specs.

I believe that there is a very specific way in which one can benefit from shared examples groups (and should keep them) and many more in which they come in handy at some point and should be refactored away from as development continues.

Stephan HagemannStephan Hagemann
Specific interfaces - in the small
edit Posted by Stephan Hagemann on Wednesday March 07, 2012 at 11:55AM

Everyone on the Web I found who states that quote I was looking for says "I don't know who said it, but be 'Generous on input, strict on output'" (or some variation on this). While I am unsure about the first proposition, I wholeheartedly agree with the second.

Ken MayerKen Mayer
SF Standup - Feb 13, 2012 - The Sound of One Hand
edit Posted by Ken Mayer on Monday February 13, 2012 at 09:00AM

Cries for help

Capybara is reporting "stale elements" for no good reason.

Any experience, war stories upgrading from jQuery 1.6 to 1.7, especially with the whole event delegation API change? [crickets] See interestings...

Interestings

Have a really big spec file that takes a long time to run? http://rubygems.org/gems/parallel_split_test, from our own Michael Grosser, will run a big spec file in smaller chunks.

url helpers cache results for better performance, but they seem to be losing custom options (in this case a locales setting).

In jQuery 1.6, triggering an event in an unattached DOM node would propagate the event up into the document, even though it was never attached to it. jQuery 1.7 (maybe 1.7.1) fixes this behavior.

Stephan HagemannStephan Hagemann
Test your Rake tasks!
edit Posted by Stephan Hagemann on Sunday February 05, 2012 at 10:05AM

There are several reasons why you should test your Rake tasks:

  • Rake tasks are code and as such deserve testing.
  • When untested Rake tasks have a tendency to become overly long and convoluted. Tests will help keep them in bay.
  • As Rake tasks typically depend on your models, you (should) loose confidence in them if you don't have tests and are attempting refactorings.

Jacob MaineJacob Maine
Standup 2012/2/1: Speed kills
edit Posted by Jacob Maine on Wednesday February 01, 2012 at 10:17AM

Interesting Things

  • If you haven't noticed, Jasmine tests are at least twice as fast in Chrome as they are in Firefox. Closing the inspection pane makes it even faster. Be aware that part of the speed is from Chrome's aggressive caching, which can lead to erroneous test results.
  • One team is using Backbone's local storage. When they add model.clear() after every test run, their tests go from 20 seconds to over 100 seconds. Someone suggested the silent: true option, to suppress the change events that clear triggers.
  • To avoid bugs in minified JS put semicolons in the right spots. The easiest way to do that is to run a tool like JSLint or JSHint over your code. Add it to your test suite to prevent mistakes.

Ask for Help

  • "In IE8 the numbers don't show up on ordered lists if we dynamically create lis"

Or rather, they do, but only after hovering over the list. The common wisdom is that this has been broken in IE for a long time.

  • "Our project does some DNS resolution. Is there a preferred way to mock this in tests?"

No suggestions.

  • "When replying to an email each email system adds different junk to the message. We're processing those incoming replies. Any standard way to strip out the junk?"

Everyone is using the ugly regex approach. Are there mail gems that handle this?

Jacob MaineJacob Maine
Standup 2012/1/31: The bleeding edge
edit Posted by Jacob Maine on Tuesday January 31, 2012 at 10:09AM

Interesting Things

  • There's a new release of Backbone - 0.9.0. It's billed as a release candidate for 1.0, and seems to be a bit buggy, as RCs can be. However, it's exciting to see that Backbone is getting close to that milestone.
  • You should default boolean fields to true or false, at the database layer. Otherwise your queries have to deal with three-valued logic.
  • Rails 3.2 has some unexpected behavior. First, the generated form ids have changed ... what used to be id=user_new is now id=new_user. Second, if your routes file is missing an entry, you will no longer get errors in controller tests. If you liked that behavior, try out request specs.

Ask for Help

  • "Anyone seen problems with the latest REE and iconv?"

Everything works on the Linux machines, but on Macs, there's an error about an unrecognized target encoding. Iconv works on the command line, so it's something about REE.

Jacob MaineJacob Maine
Standup 1/30/2012: It's all about sharing
edit Posted by Jacob Maine on Monday January 30, 2012 at 10:07AM

Interesting Things

  • should_not render_with_layout can be flaky. For example, asserting that an XHR request doesn't have a layout could fail if the request renders an email template that does have a layout. These matchers are not very sophisticated, mostly doing string matching, not real template resolution.

Ask for Help

Since the models are all built beforehand, it's tricky to write tests that make assertions about relative time. There were a variety of suggestions. You can write all the tests relative to a model: some_time.should == model.created_at - 3.days. You can wrap the entire suite and fixture generation in a Timecop block, but you'll have to deal with losing that context whenever any test calls Timecop.return.

  • "How do I separate multiple FixtureBuilder files, but allow them to reference objects created in each other?"

Typically you want to separate fixtures into smaller files. But, you also often want to reference objects in other files, for example so you can assign a user from user_fixtures.rb to a post in post_fixtures.rb. One solution is to eval each file in a loop, so they can share instance variables. You can also use User.where(:name => "Tom") but that's awkward and repetitive.

  • "Any suggestions for a CMS that would allow lots of different visual presentations of the content?"

The questioner is experimenting with branding. Not many suggestions... a shared database perhaps?

Other articles: