Hashrocket Projects Ruby
Best of TIL Year One: Rails
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.