active-record | Dmytro Shteflyuk's Home https://kpumuk.info In my blog I'll try to describe about interesting technologies, my discovery in IT and some useful things about programming. Tue, 08 Sep 2015 00:36:05 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 Useful helpers for RSpec mocks https://kpumuk.info/ruby-on-rails/useful-helpers-for-rspec-mocks/ https://kpumuk.info/ruby-on-rails/useful-helpers-for-rspec-mocks/#comments Sat, 05 May 2007 00:38:39 +0000 http://kpumuk.info/rspec/useful-helpers-for-rspec-mocks/ RSpec has great feature — mock objects (mocks). In a few words: mock object imitates behavior of the object, which used by the tested methods. And simple example immediately: 12345678910describe UserHelper   it 'should generate correct link to user profile in user_link' do     @user = mock('User')     @user.stub!(:id, 10)     @user.stub!(:new_record?, […]

The post Useful helpers for RSpec mocks first appeared on Dmytro Shteflyuk's Home.]]>
RSpec has great feature — mock objects (mocks). In a few words: mock object imitates behavior of the object, which used by the tested methods. And simple example immediately:

1
2
3
4
5
6
7
8
9
10
describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock('User')
    @user.stub!(:id, 10)
    @user.stub!(:new_record?, false)
    @user.stub!(:preferred_name, 'Dmytro S.')
    @user.stub!(:full_name, 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => 10), :title => 'Dmytro Shteflyuk')
  end
end

Well, and what does it mean? Initially we are creating mock object @user, which would be used instead of real model. Please don’t ask me why do we need such complexity and why we can’t just use real model. I will explain it myself. On the one hand, mocks are much faster than the database operations — when you have many tests (and you have, right?) total tests execution time would be much smaller if we would use mock objects. On the other hand, you don’t want to test the same methods again and again, right? In my example preferred_name method has non-trivia logic (it’s not simple database field), and it has already been tested in model spec. Imagine that you are using model in helper tests. If this method would break down, two specifications will be failed instead of one — model specification. In addition, there is one interesting feature exists — rcov, which shows how your code is covered by tests. Thus if model tests would not exist, and helper tests would use model,– rcov will show preferred_name method as covered, but it is not true. Oops, I have been distracted.

Oh yes, if you don’t know, stub is just method, which does nothing, it just return value, which you have passed to it.

So we have a test. Could we simplify it somehow? Yep!

1
2
3
4
5
6
7
describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock('User')
    add_stubs(@user, :id => 10, :new_record? => false, :preferred_name => 'Dmytro S.', :full_name => 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => 10), :title => 'Dmytro Shteflyuk')
  end
end

Much better. Helper method add_stubs adds to the object, passed as a first parameter, stubs, passed as a second parameter in hash. But it’s not all. Especially for the Active Record models RSpec has method mock_model, which automatically creates several stubs, common for all Ruby on Rails models, and accepts hash with stubs just like add_stubs does:

1
2
3
4
5
6
describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock_model(User, :preferred_name => 'Dmytro S.', :full_name => 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => @user.id), :title => 'Dmytro Shteflyuk')
  end
end

You definitely noticed that I have missed parameters :id and :new_record?, and it was not coincidence. Firstly, mock_model automatically defines unique ids for models, which were created using it. Secondly, it defines methods to_param (returns string representation of the id) and new_record? (returns false). The first parameter of the method is a model class, and the second one is a hash of stubs, as I have already said. That’s all, folks.

The post Useful helpers for RSpec mocks first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/useful-helpers-for-rspec-mocks/feed/ 5
Using sub-queries to avoid multiple DB requests in Rails https://kpumuk.info/ruby-on-rails/using-sub-queries-to-avoid-multiple-db-requests-in-rails/ https://kpumuk.info/ruby-on-rails/using-sub-queries-to-avoid-multiple-db-requests-in-rails/#comments Thu, 12 Apr 2007 06:12:22 +0000 http://kpumuk.info/ruby-on-rails/using-sub-queries-to-avoid-multiple-db-requests-in-rails/ When I worked on Best Tech Videos with Alexey Kovyrin, we faced a problem of filtering videos by category with selecting posts categories in the same query. It was easy to solve the problem, but there is one query optimization trick exists. For example, we are developing blogging software. We have Post and Category model, […]

