Samuel Mullen

What could possibly go wrong?

Step by Step: Setting Up Minitest and Cucumber With Rails

I’ve tried a couple times in the past to successfully set up a Rails application with both minitest and Cucumber; each time, I ran in to issues. I’m not entirely sure why I ran into the issues I did, but I just couldn’t seem to get things working. Recently, I managed to get everything going with almost no effort. Here’s what I did.

Step 1: Creating and Configuring a New Rails App with minitest

For the sake of simplicity, we’ll assume that we’re creating a new Rails application. To do so, we’ll want to skip the standard usage of Test::Unit

Initializing a New App
1
rails new <app_name> --skip-test-unit

Immediately after this, we’ll need to install and initialize the minitest-rails gem

Gemfile
1
2
3
4
5
group :test, :development do
  ...
  gem 'minitest-rails', '~> 0.3.0` # as of this writing
  ...
end

bundle install and then initialize the gem in the app.

minitest installation
1
rails g mini_test:install

The last thing we’ll do is make sure our generators behave accordingly. To do that, add the following logic to the config/application.rb config block:

config/application.rb
1
2
3
config.generators do |g|
  g.test_framework :mini_test, :spec => true, :fixture => false
end

This turns on minitest’s spec functionality and turns off fixture generation. If you don’t want to use minispec or if you do want to use fixtures, just alter the values accordingly.

Step 2: Setting up Cucumber

Okay, at this point we have our foundational test suite set up. Now we need to add cucumber to the mix.

Add cucumber-rails to your Gemfile as you normally would.

Gemfile
1
2
3
4
group :test do
  gem 'cucumber-rails', "~> 1.3.0"
  gem "database_cleaner", "~> 0.9.1"
end

Call bundle install and then call the cucumber Rails generator:

cucumber:install
1
rails g cucumber:install

Now, add the following lines of code to features/support/env.rb. (I placed mine near the top.)

features/support/env.rb
1
2
3
require 'minitest/spec'
World(MiniTest::Assertions)
MiniTest::Spec.new(nil)

For greater separation, the previous lines of code above can be added to a different support file.

Step 3: Running the Tests

At this point, everything should work. Let’s find out with a couple simple tests.

minispec Test

Create the following test file and model:

test/models/foo_test.rb
1
2
3
4
5
6
7
require_relative "../minitest_helper"

describe Foo do
  it "must be valid" do
    Foo.must_respond_to :new
  end
end

And the corresponding model file:

app/models/foo.rb
1
2
class Foo
end

Notice in the first line of foo_test.rb that we’re using require_relative instead of a normal require call. We do this to avoid the need to use rake. Now, rather than running bundle exec rake minitest:models and running all the models, we can execute bundle exec ruby test/models/foo_test.rb.

Cucumber Test

Create the following Cucumber “feature” file:

test.feature
1
2
3
4
5
6
7
8
Feature: test
  In order to test cucumber
  a user
  wants to successfully land on the home page

  Scenario: foo
    Given I am on the home page
     Then I should see "Ruby on Rails: Welcome aboard"

And an associated step file:

test_steps.rb
1
2
3
4
5
6
7
Given /^I am on the home page$/ do
  visit "/"
end

Then /^I should see \"([^"]+)"$/ do |string|
  page.has_content?(string).must_equal true
end

Run the test and everything should be green.

Cucumber execution
1
bundle exec cucumber features/test.feature

Final Thoughts

I like minitest a lot, mainly because it’s fast, it’s simple, and it’s included in Ruby 1.9+, but there are drawbacks; the two main ones for me are its readability and the lack of integration with the vim-rails plugin. Neither of these “drawbacks” are deal breakers, however: the vim-rails plugin will eventually support it, and there are gems to make the suite more “civilized” (listed below).

So if you like the idea of a really simple and fast test suite, and you don’t mind the syntax, give minitest a go on your next project. Oh, and let me know how it works out for you.

Further Reading

The Introvert’s Networking Survival Guide: Large Events

