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\" />"
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 ...>, ... }
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"}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 -pYou 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
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.
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.
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/.
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
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
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.)
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.