How to call a scope across a has_many / through relationship - ruby-on-rails-3

I have the following (on RoR 3.1 and MySQL 5.1):
class Menu < ActiveRecord::Base
has_many :menu_headers
has_many :menu_items, :through => :menu_headers
belongs_to :location
end
class MenuHeader < ActiveRecord::Base
acts_as_tree :parent_id
has_many :menu_items
belongs_to :menu
end
class MenuItem < ActiveRecord::Base
scope :is_enabled, where(:is_enabled => true)
belongs_to :menu_header
end
I'd like to be able to call the scope across the relationship; something like this:
# call the scope :is_enabled here
Menu.find(12).(where menu_items.is_enabled)
but not sure how to do this.
I'd like the behavior for:
Menu.find(12)
to continue to pull menu_items where is_enabled=false
Any ideas on how to do this?
thx
edit #1
added the act_as_tree and location associations as these also need to be working.
Something like this Scope with join on :has_many :through association might work but seems a little bit ugly

This should do the trick:
Menu.find(12).menu_items.is_enabled
It will return all enabled menuitem associated with the menu with id 12.

Related

has_many :condition multiple models how to use join here?

I have the following model structure:
Model Visitor
class Visitor < ActiveRecord::Base
has_many: triggers
end
Model Trigger
class Trigger < ActiveRecord::Base
belongs_to :visitor, :inverse_of => :triggers
belongs_to :event, :inverse_of => :triggers
end
Model Event
class Event < ActiveRecord::Base
has_many: triggers
end
I am trying to setup a custom association in Visitor model like so:
has_many: triggers_that_pass_some_condition ,:class_name => "Trigger",
:conditions => ["triggers.some_column >= events.some_column"]
The problem is that it doesn't work .. I am guessing I have to do some kind of join to compare columns of two separate models (that are associated with each other)
I have tried
triggers.some_column >= triggers.event.some_column
That does not work either. Anyone has any suggestions? thanks!
Try the following code..
class Trigger < ActiveRecord::Base
belongs_to :event
belongs_to :visitor
end
# Visitors.rb
has_many :triggers_with_condition, -> { includes(:event).where(some_trigger_column >= event.some_event_column)}, class_name: "Trigger"
Make sure you first add the correct association between Visitor and Trigger in your model setup. From there, you can add a custom association as follows:
class Visitor < ActiveRecord::Base
has_many :approved_triggers, -> { includes(:events).where("events.something = ?", true).references(:events) }, class_name: 'Trigger', inverse_of: :visitor
end
class Trigger < ActiveRecord::Base
belongs_to :visitor, inverse_of :triggers
end
Right now your Trigger class holds no association to a Visitor.
Thanks to the clue from Darpa, I eventually settled on this:
has_many :custom_trigger, {:class_name => "Trigger", :include => :event,
:conditions => ["triggers.some_column >= events.another_column"]}

How to solve N + 1 issue in select box?

In my Rails app I have people which can have many projects and vice versa. The two tables are linked by a join table jobs.
class Project < ActiveRecord::Base
belongs_to :user
has_many :people, :through => :jobs
def self.names_as_options
order(:name).map{ |p| [ p.name, p.id, :'data-people_count' => p.people.count ] }
end
end
In one of my forms I have this select box:
<%= f.select :project_id, Project.names_as_options %>
The problem is that the count on people gives me an N + 1 query for each project.
What is the best way to overcome this?
Thanks for any help.
Try use scope whit lambda, is for that, here is one example how this works:
scope :top, lambda { order('views DESC').limit(20) }
in controller just call
Project.top
this is the best way to filter results in Ruby on Rails.
If you use counts, you might better use also counter caches, they are automatically used when needed. http://guides.rubyonrails.org/association_basics.html
You could add a "counter cache" of the people count for each project. Ordinarily you would add a field to the projects table via a migration
add_column :projects, :people_count, :integer, :default => 0
and then declare to use :counter_cache in the Person model
class Person < ActiveRecord::Base
belongs_to :projects, :counter_cache => true
end
This probably won't do what you want as it stands, as you are going through a Job join. So, the Person#projects declaration is just a convenient finder, and not used in any callback. But, you get the idea.
You could add a column as suggested above, and then make use of some callback methods in the Job class.
class Job
def update_project_counter
project.update_people_counter
end
after_create :update_project_counter
after_destroy :update_project_counter
end
class Project
def update_people_counter
self.update_attribute :people_count, people.count
end
end
Or something similar thats appropriate. You should then only need the one query.
class Project < ActiveRecord::Base
def self.names_as_options
order(:name).map do |p|
[p.name, p.id, :'data-people_count' => p.people_count]
end
end
end
Eager loading will solve this issue, use 'includes' as follows.
Example,
class LineItem < ActiveRecord::Base
belongs_to :order, -> { includes :customer }
end
class Order < ActiveRecord::Base
belongs_to :customer
has_many :line_items
end
class Customer < ActiveRecord::Base
has_many :orders
end
Ref: http://guides.rubyonrails.org/association_basics.html

