Veerasundaravel's Ruby on Rails Weblog

November 26, 2014

Rails find_each method with order option

Filed under: Rails3, Ruby, Ruby On Rails — Tags: , , , , , , , — Veerasundaravel @ 9:36 pm
Rails find_each or find_in_batches methods are looping through a collection of records from the database.
This batch processing methods allow us to work with the records in batches, thereby greatly reducing memory consumption.
Person.find_each(:conditions => "age > 21") do |person|
  person.party_all_night!
end
But major drawback with these methods is  - ordering/sorting the records by primary key id, hence we cannot specify our own order_by.
order_by(:created_at).find_each == FAIL!!!
class ActiveRecord::Base
  # normal find_each does not use given order but uses id asc
  def self.find_each_with_order(options={})
    raise "offset is not yet supported" if options[:offset]

    page = 1
    limit = options[:limit] || 1000

    loop do
      offset = (page-1) * limit
      batch = find(:all, options.merge(:limit=>limit, :offset=>offset))
      page += 1

      batch.each{|x| yield x }

      break if batch.size < limit
    end
  end
end
Advertisements

October 10, 2012

Rails Caching With Query Parameters

Filed under: Caching, Rails3, Ruby On Rails — Tags: , , , , , , , , — Veerasundaravel @ 2:17 pm

Rails has various effective ways of caching contents like page caching, action caching, fragment caching etc. But some time we may need to use low level caching of Rails.cache in order to store some HTML fragments or some hash values etc that can be used some where else later on.

Rails.cache.write takes two value: key and a value
> Rails.cache.write 'foo', 'bar'
=> true

# We can read an object back with read
> Rails.cache.read 'foo'
=> "bar"

# We can store a complicated object as well
> hash = {:this => {:is => 'a hash'}}
> Rails.cache.write 'complicated-object', object
> Rails.cache.read 'complicated-object'
=> {:this=>{:is=>"a hash"}}

# We can use fetch as the combination of write and read.
Rails.cache.fetch 'complicated-object', object

 

Params as Cache Key:

Most of the Rails actions comes with multiple parameters, so when we cache any result inside the controller’s action we may need to assign the parameter values as cache key. So that we will get unique key combination for different action parameters.

Rails.cache.fetch(params.sort.flatten.join("_"), object)

Assume that the following params:

params = {:controller => 'books', :action => 'index', :status => 'stock-avail', :start_price => '$100'
, :end_price =>"$200", :delivery => 'by-post', :discount_start => "10%", :discount_ends => "20%",
 :language => 'english', :cover => 'available', :sort => "posted_date", :preface =>
 "available-on-request", :ebook => 'available-on-request'}

Then our cache key will become very unique for the supplied params as below:

"action_index_controller_books_cover_available_delivery_by-post_discount_ends_20%_discount_start_10%
_ebook_available-on-request_end_price_$200_language_english_preface_available-on-request_sort_posted_
date_start_price_$100_status_stock-avail"

But cache key should be less than 255 characters long to keep the file system happy. If cache key is greater than 255 chars, then it will throw an error like Errno::ENAMETOOLONG (File name too long) etc.

 

How to avoid too long cache key:

One of the best option to shorten the cache key would be, simply have the generated cache key run through a hash like MD5 or SHA1. This way we can get predictable cache key lengths.

Rails.cache.fetch(Digest::SHA1.hexdigest(params.sort.flatten.join("_")), object)

It will modify the cache key as follows:

Rails.cache.fetch('5c059024747e813d0fa5ec8fab890be473eab63a', object)

July 25, 2012

Rspec Shoulda-Matcher made Unit testing as easier one

Filed under: Rails3, Ruby, Ruby On Rails, Testing, Unit Test — Tags: , , , , , , — Veerasundaravel @ 12:37 am

Shoulda matcher is a library that enables to write better and more understandable tests for Rails application. It is Test::Unit- and RSpec-compatible one-liners that test common Rails functionality.

Here few very easier example of shoulda-matchers for Unit Testing.

Test model fields:

it {should have_db_column(:login)}
it {should have_db_column(:salary).of_type(:decimal).with_options(:precision =&gt; 10, :scale =&gt; 2) }
it { should_not have_db_column(:admin).of_type(:boolean) }

Test db indexes:

it { should have_db_index(:age) }
it { should have_db_index([:commentable_type, :commentable_id]) }
it { should have_db_index(:ssn).unique(true) }

Test validations:

it { should validate_uniqueness_of(:title) }
it { should validate_presence_of(:body).with_message(/Enter the message/) }
it { should validate_presence_of(:title) }
it { should validate_numericality_of(:user_id) }
it { should validate_uniqueness_of(:title) }
it { should_not allow_value("blah").for(:email) }
it { should allow_value("a@b.com").for(:email) }
it { should ensure_inclusion_of(:age).in_range(1..100) }
it { should_not allow_mass_assignment_of(:password) }

Test associations:

it { should belong_to(:parent) }
it { should have_one(:car)
it { should have_many(:friends) }
it { should have_many(:enemies).through(:friends) }

 

Further reading:

Shoulda-Matcher home page –  https://github.com/thoughtbot/shoulda-matchers/
Shoulda-context home page – https://github.com/thoughtbot/shoulda-context/
rspec_shoulda cheat sheet – http://cheat.errtheblog.com/s/rspec_shoulda/

 

Older Posts »

%d bloggers like this: