What is the best way to structure this model/DB assiociations - ruby-on-rails

I am creating a project, and I want to structure the databases in the best way possible. I have a model called Event, now i would like a single event (think a baseball game) to belong to a month (i.e August) and belong to a category (i.e baseball) AND belong to a user (i.e current_user)
In otherwords, I would like a month, a category, and a user to have many events, but I don't want the events to be duplicated.
The same event object would be returned by:
august.events.find_by_id(1)
baseball.events.find_by_id(1)
current_user.events.find_by_id(1)
Any tips? My initial thought is to just set up the relations as I described above.. But I am curious to what is the best approach to creating an event object since:
august.events.new("foo")
baseball.events.new("foo")
current_user.events.new("foo")
would all yield three different event objects, when I am looking to just create one.
Can I do something like this?
Event.new(:category => "baseball", :month => "august")
then what would be the best approach to declaring a user "has that" event, and from there run commands such as baseball.events.all and august.events.all. And how would I dive deeper into the association madness if I wanted say Category -> Sports -> Baseball -> event
Thanks for any help, it is much appreciated.

this isn't an answer, per se. It is mainly to give you an idea of the flexibility of the API.
The schema, from what I can tell, is correct. Event has month and category (or category_id) and user_id. You can create all manner of helper methods to operate on that model.
For example, with a few scopes:
class Event
scope :for_month, ->(m) { where(month: m) }
scope :for_category, ->(c) { where(category: c) }
scope :for_user, ->(u) { where(user: u) }
end
# usage
Event.for_month("august").for_category("baseball").for_user(current_user)
# below, month "august" will be set on instance
current_user.events.for_month("august").new("foo")
But all of this is pointless. With those relationships in place, you can access the Event from User or Category and fill in the details yourself:
Event.where(month: "august", category: "baseball", user: current_user)
current_user.events.where(month: "august", category: "baseball")
current_user.events.new(month: "august", category: "baseball")
There are a tonne of ways to access and manipulate this data. I'm not sure you can reduce it much further than the above. You could go crazy and add a scope with multiple arguments:
class Event
scope :for_mcu, ->(m, c, u) {
for_month(m).for_category(c).for_user(u)
}
end
# usage
Event.for_mcu("august", "baseball", current_user)

Related

Rails: How to use scope to find an element in array of arrays

I have an array of arrays like [["2","3"], ["3","1"], ["6", "1"]]. The first element of each sub-array is the user ID and the second one is the number of seats the user reserved for an event.
I want to allow each user to view his reservations by finding his ID in the array. Suppose that I have two models: User and Event. In the User controller, I want to use a scope like #mybooking = Event.mybooking(current_user.id) and the problem is how to write the proper scope in the Event model?
And, if the user is found, I want to use its second element, too.
I tried different solutions but didn't work! Let me know if you think it's not possible using the scope and if you have another kind of solution.
Edit:
As I'm still waiting for a solution that works, I should mention that I'm looking for something like this:
scope :mybookings, ->(id){where("reservations.to_enum.map{|n,m| n} ?", id)}
or
scope :mybookings, ->(id) { where("reservations.map(&:first) ?", id) }
These two don't work because of the error I get related to "...." part.
And, below solution isn't true because I'm calling the Event's scope from User controller and it's not possible to use reservations in that controller because this variable is for the Event controller.
class Event
scope :mybooking, ->(user_ids) { where(user_id: user_ids) }
end
Now it is possible to do in controller:
reservations = [["2","3"], ["3","1"], ["6", "1"]]
Event.mybooking(reservations.map(&:first))

Rails 3 complex query with sort

I have a User model and an Event model. User has_many events. What I want to be able to do is find the User that has the most events created in the last 24 hours.
Ideally, the output format would have be an array of hashes [{User => [Event, Event, ...]}], which would be sorted by User's with the highest events.count
Thanks
This works for me, but might require tweaking depending on your database.
class User
scope :by_most_events, -> {
joins(:events)
.select("users.*, count(events.id) as event_count")
.where(events: { created_at: 24.hours.ago..Time.now })
.group("users.id, events.id") # this is for postgres (required group for aggregate)
.order("event_count desc")
}
end
### usage
User.by_most_events.limit(1).first.event_count
# => 123
As Sixty4Bit mentioned, you should use counter_cache, but it will not suffice in this situation because you need to query for events created in the last 24 hours.

Comparing two lists and syncronizing the database based on differences