I am an introvert. As a freelance developer, that works out well for me (mostly). It allows me to work from my home office with little to no contact with the majority of mankind for days on end. But as a freelancer, I also recognize that I have to get out and meet people in order to find new work.

A little over a year ago, I was invited to a BNI networking event. I thought it would be the same thing I’ve seen at any large gathering of people: people gathering to those they already know, new people getting ignored, uninteresting small talk, etc.

I was wrong.

From the moment I stepped through the door, I was greeted with a dozen hands thrust out to shake mine, people asking me my name, what I did, where I was located, then telling me their name, telling me what they did, wanting to know my pitch, telling me theirs, asking me if I was a member, if I wanted to be a member.

It was too much.

As soon as I could, I got out of the room and into a hallway, and called my wife. Yes, I wanted to talk to her, but at that moment, I also needed an excuse to escape and focus on one person. I slipped back into the room once the meeting had officially started, and counted the minutes until I could leave.

Since then, I’ve been to a number of other networking events. Each event had its own characteristics and quirks, and I’ve learned more about networking from each event I’ve attended.

Tip #1 :: No One Expects the Spanish Inquisition

Expectations are everything, and we enter in to every situation with at least some sort of preconceived idea of what is going to happen. Under normal circumstances, being a little off about what to expect isn’t a problem, but when your expectations are completely wrong, as mine were at the BNI event (and my 10th grade Geometry class), it can be difficult to recover from.

Knowing this about myself, I find it helpful to do a little pre-networking research. Not a lot, but just enough to have a better grasp of what to expect. Here are some of the things I look for:

  • Setting :: Where is the event taking place?Is it a bar or restaurant? A business? On a college campus?
  • Size :: How many people are expected to attend? This is an important one for me.
  • Format ::
    • Is this a presentation plus networking event?
    • Is it just networking?
    • Is there a structure to the event?
  • Topic :: Is there an overriding theme to the event?
  • People-group :: Is the event specific to a particular profession or more generalized?

The Spanish Inquisition Sketch

Tip #2 :: What is Your Quest?

“Why are you going to this event?” Have you ever asked yourself that question? Obviously you’re going there to increase your network and meet people, but really, to what end?

Are you looking to make a sale? To find someone to partner with? Are you looking for investors, or employees? Are you going just to practice networking (don’t laugh)?

Having a purpose in going to an event can 1) help determine if you should even go; 2) help determine if the event in question is going to help meet that goal; and 3) keep you on track while you are at the event.

If you don’t have a purpose in going to a networking event, you probably shouldn’t be going.

Three Questions

Tip #3 :: Are you the Judean People’s Front?

Okay, so you know where the event is going to be and what it’s going to be like, and you have a purpose in going, the last thing you need to know is who to talk to. If you’ve figured out why you are going to the event, this part should direct you to the people you should look for.

As an example, if I go to a networking event and my goal is to find a new client, I’m not going to spend a lot of time speaking with other freelancers, except to find out if they are looking to offload a client. Instead, I’m going to target IT decision makers in the markets I’m looking to get into.

Simple, right? But as introverts, it’s a lot easier to gravitate to those people we know, and to corners we feel safe in. Sometimes, you just have to embrace the suck and talk to people.

So based on your purpose for going to the event, who are the people who would best help you meet your goals?

The People’s Front

Bonus Tip :: Why Did You Say “Good Morning”?

Okay, last point. Most networking events - thankfully - last less than three hours. That means you have that much time to acclimate yourself to the environment, figure out how to meet your goals, and then find the people you need to meet. The last thing you need to do is waste the time with small talk.

I don’t care what the Bene Gesserit say, small talk, not fear, is the mind-killer.We fall in to the trap of small talk because it’s safe and it’s comfortable, but it’s also completely forgettable. The point of networking is to connect with people. If your conversation is forgettable, what value is the connection?

