3

I have a Cover model, in the cover.rb file, I also define a method called size which returns an integer representing 'small, intermediate, large'. My question is how can I retrieve all the small/intermediate/large covers? My guess is to use scope, but I cannot figure out how to pass the size method as a condition.

class Cover < ActiveRecord::Base
  attr_accessible :length, :width

  # TODO
  scope :small
  scope :intermediate
  scope :large

  # I have simplified the method for clarity.
  # 0 - small; 1 - intermediate;  2 - large
  def size
    std_length = std_width = 15
    if length < std_length && width < std_width
      0
    elsif length > std_length && width > std_width
      2
    else
      1
    end
  end

end
Chelsea White
  • 285
  • 5
  • 19

3 Answers3

4

This could work:

class Cover < ActiveRecord::Base
  attr_accessible :length, :width

  scope :of_size, lambda{ |size| 
                          case size
                            when :small
                              where('width < 15 AND height < 15')
                            when :large
                              where('width > 15 AND height > 15')
                            when :intermediate
                              where('(width < 15 AND height > 15) OR (width > 15 AND height < 15)')
                            else
                              where(id: -1) # workaround to return empty AR::Relation
                        }

  def size
    std_length = std_width = 15
    return :small if length < std_length && width < std_width
    return :large if length > std_length && width > std_width
    return :intermediate
  end

end

And use it like this:

Cover.of_size(:small) # => returns an array of Cover with size == small

To make it work with several arguments:

# in the model:
scope :of_size, lambda{ |*size| all.select{ |cover| [size].flatten.include?(cover.size) } }
# how to call it:
Cover.of_size(:small, :large) # returns an array of covers with Large OR Small size
MrYoshiji
  • 54,334
  • 13
  • 124
  • 117
  • You're welcome :) You could extend it with several arguments, and you could use it like `Cover.of_size(:small, :large)` – MrYoshiji Dec 14 '12 at 19:21
  • BTW, is there any solution to return ActiveRecord::Relation objects? I found that the paginate method fails with array... – Chelsea White Dec 14 '12 at 19:30
  • add `where(nil)` at the end, probably like this: `Cover.of_size(:small).where(nil)` ### Doesn't work actually – MrYoshiji Dec 14 '12 at 19:33
  • It says `NoMethodError: undefined method `where' for #` – Chelsea White Dec 14 '12 at 19:37
  • I updated my answer. Now it is not using the `select` method (which was using a lot of resources: get all the Cover, translate them into Ruby objects, test each one). The scope is better now, but you should use class variables for the max_width & max_height ;) – MrYoshiji Dec 14 '12 at 19:46
  • I didn't read the update carefully. I've simplified my size method. The real method involves many other calculations, and that's why I haven't figured out how to use 'where' clause appropriately. – Chelsea White Dec 14 '12 at 20:01
0

I think you the simplest solution would be a class method:

def self.small
  Cover.all.select {|c| c.size == 0}
end
Andrea
  • 116
  • 1
  • 9
0

If you're considering passing one of three sizes, you might as well call one of three scopes. It'll be more readable.

scope :small, where('width < 15 AND height < 15')
scope :large, where('width > 15 AND height > 15')
scope :intermediate, where('(width < 15 AND height > 15) OR (width > 15 AND height < 15)')
Mark Swardstrom
  • 17,217
  • 6
  • 62
  • 70