I have the following resource relationship:
Class Course < ActiveRecord::Base
has_many :track_courses
has_many :tracks, through: :track_courses
end
as well as a mirroring relationship inside the Track model. The TrackCourse table which connects these models has these rows:
id: primary key
track_id: represents the track
course_id: represents the course
position: the ordering of the course inside that track
I want to allow admin users to be able to update the courses in each track via ajax. I have a list on the front-end that is being passed to the controller as a hash:
front_end_list = { course_id => position }
which represents the object and its position on the front-end sortable.
I'm also looking up the list of existing courses in that track:
existing_courses = TrackCourse.where("track_id = ?", track_id).all
GOAL: Compare these two lists and syncronize the database entries according to the front-end list. Essentially, if the user inserts Course 15 into position 2 on the webpage, I need to either insert that entry into TrackCourse table (if it doesn't exist) or update its position (if it exists). And vice versa for remove.
What is the best way of doing this? Do ActiveRecord/ActiveRelation provide methods for it? Or do I have to write something myself?
UPDATE: I found a gem called acts_as_list, but it seems to be designed for ActiveRecord tables as opposed to ActiveRelation. It essentially expects position values to be unique, whereas in TrackCourse there can be multiple course with same position (in different tracks).
I figured out a solution. I'll post my code here in case it helps anyone else down the line.
I have this method in my controller that processes the ajax request from the front-end:
def sort
track_id = params[:track_id]
courses_in_list = {}
params[:course].each do |courseid|
position = params[:course].index(courseid)
courses_in_list[courseid.to_i] = position
end
existing_courses_in_track = {}
TrackCourse.where("track_id = ?", track_id).to_a.each do |track_course|
existing_courses_in_track[track_course.course_id] = track_course.position
end
if courses_in_list.length < existing_courses_in_track.length
existing_courses_in_track.each do |courseid, position|
if courses_in_list[courseid].nil?
track_course = TrackCourse.where(track_id: track_id, course_id: courseid).first
track_course.remove_from_list
track_course.destroy!
end
end
else
if existing_courses_in_track.empty?
track_course = TrackCourse.new(track_id: track_id,
course_id: courses_in_list.keys[0])
track_course.insert_at(courses_in_list.values[0])
p "first track!"
else
courses_in_list.each do |courseid, position|
track_exists = false
if !existing_courses_in_track[courseid].nil?
track_course_position = existing_courses_in_track[courseid]
track_exists = true
end
if !track_exists
TrackCourse.new(track_id: track_id, course_id: courseid).insert_at(position)
else
p "else statement"
track_course = TrackCourse.where(track_id: track_id, course_id: courseid).first
track_course.update_attribute(:position, position)
end
end
end
end
render :nothing => true
end
Essentially, I'm building two hashes, one based on the list of front-end items and their position, and one based on the database courses and their position. I then compare them. If the front-end list is shorter, that means the user removed an item, so I iterate through the backend list, find the extra item, and remove it. Then I employ a similar mechanism for adding items to the list and resorting the list. The acts_as_list gem really helps with keeping things in the correct position. However, I did have to limit its scope when I included it in my model to ensure it runs only on relationships (TrackCourses) with a specific track_id.

Rails 4: Displaying a table of a collection of items, sorted by various item attributes?

So I have a CareerEntry model that has the following attributes: name, job_category, company, group, location, year, full_intern, and it represents the job offers that people have received. full_intern is a string that is either "internship" or "full-time", and represents what the type of the job offer is. All CareerEntries will be created by an Admin interface, so it is essentially acting as a standalone model. This is my question: given a bunch of CareerEntry objects, I want to display a table to display on my careers page (which has an action in a PagesController).
I want the table to be sorted according to multiple attributes. I want each year to be its own section in the table, then within each year, I want the internship entries grouped together and the full-time entries grouped together. Then, within these groupings, I want each job_category to be its own section (job_categories comprise of things like 'Investment Banking,' or 'Technology.')
A very good example of what I'm going for is shown under the "2013" tab in this link.
What is the best way to go about achieving this? I know that in the careers action definition of my PagesController, I could have:
class PagesController < ApplicationController
def careers
#careerentries = CareerEntry.order(:year => :desc, :fullintern => :asc, :job_category => :asc)
end
end
But this would simply return all the entries in the order that I want, and would not allow me to place headers and dividers to separate, say, the job_categories.
Is there any easier way of achieving what I'm looking for?
Perhaps you're looking for .group_by?
Group By
From the link you gave, it looks like you want to group your results by year, like this:
#careerentries = CareerEntry.order(year: :desc, fullintern: :asc, job_category: :asc)
#entries_by_year = #careerentries.group_by { |entry| entry.year }
This gives you all the data, ordered to your specs. You can then sort through it, using the group_by method:
#entries_by_year.each do |entry|
entry.name
end
You could then work this into your table
Good reference Group posts by Year - Rails

How can I write this query efficiently in Ruby on Rails?

I have three tables: Accounts, Investments, and Games. An Investment has an account_id, game_id, some statistic counters, and is created the first time an Account participates in a Game.
I want to provide a JSON list of the latest Games along with the user's Investment in that Game, like this:
[{id: 666, name: "Foobar", ..., investment: {tokens: 58, credits: 42, ...}},...]
If they have not yet participated in the game, I still want to include an Investment object with default values, so I overrode the serializable_hash function in my Game model:
# game.rb
has_many :investments
def serializable_hash(options=nil)
options ||= {}
i = investments.find_or_initialize_by_account_id options[:uid]
{:id => id, ..., :investment => i.serializable_hash}
end
However, when I run something like Game.find(list_of_ids).to_json(:uid => current_user.id), Rails does a separate query on the Investments table for each Game. I tried Game.includes(:investments).find(list_of_ids).to_json(:uid => current_user.id) but not only does that load the investments for all users, it still does a separate query for each game to find or initialize the investment object.
In short, given a list of game IDs and an account id, what's a clean way to load the associated Investment objects that exist in one query, and initialize the rest?
You want to give the list of ids to the server in one go. I use the IN operator for this:
Game.includes(:investments).where("games.id IN (?)", list_of_ids)

Resources