So what do you talk about? I like to use some of the following questions and roll on from there:

  • Why are you here and what kinds of people are you looking to connect with?
    • You will probably want to ask that less directly. The point is just to find out if there is potential value in the connection.
  • What do you do? It’s cliche and small talky, but it has to be asked.
  • What is your favorite part about what you do?
    • I ask this solely to ensure there is a positive experience to the time. Well, that, and it’s usually fun to see people get excited about their work/family/etc.
    • Note: I don’t ask what their least favorite part is
  • What are some of the struggles you are currently facing in your work?
    • This isn’t asking what they don’t like.
    • This is discovering if you can fix their problems.
  • and so on…

Silly Job Interview

As far as networking goes, I usually prefer meeting people for coffee or lunch. It fits my temperament, it’s less intense, and it generally makes for a better conversation. That’s the advice most “networking for introverts” articles provide. While more intimate types of networking fits the introverts’ personality, it doesn’t address the problem of what to do if you’ve managed to stumble into a larger event, which is to say, find out what to expect, figure out your purpose, know who you want to meet, and avoid small talk.

If all else fails, you can always hide out in the hallway and talk to your wife on the phone.

My Letter to the City of Shawnee, Kansas

As a rule, I don’t post anything relating to politics, but since this has nothing to do with the banalities of left vs. right vs. independents, I am making an exception.

I am so completely excited about Google Fiber, I could burst, and I am terrified that rather than opening our doors to the future and to future growth, our local governments will instead see this as an opportunity to make a buck and flex its muscles.

The Internet represents freedom. We need to open our arms to it.

Ladies and Gentlemen of the Governing Body of Shawnee,

My name is Samuel Mullen. I’m a freelance programmer specializing in web and iPhone development. As such, I am very keen to learn what, if anything, has been done to encourage Google Fiber to come to Shawnee. Obviously we are some distance from the initial locations selected by Google, but they will inevitably spread out, and I am anxious for it’s arrival. I am convinced my business can only grow with its arrival: through new companies, technological improvements, and networking.

I believe the greater Kansas City metro area is in a unique position to usurp the technology crown which Silicon Valley currently wears. New businesses, new ideas, new people, and new opportunities are all on the horizon, we must be prepared to take up the mantle of Silicon Prairie.

Please let me know that you, the current leaders of Shawnee, are as excited about this technology as I am, and that you are doing everything in your power to eliminate road blocks which might hinder the arrival of Google Fiber.

Thank you, Samuel Mullen

If you live in the Kansas City metro area and you are not part of the Google Fiber rollout, I would encourage you to contact your city officials as well.

Update: August 24, 2012

The City of Shawnee did end up reaching out to Google. This is their letter.

Getting More Out of the Rails Console

Even at it’s most basic functionality, the console is indispensable in the Rails developer’s arsenal of tools. Whether it’s running a query, testing a chain of methods, or executing small blocks of code, the console may be the most useful tool available to the Rails developer. With that in mind, doesn’t it make sense to do what we can to get the most out of it?

IRB

.irbrc

Under the hood, the Rails Console is just IRB (Interactive Ruby), so anything you can do with IRB, you can do in the console. This means you can modify your IRB environment and .irbrc file to define methods to use at the console. Here are three methods I frequently use:

Useful IRB methods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
\# return a sorted list of methods minus those which are inherited from Object
class Object
  def interesting_methods
    (self.methods - Object.instance_methods).sort
  end
end

\# return an Array of random numbers
class Array
  def self.test_list(x=10)
    Array(1..x)
  end
end

\# return a Hash of symbols to random numbers
class Hash
  def self.test_list
    Array(:a..:z).each_with_object({}) {|x,h| h[x] = rand(100) }
  end
end

Of course this isn’t even scratching the surface of what’s possible. Check out what other people are doing:

The Last Expression

While working in the console, have you ever typed out a bit of code to return a value and then realize you forgot to assign the returned value to a variable? You then have to go back into the console history, move your cursor to the beginning of the line, add the variable, and then execute the code again.

Ugh, what a pain!

Unbeknownst to most people, IRB places the output of the last command into the _ variable. Here, let me show you:

_ example
1
2
3
4
 > Array(1..10)
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 > _
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