Cannot modify association because the source reflection class is associated to via :has_many + rails 3.0.10

Getting Following Error
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection in ProjectController#create
Cannot modify association 'ProjectMaster#tag_masters' because the source reflection class 'TagMaster' is associated to 'ProjectTag' via :has_many.
Following are my models.
class ProjectTag < ActiveRecord::Base
has_many :tag_masters
has_many :project_masters
end
class TagMaster < ActiveRecord::Base
has_many :project_tags
has_many :project_masters, :through => :project_tags
end
class ProjectMaster < ActiveRecord::Base
has_many :project_tags
has_many :tag_masters, :through => :project_tags
# Some more code and associations here..
end
I am new to rails and tried to solve it but I don't think i can change my associations.
I am using rails 3.0.10
Please help me out here.
Thanks
I think my associations were wrong.
class ProjectTag < ActiveRecord::Base
has_many :tag_masters
has_many :project_masters
end
instead of has_many; I had to use belongs_to.

inheriting scope from a has_many relationship

I am using ruby on rails 3.1 and have 2 models, an event and a group. Each event has_many groups, but has to have at least one "master" group, where the column :is_master => true
Class Group < ActiveRecord::Base
has_many :users
belongs_to :event
scope :master, where (:is_master => true)
end
Class Event< ActiveRecord::Base
has_many :groups
def master_group
groups.master
end
end
I want to be able to default all properties of the master group to the event, so for example, event.users.count should be the same as event.master_group.users.count.
Is there any way to do something like this? Can I do a has_many :through => master_group? Am I approaching this the wrong way?
Thanks!
I think what I was looking for was delegate
delegate :users, :to => :master_group
hope this helps someone...

How to find all that has multiple relationships in many to many with Rails3 in a elegant way?

I have a User class with
class User < ActiveRecord::Base
has_many :forum_subscriptions
has_many :forums, :through => :forum_subscriptions
And a Forum class with
class Forum < ActiveRecord::Base
has_many :users
I want to find all the users that are subscribed to forum "sport", forum "TV" and forum "Hobby"
What is the most elegant way to do it?
(I have a lot of ugly stuff in my mind :-)
Here is something I would use:
User.joins(:forums).where("forums.title IN (?)", %w(sport TV Hobby)).group("users.id")
I assumed the column for the forum's title is 'title'. Change it when you're using some other name. You can put this inside a nice scope inside the User Model to make it a bit more dynamic.
scope :subscribed_to, lambda { |forum_titles| joins(:forums).where("forums.title IN (?)", forum_titles).group("users.id") }
Now you can call this scope from inside the Controller or some other place:
User.subscribed_to(%w(sport TV Hobby)) # => Get all users that are subscribed to sport, TV and Hobby
User.subscribed_to(["sport", "TV", "Hobby"]) # => Does the same
User.subscribed_to(%w(Hobby)) # => Get all users that are subscribed to Hobby
User.subscribed_to("Hobby") # => Does the same
I assumed you have the following relationships:
class User < ActiveRecord::Base
has_many :forum_subscriptions
has_many :forums, :through => :forum_subscriptions
end
class Forum < ActiveRecord::Base
has_many :forum_subscriptions
has_many :users, :through => :forum_subscriptions
end
class ForumSubscription < ActiveRecord::Base
belongs_to :user
belongs_to :forum
end
Hope this is what you needed. :)