Has_and_belongs_to_many in Rails 3 - ruby-on-rails-3

I've tried to look through stackoverflow to remedy my situation but unfortunately I've just made myself thoroughly confused.
I have two has_many to has_many relationships: Users has_and_belongs_to_many Roles, and Roles has_and_belongs_to_many Pods. I've already set up the correct tables and everything (i.e. the role_users and pod_roles tables): I know it's at least partially working because I'm able to do #user.roles and get all of that user's roles.
However, I want to take it one step further: I want to be able to do something akin to #user.roles.pods to get all of the pods pertaining to that user. After #user.roles.pods didn't work: here's what I tried to do:
#current_user_roles = current_user.roles
#pods = Array.new
#current_user_roles.each do |role|
#pods.push(role.pods)
end
#pods.each do |pod|
puts(pod.name+"-----------------")
end
Didn't work. This is just to try to get something super simple -- to try to get all of the pods' names to appear to I can tell if it's working.

Here's something you could try:
#pods = current_user.roles.collect { |role| role.pods }.flatten

Related

Rails nested resource join security best practices

I've started a new project which requires accounts to be able to view their candidates and related tasks.
account.rb
has_many :candidates
candidate.rb
has_many :tasks
belongs_to :account
task.rb
belongs_to :candidate
Routes are set up to allow /candidates/4/tasks/3 where account X has access to candidate 4 which has task 3.
tasks_controller.rb is currently like:
def show
#task = Task.find params[:id]
end
QUESTION: What is the best practice approach to ensure that other accounts don't have access to task 3?
One idea might be something like this but seems very messy:
def show
#task = Account.find(current_account).candidates.find(params[:candidate_id]).tasks.find(params[:id)
end
So if the join fails, you don't have access.
Another way might be done using scopes. Where you make sure all tasks queried are joined with candidates and current_account.
I could also do a before_filter to do a standalone query on candidates table to check that the account has access. This will add an extra query so not ideal.
I'm waffling here... but would love to know how others go about this?
Do it by rails way...
current_account = Account.find params[:id]
#tasks = current_account.candidates.collect(&:tasks)
This will do your job.

How to test the membership in a many-to-many relation in Rails3

For example, one user has joined many group; and one group has many user members. Now I get a user object and a group object, I want to know whether this user is a member of this group.
I can see some methods, but still wandering whether there is a better way?
So, if i understand your question, what you have is something like:
class User < ActiveRecord::Base
has_many :groups, :though => :user_members
class Group < ActiveRecord::Base
has_many :users, :though => :user_members
And you want to know if a user is a member of a specific group.
So, given something like:
u = User.first
g = Group.first
Just do:
u.groups.include? g
Thats all there is to it!
ian.
ipd's way is fine, another more Rails oriented way to do it is :
u.groups.exists?(g)

Why is a SystemStackError caused with a has_many association in Ruby on Rails?

I stumbled over a SystemStackError and found where it is caused in the source code. Though, I did not quite understand why it happens. Maybe you can help me.
Here is the scenario:
There are two models Facility and Location given by their model definitions in the following.
class Location < ActiveRecord::Base
belongs_to :facility
accepts_nested_attributes_for :facility
end
class Facility < ActiveRecord::Base
has_many :locations
accepts_nested_attributes_for :locations
end
Now I create an object of each class in the Rails console: rails c.
location = Location.create(...)
facility = Facility.create(...)
Then I want to associate both with each other.
location.facility = facility
facility.locations << location
I cannot execute the last command when I executed the first before - it raises a SystemStackError: stack level too deep. Though, I can run the association commands separate from each other or sequential but in reverse order. The problem is that I cannot add the location again. Why?
Why do both?
This line:
facility.locations << location
Will already set the location's facility to be the specified facility. Both lines in this case are doing the same thing. What I would recommend doing is to use the association builder, like this:
facility.locations.create!(...)
This way, Rails takes care of setting the facility_id field, rather than you doing a manual assignment after it.
The first thing that I would suspect here is that the has_many association is really has_too_many. In other words, you may have too many locations in the relationship.
In fact, given the code you posted, you seem to have created an infinite loop of associations. You wrote:
accepts_nested_attributes_for :facility
I am assuming that this causes ActiveRecord to open the facility attribute where it finds another location with yet another facility attribute ad infinitem. before you dig too deeply, try this to see if it works:
facility.locations << location
location.facility = facility
However, be wary because this might just push the stack error to some other place in the app. If you Google for that error message you can find several people who have run into infinite recursion problems, generally related to saving a record.

Constructing a has-and-belongs-to-many query

I have a rails app (running on version 2.2.2) that has a model called Product. Product is in a has-and-belongs-to-many relationship with Feature. The problem is that I need have search functionality for the products. So I need to be able to search for products that have a similar name, and some other attributes. The tricky part is that the search must also return products that have the exact set of features indicated in the search form (this is represented by a bunch of checkboxes). The following code works, but it strikes me as rather inefficient:
#products = Product.find(:all, :conditions=>["home=? AND name LIKE ? AND made_by LIKE ? AND supplier LIKE ? AND ins LIKE ?",hme,'%'+opts[0]+'%','%'+opts[1]+'%','%'+opts[3]+'%','%'+opts[4]+'%'])
#see if any of these products have the correct features
if !params[:feature_ids].nil?
f = params[:feature_ids].collect{|i| i.to_i}
#products.delete_if {|x| x.feature_ids!=f}
end
I'm sorry that my grasp of rails/sql is so weak, but does anyone have any suggestions about how to improve the above code? Thanks so much!
First, i would recommend you to manually write a FeatureProduct model (and not use the default 'has_and_belongs_to_many')
EG
class FeatureProduct
belongs_to :feature
belongs_to :product
end
class Product
has_many :feature_products
has_many :features, :through => :feature_products
end
class Feature
has_many :feature_products
has_many :products, :through => :feature_products
end
For the search: You may find the gem SearchLogic to be exactly what you need. It has support for 'LIKE' conditions (it means that you can write in a more 'Rails way' your query). It also has support for performing a search with conditions on a related model (on your Feature model, to be more precise).
The solution would be something like:
search = Product.search
search.name_like = opt[0]
search.made_by_like = opt[1]
...
search.feature_products_id_equals = your_feature_ids
..
#product_list = search.all
There is also an excellent screencast explaining the use of this gem.
Good luck :)

Help with Rails find_by queries

Say if #news_writers is an array of records. I then want to use #news_writers to find all news items that are written by all the news writers contained in #news_writers.
So I want something like this (but this is syntactically incorrect):
#news = News.find_all_by_role_id(#news_writers.id)
Note that
class Role < ActiveRecord::Base
has_many :news
end
and
class News < ActiveRecord::Base
belongs_to :role
end
Like ennen, I'm unsure what relationships your models are supposed to have. But in general, you can find all models with a column value from a given set like this:
News.all(:conditions => {:role_id => #news_writers.map(&:id)})
This will create a SQL query with a where condition like:
WHERE role_id IN (1, 10, 13, ...)
where the integers are the ids of the #news_writers.
I'm not sure if I understand you - #news_writers is a collection of Role models? If that assumption is correct, your association appears to be backwards - if these represent authors of news items, shouldn't News belong_to Role (being the author)?
At any rate, I would assume the most direct approach would be to use an iterator over #news_writers, calling on the association for each news_writer (like news_writer.news) in turn and pushing it into a separate variable.
Edit: Daniel Lucraft's suggestion is a much more elegant solution than the above.