Today I Learned

ins8s

37 posts about #rails

Add React With Webpacker To A New Rails App

Webpacker makes it easy to manage app-like JavaScript in the context of a Rails app. React is a great candidate for this kind of webpack-powered JavaScript processing pipeline.

To set up a new Rails project with Webpack and React wired up, add the --webpack=react flag:

$ rails new rails-react-app --webpack=react

As part of the generated app, you will get a app/javascript/packs directory with a hello_react.jsx file that has a really basic React component.

source

Change The Nullability Of A Column

Do you have an existing table with a column that is exactly as you want it except that it needs to be changed to either null: false or null: true?

One option is to use ActiveRecord’s change_column_null method in your migration.

For example to change a nullable column to null: false, you’ll want a migration like the following:

def change
  change_column_null :posts, :title, false
end

Note, if you have existing records with null values in the title column, then you’ll need to deal with those before migrating.

If you want to make an existing column nullable, change that false to true:

def change
  change_column_null :posts, :title, true
end

Generating And Executing SQL

Rails’ ActiveRecord can easily support 90% of the querying we do against the tables in our database. However, there is the occasional exceptional query that is more easily written in SQL — perhaps that query cannot even be written with the ActiveRecord DSL. For these instances, we need a way to generate and execute SQL safely. The sanitize_sql_array method is invaluable for this.

First, let’s get a connection and some variables that we can use downstream in our query.

> conn = ActiveRecord::Base.connection
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLAdapter ...>
> one, ten = 1, 10
=> [1, 10]

Now, we are ready to safely generate our SQL query as a string. We have to use send because it is not publicly available. Generally, this is frowned upon, but in my opinion it is worth breaking the private interface to ensure our SQL is sanitized.

> sql = ActiveRecord::Base.send(:sanitize_sql_array, ["select generate_series(?, ?);", one, ten])
=> "select generate_series(1, 10);"

Lastly, we can execute the query with our connection and inspect the results.

> result = conn.execute(sql)
   (0.4ms)  select generate_series(1, 10);
=> #<PG::Result:0x007facd93128a0 status=PGRES_TUPLES_OK ntuples=10 nfields=1 cmd_tuples=10>
> result.to_a
=> [{"generate_series"=>1},
 {"generate_series"=>2},
 {"generate_series"=>3},
 {"generate_series"=>4},
 {"generate_series"=>5},
 {"generate_series"=>6},
 {"generate_series"=>7},
 {"generate_series"=>8},
 {"generate_series"=>9},
 {"generate_series"=>10}]

Polymorphic Path Helpers

Underlying many of the path helpers that we use day to day when building out the views in our Rails apps are a set of methods in the ActionDispatch::Routing::PolymorphicRoutes module.

The #polymorphic_path method given an instance of a model will produce the relevant show path.

> app.polymorphic_path(Article.first)
  Article Load (0.5ms)  SELECT  "articles".* FROM "articles"  ORDER BY "articles"."id" ASC LIMIT 1
=> "/articles/2"

Given just the model’s constant, it will produce the index path.

> app.polymorphic_path(Article)
=> "/articles"

Additionally, there are variants with edit_ and new_ prefixed for generating the edit and new paths respectively.

> app.edit_polymorphic_path(Article.first)
  Article Load (0.6ms)  SELECT  "articles".* FROM "articles"  ORDER BY "articles"."id" ASC LIMIT 1
=> "/articles/2/edit"
> app.new_polymorphic_path(Article)
=> "/articles/new"

Mark For Destruction

Do you have some complicated logic or criteria for deleting associated records? ActiveRecord’s #mark_for_destruction may come in handy.

Let’s say we have users who author articles. We want to delete some of the user’s articles based on some criteria — those articles that have odd ids.

> user = User.first
#=> #<User...>
> user.articles.each { |a| a.mark_for_destruction if a.id.odd? }
#=> [#<Article...>, ...]
> user.articles.find(1).marked_for_destruction?
#=> true
> user.articles.find(2).marked_for_destruction?
#=> false

We’ve marked our articles for destruction and confirmed as much with the #marked_for_destruction? method. Now, to go through with the destruction, we just have to save the parent record — the user.

> user.save
   (0.2ms)  BEGIN
  User Exists (0.8ms)  SELECT  1 AS one FROM "users" WHERE ("users"."email" = 'person1@example.com' AND "users"."id" != 1) LIMIT 1
  SQL (3.0ms)  DELETE FROM "articles" WHERE "articles"."id" = $1  [["id", 1]]
  SQL (0.2ms)  DELETE FROM "articles" WHERE "articles"."id" = $1  [["id", 3]]
   (2.1ms)  COMMIT