Now, as cool as that is, you have to understand that _ always contains the output of the last expression. This means if you try call a method on it, it will then contain the output of the method executed.

_ detailed example
1
2
3
4
5
6
7
8
 > Array(1..10)
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 > _
 => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
 >  _.map {|i| i * 2}
 => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
 > _
 => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

Forking IRB

When working in the console, it’s sometimes desirable to have another instance to play with. It may be that you don’t want to lose what you were working with, or you just need another scratch area, but whatever the case, you can create a new console (IRB) session by calling irb at the prompt (note: you’ll use irb for the rails console as well).

I typically don’t use this. If I need another IRB instance, I just open a new tmux pane or window and work there.

If this sort of method fits your workflow, I highly recommend reading Gabriel Horner’s in depth post on IRB commands

The Rails Console

Models

One of the things you will want to make extensive use of in the console are your app’s models. The Rails console is a great way to play with your models and an alternative way of accessing your data.

Model Example
1
2
3
4
5
6
7
8
9
10
11
 > u = User.find 1234; nil
=> nil

 > u.name
 => "Foo Man"

 > u.email = "fooman@example.com"
 => "fooman@example.com"

 > u.save
 => true

The “app” object

The app object is used by test processes to mimic system interactions. Through this object, we can access routing information and even make requests to our app.

app Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# displaying path information
 > app.users_path
=> "/users/

 > app.user_path(User.last)
=> "/users/42

# making app requests

 > app.get app.user_path(User.last)
=> 200

 > app.response.body
=>  => "<!DOCTYPE html>\n<html>\n  <head>\n    <title>..."

The “helper” Object

I really don’t think I can do a better job on this than what Nick Quaranto already did in his “Three Quick Rails console tips” post.

Reloading the Environment

If you make changes to your app while still in the console, you will need to reload your console session with the reload! command. You will also need to reinstantiate objects which existed prior to the reload for them to recognize your changes.

reload! Example
1
2
3
4
5
6
7
8
 > u = User.find 1234; nil

\# changes made to User model outside the console 

 > reload!
Reloading...
 => true
 > u = User.find 1234; nil

Better Output

At one time it seemed like everyone was writing a gem for improving the IRB experience, but it appears like that particular endeavor has since been largely ignored. The one project that appears to be currently active is the awesome_print gem.

I’ve used this gem in the past, and it really does improve the output and IRB experience. It also supports pry.

In a pinch, you can format the output as YAML with the y command.

y example
1
2
3
4
5
6
7
8
 > y Array.test_list(5)
---
- 1
- 2
- 3
- 4
- 5
 => nil

Avoiding Output

Running commands in the console can get pretty noisy. Output follows every command which is run. To get around this, just end your command with a semicolon and nil.

Avoiding Output
1
2
 > u = User.last; nil
=> nil

In the above example, u still contains the “last” user record, it just doesn’t print out all the output that would normally be produced.

The Sandbox

Sometimes it would be nice to open up a console session and mess around with the data to see what happens. But if you do that, the data’s messed up. The solution to that is to lunch the console with the --sandbox flag. When launched, you can handle the data, tweak it, and destroy it, all without fear of harming any of your data.

Rails Console Sandbox
1
2
3
4
5
rails console --sandbox

Loading development environment in sandbox (Rails 3.2.1)
Any modifications you make will be rolled back on exit
 >

Conclusion

In my workflow, the rails console is an indispensible tool. It allows me to test out ideas, access the database, run minor tasks, and even calculate basic math. I can’t imagine developing Rails applications without it, because I know how painful it is to be deprived of such a tool in other languages and frameworks.

What are your favorite IRB and console tricks?

Validating Booleans

I ran into an instance today wherein I needed to validate that a boolean field was either true or false and not null. I tried using validates :fieldname, :presence => true, but since :presence uses #blank? under the hood, it was reading false as not being present. (Why is false considered blank?)

Anyway, I needed a validator to test whether an attribute was either true or false and I couldn’t find anything among the standard validators, so I wrote my own.

Just plop this file in your app’s lib/validators directory.

