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).
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.
Quiz time!
- How many times (each day) have you typed this at your console?
git push heroku masterand then forgotten to runheroku run rake db:migrate --app yellow-snow-3141orheroku ps:restart - Does your script support a multi-stage environments?
- Do you remember how to get to the application's console process?
- Is your application's configuration consistent across all stages?
- Are you deploy scripts tested?
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.
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.
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.
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.
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 thesilent: trueoption, to suppress thechangeevents thatcleartriggers. - 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?
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_newis nowid=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.
Interesting Things
should_not render_with_layoutcan 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
- "How do I test relative time with FixtureBuilder?"
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?
