I just published a new gem, inherited-attributes, for Active Record that works with the Ancestry gem to provide a tree data structure the ability to inherit attributes from their parent node, or farther up the tree. We’ve been using this technique for a long time to support configuring our multi-tenant application.
Once the gem is installed, its very simple to configure:
create_table :nodes, :force => true do |t|
t.string :ancestry, :index => true
class Node < ActiveRecord::Base
From there, you can access the effective attributes which look up the tree ancestry to find a value to inherit.
root = Node.create!
child = Node.create!(:parent => root, :value => 12)
grandchild = Node.create!(:parent => child)
root.effective_value # nil
child.effective_value # 12
grandchild.effective_value # 12 -- inherited from child
There are more options and examples in the gem, including has-one relationships, default values and support for enumerations.
We’ve found it helpful and writing a gem made this code much easier to test. What code do you have that would be easier to test as a gem or would be useful to others?
Rails 4.1 introduced ActiveRecord Enums, a handy little feature that lets you store an integer in a database column, but use symbols or strings in your code. Until today, I’ve been presenting these in select boxes with code like this:
This generates a perfectly acceptable select box if you don’t need internationalization and the enum names you’ve picked are good enough to present to the user. If thats not the case, or you to change the presentation without changing the code, a new helper method leveraging Ruby’s internationalization (i18n) features can be a good approach.
Keep reading for how I changed the presentation of enumerations in a drop down without changing my models or renaming the enumeration names.
Here is how we can provide alternate labels for some of our drop down values.
First, create a new helper that will pass each enumeration name through i18n to perform a label conversation
Second, change your form to call the helper
Third, provide the translations.
A few notes:
- If the translation is missing, the code’s default is to fall back to the titleized text. If you want to omit options where the translation is missing, You can modify the methods to call enum_to_translated_option with a default of blank and the blank options will be removed from the select.
- enum_to_translated_option is useful in a show template.
- It may be useful to sort the options after translation. That is an easy addition to the helper.
Is this useful for you? Is there a better way? Let me know!
Upgrading from Rails 3.2 to Rails 4.0 is not a trivial task. Sure, there is a guide, but when you upgrade you’ll probably be upgrading a lot of your gems, maybe your jQuery and jQuery-UI versions and then there are all the undocumented unintentional changes that can cause you grief. If you’ve got a good test suite (you have one right?) you’ll catch a lot of these, but some will leave you scratching your head. This one tripped me up for a while: we had a very simple scope stop working in Rails 4.
The scope model below works in Rails 3.2, but fails in Rails 4 when used with an association:
Here is the failure:
In the second query, post.comments.model.count the query has lost the comments scope as well as the conditional defined in our model scope and it counts all comments in the system.
The fix is quite easy, just change the name of the scope to something like not_index.
This seems to be change in associations that is most clearly illustrated by this:
I could never quite figure out where in activerecord this changed, but model was not a great choice for a scope name. This executable gist has everything in this post if you want to explore this more.
What surprises have you found with Rails 4?
I was a little surprised to discover that when you update an association through nested attributes, it won’t touch the parent record. It makes sense when you consider that Rails is optimizing by not writing records that have not changed, but if were using updated_at on the model for caching you may be surprised.
With these models:
This code will not change updated at on the post record:
As shown in the log:
The fix is very simple. Just touch the post record from the comment if you need updated_at to change on posts.
And now our post is updated:
Of course there are other ways to do this…
The code for this is on github.