truthiness_validator.rb
1
2
3
4
5
6
7
class TruthinessValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value == true || value == false
      record.errors[attribute] << "must be true or false"
    end
  end
end

In your model, add the validation like so:

some_model.rb
1
2
3
4
5
6
7
class SomeModel < ActiveRecord::Base
  ...

  validates :field_name, :truthiness => true

  ...
end

For more information on writing validators, see Getting Started with Custom Rails3 Validators.

Why It’s Okay to Write Crummy Code

There is an inherent need in good developers to write “good” code. We continually look for ways to simplify, enhance, improve, and speed up the code we - or others - have written. We also look back on our older code and shake our head in disgust.

Perhaps it is because of this love of “good” code or perhaps it is because of what we know we can do in more familiar languages, or perhaps, even, it’s because we know what we don’t know, but it seems there is a sort of barrier between learning and using a new technology (by technology I mean a language, framework, methodology, etc.).

Rather than doing, sometimes it’s easier to continue reading and researching. “Just one more section”, “just one more chapter”, “just one more book”. It makes sense, of course, we have to have some sort of foundation upon which to build; and so we read a little bit more.

But the argument that we need to learn just a little more before we begin doing is a hollow argument: we learn far more by doing than by reading. Think back upon when you’ve learned the most: was it when you were in class listening to lectures, or in lab applying what you learned; when you read the chapter, or when you completed the exercises; when you read the API, or tried to implement it?

But the “doing” is difficult, we end up hitting speed bumps and roadblocks, we go in circles in roundabouts, and occasionally we even find ourselves in dark alleyways or hit dead ends, but eventually, if we persevere, we find our way onto the right path. It’s those little success which make all the difference. Finding out what doesn’t work is a learning experience all to itself, but it is the successes, both large and small, which make all the difference. It is the successes which provide us with the energy and motivation to continue on.

With this in mind, go ahead and write crummy code. Write obtuse, ugly, poorly thought out code. Disregard methodologies and best practices, violate principles, ignore everything which hinders you from learning and playing. Fool around and explore, find the speed bumps, the roadblocks, and even the dark alleyways; it’s how you learn and how you eventually succeed.

So to Hell with being ready and writing perfect code; go have fun. If you’re not having fun, you’re doing it wrong anyway.

Evolution of an Apple Fanboy

  • 1997 - 2010: I was an anti-apple, Linux fanboy.
  • 18 months ago: I bought an iPad (16GB) instead of a Kindle at my wife’s behest
  • 17 months ago: I won a second iPad (32GB)
  • 1 year ago: I bought a MacBook Pro so I could be on the same page as my Ruby coworkers
  • Six months ago: I won a 13” MacBook Air
  • 10 days ago: I started learning Objective-C and iOS development.
  • This morning: I searched out iOS developer podcasts
  • Currently: I’m planning on getting the next iPhone, the new iPad, and the Apple TV (little black box).

Things got really weird somewhere in that mess, and I have no idea how it happened. I still see myself as a Linux guy, but I can no longer deny my appreciation for Apple products. Who says I’m closed-minded?

Compiling Objective-C Programs at the Command Line

I’ve switched my learning efforts from JavaScript frameworks to iOS development. I’m using my morning 30 minute period of time to study the ”Objective-C Programming” book from ”Big Nerd Ranch”. I’m also taking an hour out of my normal billable hours to study Big Nerd Ranch’s ”iOS Programming”.

Many of the chapters of the “Objective-C Programming” book have exercises at the end to help the student cement what he or she has learned. As I’ve been going through them, I’ve become very frustrated with needing to open a new XCode project for each exercise. It would be a lot easier for me just to code up the solutions in Vim and compile them at the command line.

I wasn’t sure it was possible, but after a quick Googling, I ran across a sample chapter from “Programming in Objective C” which had the solution.

In a nutshell:

  1. Create an objective-c implimentation file (*.m)
  2. Add the necessary code
  3. Compile it using the following command
Compiling obj-c Programs
1
$ clang -fobjc-arc –framework Foundation filename.m -o output_filename

