How do I Query Polymorphic Comments when commentables can belong to another commentable? - ruby-on-rails-3.1

So I'm trying to do a relatively advanced query on a polymorphic model. I have the following models:
class Project < ActiveRecord::Base
has_many :project_stakeholders, :dependent => :destroy
has_many :features, :dependent => :destroy
has_many :iterations, :dependent => :destroy
has_many :comments, :as => :commentable, :dependent => :destroy
end
class ProjectStakeholder < ActiveRecord::Base
belongs_to :project
has_many :comments, :as => :commentable, :dependent => :destroy
end
class Feature < ActiveRecord::Base
belongs_to :project
has_many :comments, :as => :commentable, :dependent => :destroy
end
class Iteration < ActiveRecord::Base
belongs_to :project
has_many :comments, :as => :commentable, :dependent => :destroy
end
class User < ActiveRecord::Base
has_many :comments, :dependent => :destroy
end
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
belongs_to :created_by, :class_name => 'User'
end
I'm trying to find the current_user's comments for a particular project (project.comments, project.features.comments, project.iterations.comments, project.project_stakeholder.comments) and sort them in created_at descending order.
The best I have come up with is:
Class Project < ActiveRecord::Base
def all_comments_for_user(user)
Comment.where(:created_by_id => user.id).select { |c| c.commentable.attributes.has_key?('project_id') }.select { |c| c.commentable.project == self } | comments.where(:created_by_id => user)
end
end
But this doesn't address the descending create_at sequence.

Class Project < ActiveRecord::Base
def all_comments_for_user(user)
Comment.where(:created_by_id => user.id).order('created_at DESC').select { |c| c.commentable.attributes.has_key?('project_id') }.select { |c| c.commentable.project == self } | comments.where(:created_by_id => user)
end
end
Does this work ? I just added order('created_at DESC')

Related

Rails 4 dynamic form nested attributes without hidden input

