Ruby Quicktips Logo

Ruby Quicktips

Random Ruby and Rails tips.
This blog is dedicated to deliver short, interesting and practical tidbits of the Ruby language and Ruby on Rails framework. Read more...

Your submissions are more than welcome!
Aug 18 ’12

Quick Hash to a URL Query trick

Got a hash of values you want to convert into a url query string? Use the to_query method:

"http://www.example.com?" + { language: "ruby", status: "awesome" }.to_query
# => "http://www.example.com?language=ruby&status=awesome" 

Want to do it in reverse? Use CGI.parse:

require 'cgi' # Only needed for IRB, Rails already has this loaded
CGI::parse "language=ruby&status=awesome"
# => {"language"=>["ruby"], "status"=>["awesome"]} 

Both methods support nested values.

This tip was submitted by Victor Solis.

9 notes 0 comments

Jul 22 ’12

Testing CSV file uploads

Let’s say you have a form like this for uploading a CSV file:

<%= form_tag csv_import_path, :multipart => true do %>
  <%= file_field_tag :file, :accept => "text/csv" %>
  <%= submit_tag "Upload" %>
<% end %>

And your controller action looks like this:

require 'csv'

def csv_import    
  file_data = params[:file].read
  csv_rows  = CSV.parse(file_data)

  csv_rows.each do |row|
    # do something with each row
  end
    
  respond_to do |format|
    format.html { redirect_to your_path, :notice => "Successfully imported the CSV file." }
  end
end

How would you test this functionality?

You can’t use fixture_file_upload and stick a sample file in test/fixtures/files/ (as often suggested - e.g. here on SO). That’s because CSV and YAML files will be imported as fixtures into the test database as soon as you run tests. E.g., this would not work:

def test_file_upload
  post :csv_import, :file => fixture_file_upload('files/new_users.csv','text/csv')
end

But what you can do is to use the Tempfile and Rack::Test::UploadFile classes and manually create a CSV file and supply this to the post (or put) method:

def test_should_successfully_import_csv
  csv_rows = <<-eos
Name1,name1@example.com
Name2,name2@example.com
Name3,name3@example.com
eos

  file = Tempfile.new('new_users.csv')
  file.write(csv_rows)
  file.rewind

  assert_difference "User.count", 3 do
    post :csv_import, :file => Rack::Test::UploadedFile.new(file, 'text/csv')
  end

  assert_redirected_to your_path
  assert_equal "Successfully imported the CSV file.", flash[:notice]
end

5 notes 0 comments

May 3 ’12

Using ActiveRecord to query for times within a range

You can pass a range to query for records within that range:

hackerinspiration:

Just discovered this, something I wish I knew a LONG time ago.

Album.where(:created_at => 2.days.ago..Time.now)

Which will generate the following SQL query (depending on the database):

SELECT "albums".* FROM "albums" WHERE ("albums"."created_at" BETWEEN '2012-04-28 11:10:22.780712' AND '2012-04-30 11:10:22.780907')

10 notes 0 comments (via hackerinspiration)

May 1 ’12

Try out your helpers in the Rails console

Ever wanted to try out any of Rails’ view helper methods in the console? Just include ActionView::Helpers after starting the Rails console:

include ActionView::Helpers
# => Object
text_field(:post, :title)
# => "<input id=\"post_title\" name=\"post[title]\" size=\"30\" type=\"text\" />"

1 note 0 comments

Apr 12 ’12

Rails’ index_by: the easy way to convert an Array to a Hash

As Aditya Sanghi notes in “Quickly convert an Array to a Hash”, a very quick way to convert an Array to a Hash, is to use Rails’ Enumerable#index_by:

Post.all.index_by { |post| post.id }
# => { 1 => #<Post ...>, 2 => #<Post ...>, ... }

Post.all.index_by(&:title)
# => { "My first post" => #<Post ...>, "My second post" => #<Post ...>, ... }

6 notes 0 comments

Mar 29 ’12

Enumerable#each_with_object

Enumerable#each_with_object is quite similar to Enumerable#inject, yet slightly different. From the Ruby 1.9 and Rails 3 API docs:

Iterates the given block for each element with an arbitrary object given, and returns the initially given object.

Iterates over a collection, passing the current element and the memo to the block. Handy for building up hashes or reducing collections down to one object.

evens = (1..10).each_with_object([]) {|i, a| a << i*2 }
#=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

%w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
# => {'foo' => 'FOO', 'bar' => 'BAR'}

As BiHi noted on this tip on Enumerable#inject, its example can be slightly simplified using each_with_object instead of inject:

my_hash = { a: 'foo', b: 'bar' }
# => {:a=>"foo", :b=>"bar"}

my_hash.each_with_object({}) { |(k,v), h| h[k.upcase] = v.upcase }
# => {:A=>"FOO", :B=>"BAR"}

