How to call instance method of comment class through Article Model ? - ruby-on-rails

class Comment < ActiveRecord::Base
belongs_to :article
end
class Article < ActiveRecord::Base
has_many :comments do
def posted_comments
#user_comment is just an attribute of comment.
collect(&:user_comment)
end
end
end
to fetch the posted comments :
Article.first.comments.posted_comments
=> ["Nice article posted", "comment 2 added", "Good article"]
Above one is fetching correct results, but I want to have a more compact version.
Something like this:
Article.first.posted_comments
#this should list the collection of comments on the article.
Can we do something like this with Rails ActiveRecord ?

For simply solution, you can define method called posted_comments that calls nested association as the following:
def posted_commments
self.comments.posted_comments
end
Or, Try the following code:
has_many :posted_comments, -> { select("user_comment") }, class_name: "Comment"

Related

Cannot modify association ":has_many." using Ruby on rails

I'm working with three tables as follows:
article.rb
class Article < ActiveRecord::Base
has_many :comments
has_many :comentarios, :through => :comments
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :article
has_many :comentarios
end
and comentario.rb
class Comentario < ActiveRecord::Base
belongs_to :article
end
Everything works fine until I attempt to add a 'comentario' and returns this error
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection in ComentariosController#create
Cannot modify association 'Article#comentarios' because the source reflection class 'Comentario' is associated to 'Comment' via :has_many.
This is the code I use to create a new 'comentario'
comentarios_controller.rb
class ComentariosController < ApplicationController
def new
#comentario = Comentario.new
end
def create
#article = Article.find(params[:article_id])
#comentario = #article.comentarios.create(comentario_params)
redirect_to article_path(#article)
end
private
def comentario_params
params.require(:comentario).permit(:comentador, :comentario)
end
end
The output returns an error in the line where I create #comentario from calling #article but I can't see why since Ruby documentation says that once I associate comentario to article using :through, I can simply call something like #article.comentario.
Any idea of what is causing this error?
or do you have any suggestion on how to achieve this association in any other way?
Ok. The issue is that Rails is confused about which article to use here.
Your Comment model belongs_to :article but also your Commentario belongs_to :article... so if you use #article.commentarios - it's confused as to whether the article refers to the article of the comment or the article of the commentario.
You will probably need to update your form to be more explicit about what you're referring to. A form for the commentario should actually include fields for the comment it creates.
Somebody else had the same problem here. You may wish to look at the solution here: "Cannot modify association because the source reflection class is associated via :has_many"

How to create your own methods like model.association.build in Rails?

I was wondering if there is some standard way to create methods like the build method, which is generated on a has_many association. For illustration, assuming the following setup
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
end
Rails automatically generates a post.comments.build method. Is there a standard way or The Rails Way to create my own method like this? I tried it by defining the method on the singleton class of the comments object, like so:
class Post < ActiveRecord::Base
after_initialize do
class << comments
def go
#do something where I can access the the 'owning' post object
end
end
end
end
But this code seemed to break after an ActiveRecord update. So, I was wondering if there is a better way.
You can add methods to the association by passing a block to the has_many method call and defining the method. An example taken from the docs:
has_many :employees do
def find_or_create_by_name(name)
first_name, last_name = name.split(" ", 2)
find_or_create_by(first_name: first_name, last_name: last_name)
end
end

Is there a Rails active record method I can call to get all #images?

I have these two models:
class Article < ActiveRecord::Base
has_many :images
end
and
class Image < ActiveRecord::Base
belongs_to :article
end
To get all my articles I simply do this:
#article = Article.all.find(params[:id])
Then I do the following to get all images this article has:
#images = Image.where article_id: #article.id
Is there some active record method that I can call to get all my #images, rather than what I have above? I just wonder if there is a better way than what I am doing.
I tried #article.images but it only returned the relation without the data.
Try this :
#article.images.all

accessing associations within before_add callback in Rails 3

In Rails 3.2 I have been looking for a way to traverse the associations of an object within the before_add callback.
So basically my use case is:
class User < ActiveRecord::Base
has_and_belongs_to_many :meetings
end
class Meeting < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :comments, :before_add => :set_owner_id
end
class Comment < ActiveRecord::Base
belongs_to :meeting
end
def set_owner_id(child)
child.owner_id = <<<THE USER ID for #user >>>
end
and I am creating a comment within the context of a user:
#user.meetings.first.comments.create
How do I traverse the associations from within the before_add callback to discover the id of #user? I want to set this at model level. I have been looking at proxy_association, but I may be missing something. Any ideas?
You should probably create the comment in the context of the meeting, no? Either way, you should handle this in the controller since you'll have no access to #user in your model.
#comment = Meeting.find(id).comments.create(owner_id: #user, ... )
But if you insist on your way, do this:
#comment = #user.meetings.first.comments.create(owner_id: #user.id)

Active Relation: Retrieving records through an association?

I have the following models:
class User < ActiveRecord::Base
has_many :survey_takings
end
class SurveyTaking < ActiveRecord::Base
belongs_to :survey
def self.surveys_taken # must return surveys, not survey_takings
where(:state => 'completed').map(&:survey)
end
def self.last_survey_taken
surveys_taken.maximum(:position) # that's Survey#position
end
end
The goal is to be able to call #user.survey_takings.last_survey_taken from a controller. (That's contrived, but go with it; the general goal is to be able to call class methods on #user.survey_takings that can use relations on the associated surveys.)
In its current form, this code won't work; surveys_taken collapses the ActiveRelation into an array when I call .map(&:survey). Is there some way to instead return a relation for all the joined surveys? I can't just do this:
def self.surveys_taken
Survey.join(:survey_takings).where("survey_takings.state = 'completed'")
end
because #user.survey_takings.surveys_taken would join all the completed survey_takings, not just the completed survey_takings for #user.
I guess what I want is the equivalent of
class User < ActiveRecord::Base
has_many :survey_takings
has_many :surveys_taken, :through => :survey_takings, :source => :surveys
end
but I can't access that surveys_taken association from SurveyTaking.last_survey_taken.
If I'm understanding correctly you want to find completed surveys by a certain user? If so you can do:
Survey.join(:survey_takings).where("survey_takings.state = 'completed'", :user => #user)
Also it looks like instead of:
def self.surveys_taken
where(:state => 'completed').map(&:survey)
end
You may want to use scopes:
scope :surveys_taken, where(:state => 'completed')
I think what I'm looking for is this:
class SurveyTaking < ActiveRecord::Base
def self.surveys_taken
Survey.joins(:survey_takings).where("survey_takings.state = 'completed'").merge(self.scoped)
end
end
This way, SurveyTaking.surveys_taken returns surveys taken by anyone, but #user.survey_takings.surveys_taken returns surveys taken by #user. The key is merge(self.scoped).
Waiting for further comments before I accept..

Resources