How to get a subset of has_and_belongs_to_many relationship - ruby-on-rails-3

In my Rails 3.2 app I have a has_and_belongs_to_many :notes association on Profile. Note has a type on it - "urgent" or "standard". (Note has_and_belongs_to_many :profiles.)
I'd like to be able to do something like profile.urgent_notes without building it in a method like:
def urgent_notes
urgent_notes = Array.new
goals.each do |g|
if g.type == "urgent"
urgent_notes << g
end
end
urgent_notes
end
So is there a clean way to do this by adding another association? Or would something like a scope be best?

A scope on Note would best best.
scope :urgent, where(type: 'urgent')
Then you can do profile.notes.urgent to get the urgent notes.

If you don't want to use a scope, here's a rewrite of your Profile method:
def urgent_notes
self.goals.map {|g| g.type == 'urgent'}
end
You don't need to create a new array or anything. Rails and Ruby do all that work for you.

Related

Rails ActiveRecord: find related model COUNT with a HABTM (has_and_belongs_to_many) relationship

I have two models
class Items < ApplicationRecord
has_and_belongs_to_many :users
end
class Users < ApplicationRecord
has_and_belongs_to_many :items
end
I want to find out all the items that have 2 users associated with them, and delete them.
I could think of doing it using an iterative approach like:
Item.all.each do |i|
if i.users.all.count == 2
i.delete
end
end
Is there a more elegant way (oneliner?) to do this using only the ActiveRecord ORM ?
Thanks.
Not sure but following should work
Item.joins(:users).group('items.id').having('COUNT(items_users.user_id) = 2').destroy_all
Use group and having for filter the condition.
Item.joins(:users)
.group('COUNT(users.id)')
.having('COUNT(users.id) = ?', 2)
.select('COUNT(users.id) AS users_count')
.destroy_all

What's the best practice to save multiple foreign keys in "create"?

I'm building a crud with nested resource.
Post has_many :comments and my comments belongs_to :user and belongs_to :post. When I am adding a new comment, I am currently doing something like this in the create action of the comment controller:
#post = Post.where(id: params[:post_id]).first
#post_comments = #post.post_comments.build
#post_comments.update_attributes(params[:post_comment])
#post_comments.user = current_user
if #post_comments.save
...
I also saw this post: https://stackoverflow.com/a/5978113 which seems to do what I am doing.
This seems choppy, and I'm not sure if I am doing this correctly. Is there a better way? What is the best practice?
I don't know of any defined best practice for this but with your code, you don't need the call to update_attributes. There are 2 ways to save both foreign keys (actually 4 ways, if you're goining to build the comment from the user)
First option:
params[:post_comment].merge!(user_id: current_user.id)
#post = Post.where(id: params[:post_id]).first
#post_comment = #post.post_comments.build(params[:post_comment])
if #post_comment.save
...
else
...
end
Second option:
#post = Post.where(id: params[:post_id]).first
#post_comment = #post.post_comments.build(params[:post_comment])
#post_comment.user = current_user
if #post_comment.save
...
else
...
end
Just a note though, you should use the singular form if you're dealing with a singular resource so #post_comments should be #post_comment

relation one_to_one: get a list of non-linked "belongs_to" entities

I have an optimization question. I search how to use ActiveRecord to do a request to get all entities in a one_to_one or one_to_many which don't have any link.
I have:
class Model1 < ActiveRecord::Base
has_one :model2
...
and
class Model2 < ActiveRecord::Base
belongs_to :model1
...
If I want the list of all model2s non-link, I just have to do:
unlinked_model2s = Model2.where(:model1_id => nil)
But how I do the same for the model1s? I would have the list of all model1s which are not linked to a model2.
I tried many things, but the only way to make it works, is to to all the requests one by one, which is horrible:
unlinked_model1s = Array.new
Model1.all.each do |model1|
unless model1.model2
unlinked_model1s << model1
end
end
Thank you for your help!
This would require to write some SQL. By default AR supports only inner join, so you can't use Model1.joins(:model2).where(...).
Looks like you can try Model1.joins("left join model2s on (model2s.model1_id = model1s.id) ").where(:model2s => {:id => nil})
or
Model1.joins("left join model2s on (model2s.model1_id = model1s.id) ").where("model2s.id is NULL")
Not sure this is syntactically (can't try the code :) ) correct, but hope this will give you an idea on how to accomplish your task.

Storing user_id in the paper_trail versions table

I am using paper trail to audit changes to data and would like to store the user_id of the current user in addition to the "whodunnit" column that paper_trail stores by default.
I had no trouble modifying the versions migration to add the user_id column. But I haven't figured out an easy way to set that column from the various models in my app.
It seems like this should work:
has_paper_trail :meta => { :user_id => current_user.id
}
And, I think it might work if I had access to the current_user in my models. But I don't. After researching how to get access to the current_user in my model, I see there is a philosophical debate here. That's not my question though.
So I'm thinking of using a gem like sentient_user or sentient_model to give me access to the current_user in my models so I can set it with something like the code above.
However, adding these gems seems complicated for the little thing I'm trying to do here. I'm wondering if there is an easier way.
What is the easiest way to add the user_id of the person who took the action to the versions table?
The current_user don't exists in models by itself, it appears from controller. So, standard approach is applicable:
class ApplicationController < ActionController::Base
def user_for_paper_trail
current_user if user_signed_in?
end
def info_for_paper_trail
{ user_id: current_user.id } if user_signed_in?
end
end
# config/initializers/paper_trail.rb
class Version < ActiveRecord::Base
attr_accessible :user_id
end

How to get a scope for all database rows in rails 3?

assume we the following setup:
class Post < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
has_many :posts
end
assume further the user has a boolean attribute 'admin', which indicates if he is a global admin or not.
I want to write a method (or a scope?) for the User class, called 'visible_posts'. If the user is no admin, it should return just its own posts. If he IS admin the method should return all posts in the system.
My first attempt was something like this:
class User < ActiveRecord::Base
[...]
def visible_posts
if admin?
Post.all
else
posts
end
end
end
Problem here is that Post.all returns an Array, but I would rather like to have an ActiveRecord::Relation like I get from posts to work with it later on.
Is it somehow possible to get an ActiveRecord::Relation that represents ALL posts ?
You can do Post.scoped i guess in Rails
And later on this you can call .all to fetch the results