For those who are familiar with GCC or other command-line compilers, that should be enough to get you started. For the rest of you, you may want to stick with XCode. Seriously, I’m not sure how long I’ll stick with this method.

You should note that writing and debugging Objective-C programs from the terminal is a valid approach. However, it’s not a good long-term strategy. If you want to build Mac OS X or iOS applications, there’s more to just the executable file that needs to be “packaged” into an application bundle. It’s not easy to do that from the Terminal application, and it’s one of Xcode’s specialties. Therefore, I suggest you start learning to use Xcode to develop your programs. There is a learning curve to do this, but the effort will be well worth it in the end.

As stated at the end of the linked article (chapter), this is not something you’ll want to use to build MacOS or iOS applications. I’m only doing things this way to simplify completion of exercises.

Thirty Minutes a Day

Thirty minutes doesn’t seem like much time, especially for programmers. It takes that much time just to get into the “zone”. It takes at least that much time just to research some libraries or problems. You can read a chapter of a book in thirty minutes, but can you imagine working on a project for only thirty minutes a day?

But if thirty minutes a day is the only amount of time you have, the question changes from “can you?” to “do you want to?”.

I get around thirty minutes to myself in the mornings before my family wakes up and I have to get everyone ready for their day. For a long while I used that precious amount of time to read blog posts, twitter, email, or pitter it away in some other manner. I’ve not done that this year.

Since January, I’ve used those thirty minutes to write ten blog posts and read three books. It doesn’t sound like much, but if I were to keep up that pace, I would have forty blog posts written and 12 books read by the end of the year. That’s not too shabby…If I were to keep up the pace.

I’ve been wanting to learn Backbone.js for a while now, but the free time I have during the rest of my day involves my family. The only time I have is my thirty minute window. So I’ve decided to use that time to work on a project using backbone.

I won’t have time to write - although I have found time this Saturday morning - and I won’t have as much time to read, but I really want to learn backbone.js.

I get thirty minutes a day. It’s not much, but it’s mine, and it’s not wasted.

ActiveRecord::Base.store - Pros, Cons, and Usage

In my post on ActiveRecord’s new explainmethod, I mentioned there were “two” features in ActiveRecord (introduced in Rails 3.2) I wanted to look at. This post covers the second method: ActiveRecord::Base.store.

Store provides a simple means of accessing and storing key/value pairs related to a model. The API documentation uses the example of a User model which has settings. “Settings” may not in themselves warrant their own model, but there still needs to be a means of accessing them. This is where Store comes in to play.

Behind the curtain, store is just a Hash which gets serialized and deserialized upon save and load. It has a few accessors added in to make it look less hashy, but the hash is still there nonetheless as we’ll see later.

Prior to Rails 3.2, if you needed this functionality you had three choices: 1) implement it yourself; 2) Muck up your table with a bunch of extra fields; 3) create another table to store all the fields.

For the sake of laziness, I’ll use the same project I used in my previous post: https://github.com/samullen/ar_explain

Setup

In our example, we’re going to allow users to toggle the different types of email they want to receive. The first thing we’ll need to do is add the field in which to store the settings.

Add contact_settings to Users
1
2
3
4
5
class AddContactSettingsToUsers < ActiveRecord::Migration
  def change
    add_column :users, :contact_settings, :string, :limit => 4096
  end
end

You’ll likely need some space for your settings so set the string limit to something large. Alternatively, you can use text instead of string if you like, but I tend to run more conservative.

Next, we’ll need to let the user model know we’ll be using the contact_settings field as a store.

User Model
1
2
3
4
5
class User < ActiveRecord::Base
  store :contact_settings, accessors: [ :daily_email, :weekly_email, :account_email ]

  has_many :owner_restaurants
end

Well that wasn’t too difficult.

Usage

Like I said, the field used as the store is really just a hash. You can see that in the console when I retrieve the first user record:

Retrieving the First User
1
2
3
4
5
6
7
1.9.3p125 :001 > u = User.first
  User Load (0.3ms)  SELECT `users`.* FROM `users` LIMIT 1
 => #<User id: 1, name: "John Galt", email: "john@galtsgulch.com", created_at: "2012-03-08 01:50:22", updated_at: "2012-03-08 01:50:22", contact_settings: {}> 
1.9.3p125 :002 > u.contact_settings
 => {}
1.9.3p125 :003 > u.contact_settings.class
 => Hash

Instead of accessing the attributes through contact_settings as you would a normal Hash, you access them as if they were attributes on the model itself.

Accessing Store Attributes
1
2
3
4
5
6
7
8
9
10
1.9.3p125 :005 > u.weekly_email = true
 => true
1.9.3p125 :006 > u.account_email = true
 => true
1.9.3p125 :007 > u.daily_email = false
 => false
1.9.3p125 :008 > u
 => #<User id: 1, name: "John Galt", email: "john@galtsgulch.com", created_at: "2012-03-08 01:50:22", updated_at: "2012-03-08 01:50:22", contact_settings: {:weekly_email=>true, :account_email=>true, :daily_email=>false}> 
1.9.3p125 :009 > u.contact_settings
 => {:weekly_email=>true, :account_email=>true, :daily_email=>false}

As mentioned earlier, store fields are just hashes. This means you can access them and use methods on them just like any other hash. You can even add attributes not defined in the store.

Accessing Store as a Hash
1
2
3
1.9.3p125 :010 > u.contact_settings[:foo] = "bar"
1.9.3p125 :012 > u.contact_settings
 => {:weekly_email=>true, :account_email=>true, :daily_email=>false, :foo=>"bar"}

If we were to save the record and look at it in the database (without :foo => "bar"), it would look like this.

User Record
1
2
3
4
5
6
7
8
9
10
11
mysql> select * from users;
+----+--------------+----------------------+---------------------+---------------------+-------------------------------------------------------------------+
| id | name         | email                | created_at          | updated_at          | contact_settings                                                  |
+----+--------------+----------------------+---------------------+---------------------+-------------------------------------------------------------------+
|  1 | John Galt    | john@galtsgulch.com  | 2012-03-08 01:50:22 | 2012-04-04 11:23:47 | ---
:weekly_email: true
:account_email: true
:daily_email: false
 |
|  2 | Howard Roark | howard@architect.com | 2012-03-08 01:50:22 | 2012-03-08 01:50:22 | NULL                                                              |
+----+--------------+----------------------+---------------------+---------------------+-------------------------------------------------------------------+

Pros

I think many of the “pros” for ActiveRecord.store are pretty obvious: it eliminates the need for yet another table or extra fields; simplifies adding new attributes; they work just like normal model attributes. Did I mention that validations work on Store attributes? They do, and Rafal Wrzochol has a great write up on using them

So what are the drawbacks?

Cons

There are a number of philosophical reasons against using the new Store feature. The main argument against is that the data really isn’t normalized. But we’re using Rails, which means we have a tendency to abuse normalization for the sake of the application anyway.

Another minus is that dirty attributes don’t work quite as well in the Store. For instance, you can’t call User#weekly_email_changed?. The only thing you can do is check if the Store field has changed (e.g. User#contact_settings_changed?). Again, it’s not really a huge issue and I imagine this will get resolved in future releases.

Really the main “con” with regard to using store - and this really is a big deal - is that you can’t perform efficient searches on the field. The only way to perform a search is by surrounding the search term with “%” characters.

1
SELECT * FROM users WHERE contact_settings LIKE '%weekly_email: true%';

If the percent sign was just on the end, that would be one thing, but with the leading “%” it’s now the slowest possible way of searching the database.

I really think the new Store feature in Rails 3.2 is a nice feature. They’ve done it well and made its usage fairly seamless (i.e. store attributes look and act like any other attribute). If your application’s database is fairly large or if you plan on running a lot of queries against the attributes in the Store (e.g. gathering lots of metrics) you may want to use a separate table. For most applications out there, however, this is a very safe and sane solution.

Further Reader