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.
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
You can pass a range to query for records within that range:
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')
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\" />"
Creating a regular expression using the r{}
literals can for example be helpful when matching URLs, because you don’t have to escape slashes:
url = "http://example.com/" # /../ literals: url.match /http:\/\/example\.com\// # => #<MatchData "http://example.com/"> # %r{} literals: url.match %r{http://example\.com/} # => #<MatchData "http://example.com/">
The Hash::[] class method can be used in addition to the splat operator to create a hash from an array.
arr = [:a, 1, :b, 2] Hash[*arr] # => {:a => 1, :b => 2}
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 ...>, ... }
As Brich Thompson notes in the comments on “Convert between number bases easily”, you can also convert a number string in any base to a decimal number with the String#to_i method:
"21".to_i #=> 21 "21".to_i(8) #=> 17 "21".to_i(16) #=> 33
[1,2,3].each_with_object(1).map(&:+) # => [2, 3, 4] # Same outcome, even shorter [1, 2, 3].map(&1.method(:+)) # => [2, 3, 4]
In his blog post In Ruby, &method passes you!, Andrew Grimm explains how this all works.
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"}
To mark deprecated code in Ruby simply add a comment to the rdoc and call the Kernel#warn
method. For example:
class Foo
# DEPRECATED: Please use useful instead.
def useless
warn "[DEPRECATION] `useless` is deprecated. Please use `useful` instead."
useful
end
def useful
# ...
end
end
If you’re using Yard instead of rdoc, your doc comment should look like this:
# @deprecated Please use {#useful} instead
Also, don’t forget to remove the deprecated method in some future release.
We can define a new Hash in a smart way by using Hash#new and passing it a block:
h = Hash.new {|hash,key| hash[key] = hash[key-1] + hash[key-2]} h[1] = 0 h[2] = 1 puts h[3] #=> 1 puts h[100] #=> 218922995834555169026
Here an example of famous Collatz Conjecture:
h = Hash.new {|hash,n| hash[n] = 1 + (n.odd? ? hash[3*n+1] : hash[n/2])} h[1] = 1 puts h[100] #=> 26 puts h[1000] #=> 112
via: thoughbot’s blog
Wrap a block of code within =begin
and =end
to comment it out.
# the comment format you're used to
puts "I am evaluated"
=begin
puts "I"
puts "am"
puts "commented"
puts "out"
=end
This tip was submitted by Tim Linquist.
Aim: perform a method chaining based on hash
Required operation:
ErrorLog.event_eq([3, 7]).subdomain_like("default").user_id_eq(100)
Given:
search_opts = { :event_eq => [3, 7], :subdomain_like => "default", :user_id_eq => 100 }
Solution:
search_opts.inject(ErrorLog) { |memo, (k, v)| memo.send(k, v) }
This tip was submitted by sumskyi.
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