=> true

Note: the parent record must have autosave: true declared on the association.

class User < ActiveRecord::Base
  has_many :articles, autosave: true
end

Convert A Symbol To A Constant

If you have a symbol and need to convert it to a constant, perhaps because of some metaprogramming induced by a polymorphic solution, then you may start off on an approach like the following. In fact, I’ve seen a number of StackOverflow solutions like this.

:module.to_s.capitalize.constantize
#=> Module

That is great for one-word constant names, but what about multi-word constants like OpenStruct. This approach will not work for the symbol :open_struct. We need a more general solution.

The key is to ditch #capitalize and instead use another ActiveSupport method,#classify`. ruby :open_struct.to_s.classify.constantize #=> OpenStruct

List The Enqueued Jobs

Many Rails apps need to delegate work to jobs that can be performed at a later time. Both unit and integration testing can benefit from asserting about the jobs that get enqueued as part of certain methods and workflows. Rails provides a handy helper method for checking out the set of enqueued jobs at any given time.

The enqueued_jobs method will provide a store of all the currently enqueued jobs.

It provides a number of pieces of information about each job. One way to use the information is like so:

describe '#do_thing' do
  it 'enqueues a job to do a thing later' do
    Processor.do_thing(arg1, arg2)
    expect(enqueued_jobs.map { |job| job[:job] }).to match_array([
      LongProcessJob,
      SendEmailsJob
    ])
  end
end

To use this in your Rails project, just enable the adapter in your test configuration file:

Rails.application.config.active_job.queue_adapter = :test

Advance The Date

In Rails land, you can advance a date forward and backward with the #advance method:

> Date.today
=> Wed, 31 Aug 2016
> Date.today.advance(days: 1)
=> Thu, 01 Sep 2016
> Date.today.advance(months: 1)
=> Fri, 30 Sep 2016
> Date.today.advance(months: -2)
=> Thu, 30 Jun 2016

h/t Dillon Hafer

Read-Only Models

Are you in the midst of a big refactoring that is phasing out an ActiveRecord model? You may not be ready to wipe it from the project, but you don’t want it accidentally used to create any database records. You essentially want your model to be read-only until it is time to actually delete it.

This can be achieved by adding a readonly? method to that model that always returns true.

def readonly?
  true
end

ActiveRecord’s underlying persistence methods always check readonly? before creating or updating any records.

source

h/t Josh Davey

Where Am I In The Partial Iteration?

Let’s say I am going to render a collection of posts with a post partial.

<%= render collection: @posts, partial: "post" %>

The ActionView::PartialIteration module provides a couple handy methods when rendering collections. I’ll have access in the partial template to #{template_name}_iteration (e.g. post_iteration) which will, in turn, give me access to #index, #first?, and #last?.

This is great if I need to do something special with the first or last item in the collection or if I’d like to do some sort of numbering based on the index of each item.

source

h/t Josh Davey

Perform SQL Explain With ActiveRecord

Want to check out the performance characteristics of some SQL query from within a Pry session? ActiveRecord allows you to perform a SQL explain on any ActiveRecord::Relation object. After chaining some Arel functions together, add an #explain.

Here is an example:

Recipe.all.joins(:ingredient_amounts).explain
  Recipe Load (0.9ms)  SELECT "recipes".* FROM "recipes" INNER JOIN "ingredient_amounts" ON "ingredient_amounts"."recipe_id" = "recipes"."id"
=> EXPLAIN for: SELECT "recipes".* FROM "recipes" INNER JOIN "ingredient_amounts" ON "ingredient_amounts"."recipe_id" = "recipes"."id"
                                 QUERY PLAN
----------------------------------------------------------------------------
 Hash Join  (cost=1.09..26.43 rows=22 width=148)
   Hash Cond: (ingredient_amounts.recipe_id = recipes.id)
   ->  Seq Scan on ingredient_amounts  (cost=0.00..21.00 rows=1100 width=4)
   ->  Hash  (cost=1.04..1.04 rows=4 width=148)
         ->  Seq Scan on recipes  (cost=0.00..1.04 rows=4 width=148)
(5 rows)

source

Demodulize A Class Name

If you call .class.name on an instance of some class, the fully qualified name will be returned, module names and all. Consider the following example class:

module One
  module Two
    class Three
      ...
    end
  end
end
> One::Two::Three.new.class.name
#=> "One::Two::Three"

If you just want the unqualified class name; modules not included, you can use the #demodulize method provided by ActiveSupport.

> One::Two::Three.new.class.name.demodulize
#=> "Three"

Attach A File With Capybara

There are two ways to attach a file with Capybara. The more conventional way is with the attach_file method.

Assuming there is a form with a file input similar to the following:

<label for='data-file'>Data File</label>
<input type='file' name='data-file' />
attach_file('data-file', 'path/to/file.csv')

The first argument to attach_file is a locator which refers to the name attribute on the input tag.

If for some reason there is no name attribute on the input tag, the file can be attached with a standard find and set.

find('form input[type="file"]').set('path/to/file.csv')

Custom Validation Message

When using Rails validations, a standard error message will be provided whenever there is a violation. Consider the scenario when there is a uniqueness validation on the email attribute and it is violated:

# User model
validates_uniqueness_of :email

# Users controller
new_user.errors.full_messages
#=> ["Email has already been taken"]

Sometimes you don’t want the default validation message. The validation declaration can be given a message option to specify an alternate validation message.

# User model
validates_uniqueness_of :email, message: 'is not available'

# Users controller
new_user.errors.full_messages
#=> ["Email is not available"]

Keep in mind that full_messages will prepend the model name to the front of the message. You’ll want to ensure that the resulting message is coherent.

Rescue From

The rescue_from method, provided by ActiveSupport, is a handy way to provide a catch-all response to a particular exception in Rails controllers.

For instance, if many of the controllers in your application raise a User::NotAuthorized error for unauthorized requests, the ApplicationController can provide a unified response. This will help dry up your controllers and prevent any potential inconsistencies.

class ApplicationController < ActionController::Base
  rescue_from User::NotAuthorized do |exception|
    # respond with some Not Authorized page
  end

  ...
end

Truncate Almost All Tables

The database_cleaner gem is a handy way to make sure you have a consistent database context for each test example or suite. One database_cleaner strategy that can be used is the truncation strategy. This truncates the data from all the tables by default. This is not ideal for fixed tables that contain domain-specific data because you end up having to do way more test setup than should be necessary. Fortunately, specific tables can be excepted by the truncation strategy using the except option.

For instance, if we have a standard set of roles for users of our application, we can except that table from truncation with a line like the following in our rails_helper.rb file:

DatabaseCleaner.strategy = :truncation, {:except => %w[roles]}

Migrating Up Down Up

When writing Rails migrations, it is good to define, when possible, what should happen when migrating up and what should happen when migrating down. You’ll then want to check that both the up and down work. This can be accomplished using the following one-liner:

$ rake db:migrate && rake db:migrate:redo

The rake db:migration does what we would expect applying our new migration and showing us that our up works. The rake db:migrate:redo first performs a rollback, showing us that our down works, and then migrates back up again. We now know that our latest migration works going both directions.

Set Schema Search Path

By default the schema search path for a PostgreSQL database is going to be "$user", public. Tables created by a Rails migration are going to end up on the public schema by default. If your application has other schemas in play, then you may want to ensure that those schemas are also on the schema search path. This can be accomplished by adding the schema_search_path setting to your database.yml file. For instance, to include both the legacy and public schema in the Postgres search path, add the following line:

schema_search_path: "legacy,public"

h/t Jack Christensen

Show Rails Routes With Pry

In Show Rails Models With Pry, I showed that pry-rails comes with some handy console commands. In addition to being able to list all your Rails models, you can list all the routes for your application using show-routes.

I get the following output by using that command in a small blog project:

> show-routes
              Prefix Verb   URI Pattern                     Controller#Action
                root GET    /                               application#index
markdownify_articles POST   /articles/markdownify(.:format) articles#markdownify
            articles POST   /articles(.:format)             articles#create
         new_article GET    /articles/new(.:format)         articles#new
        edit_article GET    /articles/:id/edit(.:format)    articles#edit
             article GET    /articles/:id(.:format)         articles#show
                     PATCH  /articles/:id(.:format)         articles#update
                     PUT    /articles/:id(.:format)         articles#update
               users POST   /users(.:format)                users#create
            new_user GET    /users/new(.:format)            users#new
                user GET    /users/:id(.:format)            users#show
            sessions POST   /sessions(.:format)             sessions#create
         new_session GET    /sessions/new(.:format)         sessions#new
             session DELETE /sessions/:id(.:format)         sessions#destroy
              signin GET    /signin(.:format)               sessions#new
                     POST   /signin(.:format)               sessions#create
              signup GET    /signup(.:format)               users#new

Show Rails Models With Pry