Previously I asked a question about building an attendance list getting the students and building a check list to mark attendance and came up with this.
def new
#attendance_list = #classroom.attendance_lists.new
#attendance_list.attendances = #classroom.student_ids.map do |student_id|
#attendance_list.attendances.build(student_id: student_id)
end
end
def create
#attendance_list = #classroom.attendance_lists.new(attendance_list_params)
#attendance_list.classroom_id = params[:classroom_id]
respond_to do |format|
end
params
params.require(:attendance_list)
.permit(:post_date, :remark,
attendances_attributes: [:student_id, :attended, :remarks ])
with simple fields
= simple_form_for [#school, #classroom, #attendance_list] do |f|
= f.input :post_date
= f.input :remark
= f.simple_fields_for :attendances do |g|
** you see i needed a hidden student_id **
= g.input :student_id, as: :hidden
......
model
class AttendanceList < ActiveRecord::Base
belongs_to :classroom
has_many :attendances
has_many :students, :through => :attendances
accepts_nested_attributes_for :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :student
belongs_to :attendance_list
end
class Classroom < ActiveRecord::Base
belongs_to :school
has_and_belongs_to_many :students
has_many :attendance_lists
validates :class_name, presence: true
end
how do I do without the hidden input because this line doesn't seem to work.
build(student_id: student_id)
class AttendanceList < ActiveRecord::Base
belongs_to :classroom
has_many :attendances
has_many :students, :through => :attendances
accepts_nested_attributes_for :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :student
belongs_to :attendance_list
end
class Classroom < ActiveRecord::Base
belongs_to :school
has_and_belongs_to_many :students
has_many :attendance_lists
validates :class_name, presence: true
end
class Student < ActiveRecord::Base
has_many :attendances
has_many :attendance_lists, :through => :attendances
end
view file
= simple_form_for #student do |f|
= f.simple_fields_for :attendances do |g|
** your code **

Rails ActiveRecord query for most recent viewed resources

I recently made the following models:
class User < ActiveRecord::Base
has_many :resources
has_many :resource_views, :through => :user_resource_views, :source => 'Resource'
end
class Resource < ActiveRecord::Base
belongs_to :user
has_many :resource_views, :through => :user_resource_views, :source => 'Resource'
end
class UserResourceView < ActiveRecord::Base
attr_accessible :resource_id, :user_id
belongs_to :resource
belongs_to :user_id
end
Now, I want my home#index action to set #resources to the current_user's most recently viewed resources. How would you advise I proceed? Perhaps something similar to current_user.resource_views.find(:all, :order => 'created_at')?
In SQL, I would do something like:
SELECT *
FROM Resource
WHERE id IN (SELECT * FROM UserResourceView WHERE user_id = current_user.id)
... but then the ORDER BY created_at, hmmm
I'll be periodically adding progress updates throughout the day until I figure it out.
Given you're on Rails 3.x, what you're looking for is probably something like this:
class User < ActiveRecord::Base
has_many :resources
has_many :resource_views, :class_name => 'UserResourceView'
has_many :viewed_resources, :through => :resource_views, :source => :resource
def recently_viewed_resources
viewed_resources.order('user_resource_views.created_at DESC')
end
end
class Resource < ActiveRecord::Base
belongs_to :user
has_many :resource_views, :class_name => 'UserResourceView'
end
class UserResourceView < ActiveRecord::Base
attr_accessible :resource_id, :user_id
belongs_to :resource
belongs_to :user_id
end
And to access the collection in your controller:
current_user.recently_viewed_resources

Rails 3 Polymorphic Associations, User model with multiple content items?

In Rails 3 I'm trying to model a user content system, where the user can post different types of content, for example, note, photo, url etc.
From a Java/C# OO perspective, I would use a polymorphic relationship between the User and an interface representing a Content item, e.g. something called IUserContent.
I'm struggling to find an example that works how I expect it to, here's what I tried first, in short I'm getting confused about the implementation of polymorphic associations in ActiveRecord.
# user.rb model - includes...
has_many :notes, :as => :postable, :dependent => :destroy, :inverse_of => :postable
has_many :urls, :as => :postable, :dependent => :destroy, :inverse_of => :postable
has_many :photos, :as => :postable, :dependent => :destroy, :inverse_of => :postable
# url.rb ...
belongs_to :postable, :polymorphic => true, :inverse_of => :urls
# photo.rb
belongs_to :postable, :polymorphic => true, :inverse_of => :photos
# note.rb
belongs_to :postable, :polymorphic => true, :inverse_of => :notes
I'm still just following examples I've found, and frankly this feels like User is the polymorphic target, not the content.
I think I want something like this...
# within user.rb
has_many :postable, :as => :postable, dependent => :destroy, :inverse_of => :users
# photo.rb
# url.rb
# note.rb
# all have the following...
belongs_to :user, :polymorphic => true, :inverse_of => :postable
... looking for a few pointers in the right direction.
Thank you.
The only way you can do that is if all of these classes inherit from the same base class, as in:
class User < ActiveRecord::Base
has_many :postable, :as => :postable, :dependent => :destroy, :class_name => 'Postable'
end
class Postable < ActiveRecord::Base
belongs_to :user, :polymorphic => true
end
class Photo < Postable
end
class Url < Postable
end
class Note < Postable
end
So, you have to use the ActiveRecord single-table inheritance to model such a relationship.

Rails 3, has_many :through and :polymorphic - Should I need to do this?

Ok so here goes. I don't know if I'm over complicating things or if I'm just still so new to Rails that I don't understand the basics. What I want in sudo code is this:
User
has_many projects as owner through relationship
has_many projects as contributor through relationship
has_many projects as follower through relationship
Project
has_one user as owner through relationship
has_many users as followers through relationship
has_many users as contributors through relationship
Relationship
belongs_to user
belongs_to project
Then I'm wanting to have the following magical things:
owner = Project.owner
followers = Project.followers
contributors = Project.contributors
projects = User.projects
myprojects = User.projects... (now I'm really not sure)
followedProjects = ...
contributingProjects = ...
So in writing that down I can see that there is another gap in my understanding of this model. The users can have the role of owner, follower or contributor or any combination of all three.
In terms of real code I have added here what I think is the relevant parts:
class User < ActiveRecord::Base
has_many :user_project_relationships, :as => :relateable, :class_name => "UserProjectRelationship"
has_many :projects, :as => :owner, :through => :relateable, :class_name => "Project", :source_type => :owner
has_many :projects, :as => :follower, :through => :relateable, :class_name => "Project", :source_type => :follower
has_many :projects, :as => :contributor, :through => :relateable, :class_name => "Project", :source_type => :contributor
end
class Project < ActiveRecord::Base
has_many :user_project_relationships, :as => :relateable, :class_name => "UserProjectRelationship"
has_one :user, :as => :owner, :through => :relateable, :class_name => "User"
has_many :users, :as => :followers, :through => :relateable, :source_type => :follower, :class_name => "User"
has_many :users, :as => :contributors, :through => :relateable, :source_type => :contributor, :class_name => "User"
end
class UserProjectRelationship < ActiveRecord::Base
belongs_to :user
belongs_to :project, :polymorphic => true
end
The migration for the relationships model is:
class CreateUserProjectRelationships < ActiveRecord::Migration
def self.up
create_table :user_project_relationships do |t|
t.integer :relateable_id
t.string :relateable_type
t.integer :project_id
t.timestamps
end
add_index :user_project_relationships, [:relateable_id, :relateable_type], :name => :relateable
add_index :user_project_relationships, :project_id
end
def self.down
drop_table :user_project_relationships
end
end
Currently I get errors for things like project.users ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :relateable in model Project
I feel like I'm too in the wilderness here to really get what I want, and maybe relying on magical rails to do more than it does. Any guidance on the best path would be greatly appreciated.
Thanks in advance
Steve
Rails can do alot, but I think instead you're trying to make the Relationship model do too much. Each of those are a different kind of relationship, so I think try to keep them so.
Split that up into separate join models:
class User < ActiveRecord::Base
has_many :owned_projects, :class_name => "Project", :foreign_key => :owner_id
has_many :projects_followers
has_many :followed_projects, :class_name => "Project", :through => :projects_followers
has_many :projects_contributors
has_many :contributed_projects, :class_name => "Project", :through => :projects_contributors
end
class Project < ActiveRecord::Base
belongs_to :owner
has_many :projects_followers
has_many :followers, :class_name => "User", :through => :projects_followers
has_many :projects_contributors, :foreign_key => :contributor_id
has_many :contributors, :class_name => "User", :through => :projects_contributors
end
class ProjectsFollowers < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
class ProjectsContributors < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Should be a lot closer to what you want. You can then indeed do
project.owner
project.followers
project.contributors
and
user.owned_projects
user.followed_projects
user.contributed_projects
That should either work, or get you pretty close.
I think your mixup came from trying to make a polymorphic relationship, which I don't think is desireable here. AFAI grok, the use case for polymorphic relationships is when you want 1 Model to be related to Any number of other models in the same way. That's not the case here, as you have 2 models with 3 different types of relationships between them.

How do I abstract similar validations/relations to parent class in Ruby/Rails?

I have these VERY similar classes:
class DeliveryDocument < CommercialDocument
# Relations
belongs_to :biller, :class_name => 'Company'
belongs_to :customer, :class_name => 'Company'
belongs_to :customer_center, :class_name => 'Center'
has_many :delivery_document_lines, :dependent => :destroy
alias_attribute :lines, :delivery_document_lines
# Some configuration
accepts_nested_attributes_for :delivery_document_lines
acts_as_freezable :only_dependencies => true,
:has_many => [:delivery_document_lines],
:belongs_to => [:biller, :customer, :customer_center]
acts_as_clonable :has_many => [:delivery_document_lines]
validates_each :lines do |record, attr, value|
# ...
end
end
And:
class InvoiceDocument < CommercialDocument
self.
# Relations
belongs_to :biller, :class_name => 'Company'
belongs_to :customer, :class_name => 'Company'
belongs_to :customer_center, :class_name => 'Center'
has_many :invoice_document_lines, :dependent => :destroy
alias_attribute :lines, :invoice_document_lines
# Some configuration
accepts_nested_attributes_for :invoice_document_lines
acts_as_freezable :only_dependencies => true,
:has_many => [:invoice_document_lines],
:belongs_to => [:biller, :customer, :customer_center]
acts_as_clonable :has_many => [:invoice_document_lines]
# Validations
validates_each :lines do |record, attr, value|
# ...
end
end
I also have some methods I didn't paste that could be extracted to the parent. I only need to know the class name in the parent. When I do this:
class CommercialDocument < Document # document inherits from AR::Base
# ...
has_many :"#{self.to_s.underscore}_lines", :dependent => :destroy
# ...
accepts_nested_attributes_for :"#{self.to_s.underscore}_lines"
# ...
end
it doesn't work, because self.to_s is CommercialDocument.
How would you refactor this behavior in parent class?
I could put things in a module and import it, but then the whole hierarchy of documents becomes almost useless.
I already have the documents' hierarchy, so if I can, I want to use it if there is a way.
You could try using Class.inherited

Resources