I'm upgrading a Rails 3 code base to Rails 4 and have been updating to use the new scope style and querying interface.
I'm unsure how to switch over this scope that is using include as well as conditions in the scope's lambda.
scope :search_stuff, lambda { |search|
search_conditions = Array.new(4, "%#{search}%")
{
:include => {:sponsor => [], :routing_form_pi_users => [:user], :item => []},
:conditions => ["id like ? or title like ? or users.name like ? or sponsors.name like ?", *search_conditions]
}
}
When you compare the documentation about the query interface in different versions of the Rails Guide then you can see that the interface for eager loading of multiple associations didn't change that much.
The example in Rails 2.3.8 syntax:
Category.find 1, :include => {:posts => [{:comments => :guest}, :tags]}
The example for Rails 4.2 syntax:
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
Therefore it should be possible to just copy the old nested hash into the new syntax:
scope :search_stuff, lambda { |search|
includes(item: [], routing_form_pi_users: [:user], sponsor: []).
where('id like ? or title like ? or users.name like ? or sponsors.name like ?', *Array.new(4, "%#{search}%"))
}
Related
I have been upgrading named scopes to scopes following this guide http://m.onkey.org/active-record-query-interface. The one thing I have run into in the code I am working with that I have not seen in any examples is how do deal with
named_scope :all, <ect>
The closest example I have seen is in the m.onkey.org guide which is
named_scope :red, :conditions => { :colour => 'red' }
to
scope :red, :conditions => { :colour => 'red' }
So would the same idea apply, making my new code
scope :all, <ect>
or am I missing something?
The scope .all is reserved since ActiveRecord allows you to retrieve all the entries with the same method:
User.all #=> returns ALL the users existing in the DB
You should rename your scope to a different name.
Also, you don't have to precise the conditions keyword. Here is part of codes I'm using in my projects:
scope :ordered, order(:name)
scope :with_color, lambda{ |color| where( color: color.try(:to_s) ) }
# this lambda scope is usable by:
# Model.with_color(:red)
I have the following named scopes in my rails app:
scope :published, :conditions => {:status => 'published'}
scope :coming_soon, :conditions => {:status => 'coming_soon'}
scope :in_development, :conditions => {:status => 'in_development'}
scope :cancelled, :conditions => {:status => 'cancelld'}
I'm having trouble writing one that combines "published" and "combing soon." Here's what I've tried.
scope :public, :conditions => {"status == published || status == coming_soon"}
Any ideas?
Rails 2: named_scope :public, :status => ['published', 'coming_soon']
Rails 3: scope :public, where(:status => ['published', 'coming_soon'])
Rails will see the array and use the IN operator in the sql.
An a note: The other approach (chain existing scopes) of Article.published.coming_soon would NOT work because an Article can't be both of those things at the time time or be a subset of each other.
Another note: Careful when you want something dependent on a variable parameter. For example say you wanted "future appointment" for a scheduling system, you might write
[This is invalid]
Rails 2: named_scope :upcoming_appts, :conditions => (['appt_dt > ?', Time.now])
Rails 3: scope :upcoming_appts, where(['appt_dt > ?', Time.now])
However there's a problem: The Time.now will get evaluated the first time the class is evaluated not when the scope itself is evaluated.
To overcome this you use a lambda (silly name but basically means anonymous function - or to put it even simpler 'a function that doesn't actually have a name') as follows:
[This is valid]
Rails 2: named_scope :upcoming_appts, lambda {:conditions => (['appt_dt > ?', Time.now])}
Rails 3: scope :upcoming_appts, lambda {where(['appt_dt >= ?', Time.now])}
This scope will now get evaluated at execution time each time - it is used - so Time.now will be the actual current date-time.
I've got the hang of nested includes—they're great for performance—but what I'd really like is a 'nested find'. What's the best way to achieve something like:
#matchingProducts = Batch.find(:all,
:conditions => 'product.name LIKE ?', "%#{search}%",
:include => :product)
As you can see, Product is a nested attribute of Batch, but I want to find a batch based on Product.name.
Rails 3 I would use the AREL syntax:
#matches = Batch.where('product.name LIKE ?', "search").includes(:product)
Your idea was right, you can do matchingProducts = Batch.find(:all, :include => 'products', :conditions => ["products.name LIKE ?", whatever_you_want], :order => order_as_you_want)
I would like to construct a query in ActiveRecord based on GET params and using named_scope. I thought I'd chain some scopes and add conditions based on GET param availability, but I fear this will overwork the db, sending a new query on each query portion added:
# in model
named_scope :sorted, :order => 'title, author'
named_scope :archived, :conditions => { :is_archived => true }
named_scope :by_date, lambda { |date| { :conditions => [ 'updated_at = ?', date ] } }
# in controller / helper
#articles = Article.sorted.all
#articles = #articles.by_date(params[:date]) if params[:date]
#articles = #articles.archived if params[:archived] == '1'
Another option I've thought of is building a method chaining string that'll then be sent to the object using Object#send, but that seems a bit dirty and somewhat problematic when the named_scope receives arguments (like by_date). I realize I can construct a query string to use with :conditions => ... in ActiveRecord::Base#find, but I thought I'd try first with named_scope to see if it's possible to do lazy querying with the latter. Any suggestions on how to do this using the named_scope and not having the database bombarded by queries? thanks.
You can make lambda more smarter
# in model
named_scope :sorted, :order => 'title, author'
named_scope :archived, lambda { |is_archived| (is_archived == 1) ? {:conditions => {:is_archived => true}} : {}}
named_scope :by_date, lambda { |date| date.nil? ? {} : { :conditions => [ 'updated_at = ?', date ]}}
# in controller / helper
#articles = Article.by_date(params[:date]).archived(params[:archived]).sorted
I am using Rails 2.3.5 and is wondering if it is possible to rewrite this query using conditions as a hash.
joined_deals = Deal.all :joins => :shops
:conditions => ["shops.name = ?", name]
to something like :conditions => {"shops.name" => name}. Is it possible in Rails 2?
Yes, it is possible in Rails 2.
For more information, refer here:
specifying-conditions-on-the-joined-tables