The post Using sub-queries to avoid multiple DB requests in Rails first appeared on Dmytro Shteflyuk's Home.]]>
When I worked on Best Tech Videos with Alexey Kovyrin, we faced a problem of filtering videos by category with selecting posts categories in the same query. It was easy to solve the problem, but there is one query optimization trick exists.

For example, we are developing blogging software. We have Post and Category model, where post could have one or more categories (many-to-many relation):

1
2
3
4
5
6
7
class Post < ActiveRecord::Base
  has_and_belongs_to_many :categories
end

class Category < ActiveRecord::Base
  has_and_belongs_to_many :posts
end

We need to display posts on the page along with categories for each one. The simplest way is to do find with :include:

1
Post.find :all, :include => :categories

And corresponding helper:

1
2
3
def show_categories(post)
  post.categories.map(&:name).join(' ')
end

But what if you need to filter off posts by category? Here is example:

1
2
3
Post.find :all,
          :include => :categories,
          :conditions => ['categories.id = ?', category_id]

It works, but there is one small trouble: you would get not all categories! Now we could fix it in helper:

1
2
3
4
def show_categories(post)
  # reload categories
  post.categories(true).map(&:name).join(' ')
end

In this case categories for all posts would be requested in separate queries. It’s not so good, therefor I propose to use sub-query:

1
2
3
Post.find :all,
          :include => :categories,
          :conditions => ['EXISTS (SELECT tmp_cp.category_id FROM categories_posts tmp_cp WHERE posts.id = tmp_cp.post_id AND tmp_cp.category_id = ?)', category_id]

Now posts would be filtered by category and all categories of posts would be loaded properly. Do you have other ideas?

The post Using sub-queries to avoid multiple DB requests in Rails first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/using-sub-queries-to-avoid-multiple-db-requests-in-rails/feed/ 8
validates_uniqueness_of and MySQL unique index https://kpumuk.info/ruby-on-rails/validates_uniqueness_of-vs-mysql-unique-index/ Wed, 11 Oct 2006 07:52:01 +0000 http://kpumuk.info/ruby-on-rails/validates_uniqueness_of-vs-mysql-unique-index/ Yesterday in mailing list of russian RoR group we have discussed following problem. MySQL database has unique index and model has validates_uniqueness_of constraint. Do we need to handle MySQL server exceptions or RoR’s validation will be enough? I think we need, but to be sure I decided to make test. First, I created table with […]

The post validates_uniqueness_of and MySQL unique index first appeared on Dmytro Shteflyuk's Home.]]>
Yesterday in mailing list of russian RoR group we have discussed following problem. MySQL database has unique index and model has validates_uniqueness_of constraint. Do we need to handle MySQL server exceptions or RoR’s validation will be enough?

I think we need, but to be sure I decided to make test. First, I created table with unique index:

1
2
3
4
5
6
7
8
9
10
11
12
class CreateCustomers < ActiveRecord::Migration
  def self.up
    create_table :customers do |t|
      t.column :name, :string
    end
    add_index :customers, :name, :unique => true
  end

  def self.down
    drop_table :customers
  end
end

Then I wrote following model:

1
2
3
4
5
6
7
8
9
10
11
12
class Customer < ActiveRecord::Base
  validates_presence_of :name, :if => lambda { |customer|
    unless @@created
      @@created = true
      Customer.create(:name => customer.name)
    end
    true
  }
  validates_uniqueness_of :name
 
  @@created = false
end

You can see two validation rules: one is uniqueness, which is the our primary goal. Second rule’s task is to create second customer between validation and insertion of first. Validations are processed in postorder, therefor validates_presence_of is going first. Now I’ve started script/console and typed:

1
Customer.create(:name => 'name')

You can find log file below. I think, you don’t need any comments.

1
2
3
4
5
6
7
SHOW FIELDS FROM customers
BEGIN
SELECT * FROM customers WHERE (customers.name = 'name') LIMIT 1
SELECT * FROM customers WHERE (customers.name = 'name') LIMIT 1
INSERT INTO customers (`name`) VALUES('name')
Mysql::Error: #23000Duplicate entry 'name' for key 2: INSERT INTO customers (`name`) VALUES('name')
ROLLBACK

Note: You right, this is broken logic, but I used it only to show what will be if two processes will insert record with the same name. You can replace code inside :if with sleep 10 and run two consoles to reproduce exception.

The post validates_uniqueness_of and MySQL unique index first appeared on Dmytro Shteflyuk's Home.]]>