Hashrocket.com / blog

Large best of til

Best of TIL Year One: Rails

posted on and written by in

Image 100x100 jake worth

Here are some of the top Rails posts from Today I Learned.

My goal with this series is to highlight some of the top posts from the first year of Today I Learned. Today we'll look at Rails, our fifth-most active channel.

Hashrocket was one of the first Ruby on Rails consultancies, and Rails is still very important to our business. Many of our projects are Rails applications, and we strive to maximize this ever-evolving framework to meet client needs. Today I Learned is itself written in Rails (source); some of these posts came directly from working on it.

Here are the top five most liked Rails posts, in order, from the first year of Today I Learned.

Enjoy!

Rails Sandbox 🏖 (Dillon Hafer)

When you are working with a complicated database structure, and find yourself needing to debug a complex or dangerous (delete) action, you might be hesitant to experiment. Keep experimenting! Don't want to setup all that data again? No worries. You can use a sandbox:

$ rails c -s
Usage: rails console [environment] [options]
  -s, --sandbox      Rollback database modifications on exit.

The sandbox flag will keep all database changes in a database transaction when you start the rails console and automatically issue a rollback when you quit the console.

Truncate Almost All Tables (Josh Branchaud)

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]}

ActiveRecord subselects (Micah Woods)

So you want to find all the rocketeers who wrote blog posts in a date range.

Blog::Post.where(published_at: 15.years.ago..4.years.ago).includes(:rocketeer).map(&:rocketeer)
  # Blog::Post Load (0.6ms)  SELECT "blog_posts".* FROM "blog_posts"
  #   WHERE ("blog_posts"."published_at" BETWEEN '2000-06-12 14:40:06.429288' AND '2011-06-12 14:40:06.429498')
  # Rocketeer Load (0.7ms)  SELECT "rocketeers".* FROM "rocketeers"
  #   WHERE "rocketeers"."id" IN (12, 13, 14, 16)  ORDER BY "rocketeers"."name"

But you want to do it in one query!

Rocketeer.where(
  id: Blog::Post.where(published_at: 15.years.ago..4.years.ago).select(:rocketeer_id)
)
  # Rocketeer Load (0.9ms)  SELECT "rocketeers".* FROM "rocketeers"
  #   WHERE "rocketeers"."id" IN (
  #     SELECT "blog_posts"."rocketeer_id" FROM "blog_posts"
  #       WHERE ("blog_posts"."published_at" BETWEEN '2000-06-12 14:42:20.005077' AND '2011-06-12 14:42:20.005317'))  ORDER BY "rocketeers"."name"

Interact with Rails via Runner (Jake Worth)

The rails runner feature of the Ruby on Rails command line interface is pretty awesome.

The documentation states:

runner runs Ruby code in the context of Rails non-interactively.

Use it like the ruby command to execute Ruby code in the context of your Rails environment. Take this file:

# rails_runner_in_action.rb
puts Developer.count # 5
puts Post.count # 40
puts Channel.count # 17

And run it with:

$ rails runner rails_runner_in_action.rb
5
40
17

It also runs Ruby code right in the terminal, so this works (rails r is an alias):

$ rails r "puts Developer.count"
5

http://guides.rubyonrails.org/command_line.html#rails-runner

Return an Empty Active Record Collection (Micah Cooper)

You can use .none in a scope to short circuit the query in the event you don't have all the data.

Imagine this query but the project_type on a Project is nil

class User

  scope :active -> { where(archived: nil }

  scope :by_project, -> (project) do
    return none unless project.type.present?
    where(project_guid: project.guid, role: project.type)
  end

end

Just return none.

The cool thing about this is it's chainable. So you can still do something like:

project = Project.new(project_type: nil)

User.by_project(project).active

Conclusion

Thanks to Dillon, Josh, Micah Woods, and Micah Cooper for these posts.

Today I Learned had a spike in traffic near the beginning of the year, and these posts are mostly from that time. But there's a lot of great Rails tips from earlier. See them all here:

https://til.hashrocket.com/rails

Thanks for reading this series of posts, and keep learning every day.


This blog post is part five of a series; here's part one, two, three, and four.

Posted in Development