ActiveRecord get all + associated details - ruby

I am trying to retrieve a list of all tasks, where each task has a developer and reviewer. I am able to retrieve the list but it contains developer_id and reviewer_id. How do I retrieve a list containing developer name and retriever name?
class Person < ActiveRecord::Base
end
class Unread_Object < ActiveRecord::Base
belongs_to :person
end
class Developer < Person
has_many :tasks
end
class Reviewer < Person
has_many :tasks
has_many :unread_objects
end
class Task < ActiveRecord::Base
belongs_to :developer
belongs_to :reviewer
has_many :documents
after_save :add_task_to_unread_objects
protected
def add_task_to_unread_objects
Person.find_each do |person|
Unread_Object.create(
:person_id => person.id,
:internal_object_id => self.internal_object_id,
:unread_cause => 'Create')
end
end
end
Things I have tried.
get '/taskslist' do
#Task.includes([:developer, :reviewer]).all.to_json
#Task.joins(:developer,:reviewer).select("tasks.*, people.*").to_json #works somewhat but only shows one name
#Task.includes(:reviewer.name,:developer.name).all.to_json #"undefined method `name' for :reviewer:Symbol"
#Task.find(:all, :include => {:people => :name}).to_json #Couldn't find all Tasks with 'id': (all, {:include=>{:people=>:name}})
end
I hope to get Tasks json with nested json for developer, reviewer and other objects.
This question is follow up of this.

After some searching found as_json(include: <association>)
So this works
Task.includes(:developer,:reviewer).all.as_json(include: [:developer,:reviewer]).to_json
But other alternatives need to be seen.

Related

How should I use attributes on the "through" object in a has_many: through: ActiveRecord relation?

class Reservation << ApplicationRecord
has_many :charges_reservations, :dependent => :destroy
has_many :charges, :through => :charges_reservations
monetize :price_cents
def associate_charge(charge, portion)
# looking for help here
end
def owing
price - amount_paid
end
def amount_paid
# looking for help here
end
end
class ChargesReservation << ApplicationRecord
belongs_to :charge
belongs_to :reservation
monetize :portion_cents
end
class Charge << ApplicationRecord
has_many :charges_reservations
has_many :reservations, :through => :charges_reservations
monetize :amount_cents
validates :state, inclusion: {in: %w(failed successful pending)}
def successful?
state == "successful"
end
end
What I want to know is, how to access the portion attribute on
ChargesReservation both when associating the charges with the reservations,
and when asking whether the reservation is fully paid. Both Charge and
Reservation are created at different points in the user flow.
So my question is twofold:
what's the best way to create the association once I have a charge?
how do I get the sum of a reservation's portions of all successful charges
associated with it, as a model method. I know, roughly, how to achieve this
in SQL (I'm rusty, but I'd get there) but I'm stumped in ActiveRecord.

Rails polymorphic aliased associations

I would like to try set up this association:
# app/models/course.rb
class Course < ActiveRecord::Base
belongs_to :subjectable, polymorphic: true
end
# app/models/student.rb
class Student < ActiveRecord::Base
has_many :courses, as: :subjectable
end
# app/models/campus.rb
class Campus < ActiveRecord::Base
has_many :courses, as: :subjectable
end
But this did not read very well in the code.
#this seems fine
campus = Campus.last
campus.courses
#this dosent make much sense gramatically
student = Student.last
student.courses
Campuses offer Courses, but Students don't have courses they have subjects. Now they are the same thing under the covers they just don't read well.
How could I get it so that student.subejects would yield the same result as student.courses?
You can name the association as you want, you don't have to mach the associated class.
In this case, you have to tell ActiveRecord what the pointed class is :
# app/models/student.rb
class Student < ActiveRecord::Base
has_many :subjects, as: :subjectable, class_name: 'Course'
end

How to get the association name in rails?

I have the following association code in my user.rb model file
class User < ActiveRecord::Base
has_many :sent_messages, class_name: 'ChatMessage', foreign_key: 'sender_id'
has_many :received_messages, class_name: 'ChatMessage', foreign_key: 'receiver_id'
end
I want a method in the ChatMessage model which should be triggered by the following
current_user.sent_messages
current_user.received_messages
The method should return the name of the association that was called.
Eg:
class ChatMessage < ActiveRecord::Base
after_find :get_association_name
def get_association_name
self.association_name // this should return sent_message or received_message depending on whether current_user.sent_messages or current_user.received_messages was called
end
end
Is there a way to get this association name in rails?
Any help is much appreciated. Thanks
I am not sure, exactly what you are looking for, but
CurrentUser.reflect_on_all_associations(:has_many)
will give an array of all has_many associations.
I haven't used AR associations extensions for cases like this, but you should be able to do:
has_many :sent_messages, class_name: 'ChatMessage', foreign_key: 'sender_id' do
def get_association_name; 'sent_messages'; end
# or, to make this more generic,
# def get_association_name; proxy_association.reflection.name.to_s; end
end
And the method should be accessible from your relation. If you were using Rails 4, you could extract the generic version out into a separate module to extend your associations more succinctly. See http://guides.rubyonrails.org/association_basics.html#association-extensions.
EDIT
Try:
has_many :sent_messages, class_name: 'ChatMessage', foreign_key: 'sender_id' do
def and_set_type
proxy_association.target.each do |msg|
msg.update_attribute(:type, 'sent')
end
scoped
end
end
And then access your sent_messages with current_user.sent_messages.and_set_type.