With the pry-rails gem, you get some extra goodies in the Rails console for your project. One of those goodies is show-models, a command for printing out a list of all models in the rails project. Add and bundle the pry-rails gem, run rails c, and then run show-models to give it a go.

> show-models
Pokemon
  id: integer
  name: string
  level: integer
  pokemon_type: varchar
  belongs_to Trainer
  created_at: datetime
  updated_at: datetime
Trainer
  id: integer
  name: string
  has_many Pokemons

Select A Select By Selector

Generally when using Capybara to select from a select input, I reference it by its name which rails associates with the label:

select("Charizard", from: "Pokemon")

However, not all forms are going to have a label paired with every select input. We don’t want to let our test coverage suffer, so we are going to need a different way to select. Fortunately, Capybara allows us to chain select off a find like so:

find('#pokemon_list').select('Charizard')

Params Includes Submission Button Info

When a form is submitted for a Rails app, the respective controller action will have access to a variety of information in the params hash. Included is an entry with the name and value of the button that submitted the form. By default, Rails will give the name commit to a submission button.

<%= f.submit %>
# results in:
<input type="submit" name="commit" value="Submit">Submit</input>

The corresponding create action will have parameters that include that submission button’s info:

# in create action
> params['commit']
=> 'Submit'

This is useful when you have multiple buttons that submit the same form, but should have slightly different results in the corresponding action. Differentiating becomes easy when you can easily check which was used to submit the form. No javascript required.

Autosave False On ActiveRecord Associations

A relationship between two ActiveRecord models can be established with a has_one or has_many association. This relationship has some implications. By default, saving a record will also save the associated records that have since been built. Consider this example of users that have many posts (has_many posts).

> u = User.first
#=> #<User ...>
> u.posts
#=> []
> u.posts.build(title: "Some Title", content: "This is a post")
#=> #<Post ...>
> u.save
#=> true
> u.posts(reload: true)
#=> [#<Post ...>]

When the user is saved, the associated post that was built for that user also gets saved to the database.

If the association is instead defined with the autosave option set to false, then saving a record will not cause associated records to also be saved. The associated records will need to be saved explicitly. Consider the same example from above, but with has_many posts, autosave: false.

> u = User.first
#=> #<User ...>
> u.posts
#=> []
> u.posts.build(title: "Some Title", content: "This is a post")
#=> #<Post ...>
> u.save
#=> true
> u.posts(reload: true)
#=> []

The post wasn’t saved with the user and it wasn’t saved explicitly, so it isn’t persisted to the database.

Hash Slicing

Rails’ ActiveSupport adds #slice and #slice! to the Hash class. The interface of these two methods seems a little inconsistent though.

> {a: 1, b: 2, c: 3}.slice(:a)
=> {:a=>1}

The #slice method returns what is being sliced.

> {a: 1, b: 2, c: 3}.slice!(:a)
=> {:b=>2, :c=>3}

The #slice! method, on the other hand, returns what is being excluded.

Code Statistics For An Application

Rails applications and engines provide stats, a rake task for reporting high level code statistics. Running it on a small project of mine generated the following report:

$ rake stats
+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |   179 |   143 |       4 |      20 |   5 |     5 |
| Helpers              |    18 |    16 |       0 |       2 |   0 |     6 |
| Models               |    30 |    22 |       3 |       2 |   0 |     9 |
| Mailers              |     0 |     0 |       0 |       0 |   0 |     0 |
| Javascripts          |    53 |    35 |       0 |       6 |   0 |     3 |
| Libraries            |     0 |     0 |       0 |       0 |   0 |     0 |
| Controller specs     |    22 |    16 |       0 |       0 |   0 |     0 |
| Decorator specs      |    30 |    22 |       0 |       0 |   0 |     0 |
| Feature specs        |   739 |   382 |       0 |      74 |   0 |     3 |
| Model specs          |    70 |    55 |       0 |       0 |   0 |     0 |
| Cucumber features    |   412 |   293 |       0 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |  1553 |   984 |       7 |     104 |  14 |     7 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 216     Test LOC: 768     Code to Test Ratio: 1:3.6

Ignore Poltergeist JavaScript Errors

Poltergeist with PhantomJS (<2.0) does not support JavaScript’s bind() method. This means that when executing an integration test that exercises JavaScript with the bind() method, an error will occur. If you cannot simply upgrade to a version of PhantomJS that supports bind(), then what can you do?

Ignore the error!

This can be achieved by placing the following rescue block in the appropriate place.

rescue Capybara::Poltergeist::JavascriptError