0 comments

Mar 13 ’12

Pretend to generate

If you’re not sure what files a Rails Generator would create for you, just add the -p option (or --pretend) for a dry run:

rails generate model Blog -p

4 notes 0 comments

Mar 1 ’12

Quick shortcut to load a SQL file in Rails

You can easily load SQL files like this:

rails db < path_to_sql_file.sql # Rails 3 
script/dbconsole < path_to_sql_file.sql # Rails 2

6 notes 0 comments

Feb 23 ’12

Rails’ include_root_in_json

When rendering JSON from your controllers (or when using to_json directly), Rails 3.1 and above won’t include the root element in the output:

post = Post.first
# => #<Post id: 1, title: "My first blogpost" ...

post.to_json
# => "{\"id\":1,\"title\":\"My first blogpost\", ...}"

To include the root element (post in this example), set ActiveRecord::Base.include_root_in_json to true:

# wrap_paramters.rb
if defined?(ActiveRecord)
  ActiveRecord::Base.include_root_in_json = true
end

Result:

post.to_json
# => "{\"post\":{\"id\":1,\"title\":\"My first blogpost\", ...}}"

In version of Rails < 3.1, including the root element is the default. You can disable it by adding ActiveRecord::Base.include_root_in_json = false to one of your files in config/initializers or in application.rb/environment.rb directly.

More info in the API docs on ActiveModel::Serializers::JSON.

0 comments

Feb 14 ’12

Clear your development logs automatically when they are too large

This snippet simply clears your logs when they are too large. Every time you run rails server or rails console it checks log sizes and clears the logs for you if necessary.

# config/initializers/clear_logs.rb

if Rails.env.development?
  MAX_LOG_SIZE = 2.megabytes
  
  logs = File.join(Rails.root, 'log', '*.log')
  if Dir[logs].any? {|log| File.size?(log).to_i > MAX_LOG_SIZE }
    $stdout.puts "Runing rake log:clear"
    `rake log:clear`
  end 
end

This tip was submitted by pahanix.

2 notes 0 comments

Feb 9 ’12

Quickly convert an Array to a Hash

As an example, let’s say you want to create an index of ActiveRecord objects by their id. Use the Hash constructor that accepts an Array of key-value pairs and do it in one line:

posts_by_id = Hash[*Post.all.map{ |p| [p.id, p] }.flatten]

This tip was submitted by http://Fullware.net/.

6 notes 0 comments

Jan 31 ’12

Log in to your database console using Rails

Instead of using the database-specific command to start your project’s database console, Rails provides one consistent interface for the most popular databases (MySQL, PostgreSQL and SQLite):

script/dbconsole [RAILS_ENV] # Rails 2
rails dbconsole [RAILS_ENV] # Rails 3
rails db [RAILS_ENV] # Rails 3 alias

3 notes 0 comments

Jan 10 ’12

Rails’ Hash#reverse_merge

Ruby’s Hash#merge combines two Hashes, where the second Hash replaces values with the same key of the calling Hash.

Rails adds the method Hash#reverse_merge which keeps the contents of the caller. This gives you - for example - an elegant way to specify default values for an optional argument Hash:

def my_method(options = {})
  options = options.reverse_merge(:option1 => "foo", :option2 => "bar")
  # If you pass in options with the :option1 and :option2 keys,
  # their values will NOT be overwritten.
  # If you leave option1 and option2 out, the defaults passed
  # to reverse_merge will be inserted.
  # actual method implementation here
end

3 notes 0 comments

Jan 5 ’12

ri: Ruby Interactive documentation

ri stands for “Ruby Interactive” and provides documentation on Ruby itself and most gems. You access it from the command line.

Here are some samples:

ri Array
ri select
ri Hash#select
ri Array#select
ri ActiveRecord::Base
ri -l #lists all classes with ri docs

(If you want to access the RDoc documentation, there’s a tip for that, too: Easy Access to the API Docs for Gems.)

13 notes 0 comments

Jan 3 ’12

Prevent rdoc and ri installation when installing a gem

When installing a gem the process that often takes the longest is generating the ri and RDoc documentation.

If you want to prevent this from happening every time a gem gets installed (either manually or through Bundler), create (or open) a .gemrc file in ~/.gemrc or /etc/gemrc and add the following two lines:

install: --no-ri --no-rdoc
update: --no-ri --no-rdoc

(If you want to overwrite this default per gem, or if you want to install the documentation later, you can do so by passing --ri and/or --rdoc options to the gem install command.)

The .gemrc file is the configuration file used by RubyGems, in which you can specify command-line arguments to be used every time the gem command runs: more about the RubyGems configuration file.

This tip was submitted by Andrea Singh.

8 notes 0 comments