How to have an ordered model association allowing duplicates

I have two models, Song and Show. A Show is an ordered list of Songs, in which the same Song can be listed multiple times.
That is, there should be an ordered array (or hash or anything) somewhere in Show that can contain Song1, Song2, Song1, Song3 and allow re-ordering, inserting, or deleting from that array.
I cannot figure out how to model this with ActiveRecord associations. I'm guessing I need some sort of special join table with a column for the index, but apart from starting to code my SQL directly, is there a way to do this with Rails associations?
Some code as I have it now (but doesn't work properly):
class Song < ActiveRecord::Base
attr_accessible :title
has_and_belongs_to_many :shows
end
class Show < ActiveRecord::Base
attr_accessible :date
has_and_belongs_to_many :songs
end
song1 = Song.create(title: 'Foo')
song2 = Song.create(title: 'Bar')
show1 = Show.create(date: 'Tomorrow')
show1.songs << song1 << song2 << song1
puts "show1 size = #{show1.songs.size}" # 3
show1.delete_at(0) # Should delete the first instance of song1, but leave the second instance
puts "show1 size = #{show1.songs.size}" # 2
show1.reload
puts "show1 size = #{show1.songs.size}" # 3 again, annoyingly
Inserting might look like:
show1.songs # Foo, Bar, Foo
song3 = Song.create(title: 'Baz')
show1.insert(1, song3)
show1.songs # Foo, Baz, Bar, Foo
And reordering might (with a little magic) look something like:
show1.songs # Foo, Bar, Foo
show1.move_song_from(0, to: 1)
show1.songs # Bar, Foo, Foo
You're on the right track with the join table idea:
class Song < ActiveRecord::Base
attr_accessible :title
has_many :playlist_items
has_many :shows, :through => :playlist_items
end
class PlaylistItem < ActiveRecord::Base
belongs_to :shows #foreign_key show_id
belongs_to :songs #foreign_key song_id
end
class Show < ActiveRecord::Base
attr_accessible :date
has_many :playlist_items
has_many :songs, :through => :playlist_items
end
Then you can do stuff like user.playlist_items.create :song => Song.last
My current solution to this is a combination of has_many :through and acts_as_list. It was not the easiest thing to find information on combining the two correctly. One of the hurdles, for example, was that acts_as_list uses an index starting at 1, while the array-like methods created by the ActiveRecord association start at 0.
Here's how my code ended up. Note that I had to specify explicit methods to modify the join table (for most of them anyway); I'm not sure if there's a cleaner way to make those work.
class Song < ActiveRecord::Base
attr_accessible :title
has_many :playlist_items, :order => :position
has_many :shows, :through => :playlist_items
end
class PlaylistItem < ActiveRecord::Base
attr_accessible :position, :show_id, :song_id
belongs_to :shows
belongs_to :songs
acts_as_list :scope => :show
end
class Show < ActiveRecord::Base
attr_accessible :date
has_many :playlist_items, :order => :position
has_many :songs, :through => :playlist_items, :order => :position
def song_at(index)
self.songs.find_by_id(self.playlist_items[index].song_id)
end
def move_song(index, options={})
raise "A :to option is required." unless options.has_key? :to
self.playlist_items[index].insert_at(options[:to] + 1) # Compensate for acts_as_list starting at 1
end
def add_song(location)
self.songs << location
end
def remove_song_at(index)
self.playlist_items.delete(self.playlist_items[index])
end
end
I added a 'position' column to my 'playlist_items' table, as per the instructions that came with acts_as_list. It's worth noting that I had to dig into the API for acts_as_list to find the insert_at method.

Rails get related items through two different relationships

I have a "two middleman" model setup as shown below:
User
has_many :comments
has_many :ratings
Comment
belongs_to :user
belongs_to :movie
Rating
belongs_to :user
belongs_to :movie
Movie
has_many :comments
has_many :ratings
Whats the best way to get all Movies that a User is associated with (either commented on or rated)?
I'd like to be able to call User.get_movies(user_id) and get back an ActiveRecord::Relation object so that it's chainable (i.e. User.get_movies(user_id).limit(3).order(...)). This returns a regular old array, and I suspect I'm hitting the database way more than I need to be.
def self.get_movies(user_id)
user = self.where(:id => user_id).includes({:comments => :movie}, {:ratings => :movie})
movies = []
user.comments.each do |comment|
movies.push(comment.movie)
end
user.ratings.each do |rating|
movies.push(rating.movie)
end
movies.uniq!
end
def movies
Movie.includes(:ratings, :comments).where("`ratings`.user_id = ? OR `comments`.user_id = ?", self.id, self.id)
end
Untested, but I'm pretty sure using a joins instead of includes also works.

Resources