Use this in moderation. You want to make sure you don’t ignore actual JavaScript errors.

source

Select Value For SQL Counts

If you are like me and prefer writing raw SQL over the Arel DSL for counting stuff in your database, then the select_value method will come in handy. Write a command similar to the following with a type cast to get the count of whatever.

> sql = 'select count(*) from posts where published_at is not null'
=> "select count(*) from posts where published_at is not null"
> ActiveRecord::Base.connection.select_value(sql).to_i
   (0.6ms)  select count(*) from posts where published_at is not null
=> 42

Writing raw SQL for a simple query like this hardly seems like a win. However when a count query starts to involve joins or other fanciness, I find it much clearer to reason about the raw SQL.

Retrieve An Object If It Exists

Rails’ Active Support provides the blank? and present? convenience methods as extensions to many objects. It also extends the Object class by providing the presence method. This method returns the receiver if it is not blank, otherwise it returns nil.

Instead of doing

User.nickname.present? ? User.nickname : User.firstname

I can simply do

User.nickname.presence || User.firstname

Pretend Generations

To get an idea of what a rails generate command is going to to generate, you can do a dry run with the -p flag or the --pretend flag. If you run

$ rails generate model post -p

then you will see the following output

    invoke  active_record
    create    db/migrate/20150513132556_create_posts.rb
    create    app/models/post.rb
    invoke    rspec
    create      spec/models/post_spec.rb
    invoke      factory_girl
    create        spec/factories/posts.rb

though those files will not have actually been created. You now know precisely what rails will generate for you.

source

All or Nothing Database Transactions

When you are updating multiple records in an all or nothing scenario, you can use Active Record Transactions to ensure that either everything is updated or none of it is updated.

For instance, if you are transferring internet points from one user’s account to another user’s account, you need to be sure that the transfer balances out. If updating one user is successful, but updating the other fails, then there will be a discrepancy in the data. A transaction will ensure that when any part of the update fails the entire transaction is rolled back (at the database level).

User.transaction do
  user1.internet_points += 20
  user2.internet_points -= 20
  user1.save!
  user2.save!
end

Attribute Getter without the Recursion

You may find yourself adding a custom getter method for one of the attributes in a Rails model. It might look something like this:

def name
  name || account.name
end

This method will fall on its face as it quickly expands the stack recursively calling itself. Instead, you can tell ActiveRecord that you want the value of that attribute without invoking the getter Instead, we want to get the value of the attribute without invoking the model’s getter. ActiveRecord allows us to do this with the read_attribute method. Check it out:

def name
  read_attribute(:name) || account.name
end

source

Capybara Page Status Code

To quickly determine if a page is rendering as expected or not, you can check the status code of the page. If your page is rendering successfully, you’ll see something like this:

> page.status_code
# => 200

If some sort of application authorization logic is causing the page to not render as normal, you may see something like this:

> page.status_code
# => 403

Creating Records of Has_One Associations

When working with a model, say a User, that has a has_many association with another model, say a Post, you can create a new post for a user like so:

u1 = User.first
=> #<User:0x...>
u1.posts.create(title: "Some Title", content: "...")
=> #<Post:0x...>

What about with a has_one association? Consider a Customer that has a has_one association with an Account. Rails provides this method for you:

c1.create_account(account_number: 123, ...)
=> #<Account:0x...>

Rails also gives you a similar build method:

c1.build_account(account_number: 123, ...)
=> #<Account:0x...>

Show Pending Migrations

Rails comes with a built-in rake task that allows you to check the status of migrations in the project.

$ rake db:migrate:status

database: pokemon_development

 Status   Migration ID    Migration Name
--------------------------------------------------
   up     20150219143706  Create pokemon table
  down    20150228003340  Create stats table

For large projects with lots of migrations, this is going to be a lot of output, so you can trim it down with a simple grep:

$ rake db:migrate:status | grep '^  down'
  down    20150228003340  Create stats table

source

Attribute Was

When modifying the attributes of an ActiveRecord object, you may want to know what values the modified attributes used to have. ActiveRecord gets some handy methods from the ActiveModel::Dirty module that allow you to check these values out even if the object’s attributes were changed before you received it (though you are out of luck once it has been saved). Just add _was onto the end of the attribute in question.

>> pokemon.name
=> "Charizard"
>> pokemon.name = "Squirtle"
=> "Squirtle"
>> pokemon.name
=> "Squirtle"
>> pokemon.name_was
=> "Charizard"
>> pokemon.save
   ...
=> true
>> pokemon.name_was == pokemon.name
=> true

source