Split the select of the same association on different fields - ruby-on-rails

I'm in the middle of developing a bike website when I stumbled on question.
I have the follow models (to simplify my description I left some models and attributes aside):
model_name:some attributes (example)
bike: name, price, color (Giant Anthem, 3999, black)
category: name (Brakes or Shifts or Transmissions, etc)
component: name (Shimano SLX or Shimano XT or Shimano XTR - in this case for components for the transmission category)
So the relationship of these models are the followed:
A bike has many components
A component is on many bikes
A component belongs to a category
A category has many components
Right now I've created these 3 tables and made the association: category has_many components and component belong_to category. Everything works well. Now I have to create a jointable and use the has many through association between bike and component BUT the problem for me is to understand the next step after that because I don't want only one dropdown to select all components. I want to select the components divided by category. Something like this:
How can I achieve this? Hope you can help me. Thank you!

The tricky part is that you want to split the select of the same association on different fields. You may need to handle the name of the field manually, something like this:
= select_tag 'bike[component_ids][]', options_from_collection_for_select(frames_category.components.all, :id, :name)
= select_tag 'bike[component_ids][]', options_from_collection_for_select(breakes_category.components.all, :id, :name)
= select_tag 'bike[component_ids][]', options_from_collection_for_select(transmissions_category.components.all, :id, :name)
= select_tag 'bike[component_ids][]', options_from_collection_for_select(tyres_category.components.all, :id, :name)
Note the names of the attribute `bike[components_ids][]'. The params hash on your controller will look something like:
params == {bike: {name: 'some name', price: '200', component_ids: [1,3,6,8]}
ActiveRecord should handle the assignment of the components using the collection_singular_ids= https://guides.rubyonrails.org/association_basics.html#methods-added-by-has-many-collection-singular-ids-ids
I'm not 100% sure it works on has many through but personally I wouldn't use a has many through there, "has and belong to many" seems to just fit your needs and it requires less configuration.

Related

How to create dropdown list of child model records grouped by the parent

I have three models, Properties, Cities and Regions. A city belongs_to a region and region has_many cities. The user picks a city when defining a new property.
There are many cities so I'd like to have the dropdown list look something like this:
Region 1:
City 1
City 2
City 3
Region 2:
City 4
City 5
etc. But only the cities are selectable, ie the regions are greyed out and are only there as a visual guide. Is there a way to group the child records by parent?
You want to use the rails "option_groups_from_collection_for_select" helper
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-option_groups_from_collection_for_select
= option_groups_from_collection_for_select(#regions, :cities, :name, :id, :name, 3)
if you want to manually provide the 2 level array of options, then use "grouped_options_for_select"
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-grouped_options_for_select

testing queries using rspec and factory_girl : Is there a better way?

I have the following classes and relationships
City has_many Cinemas
Cinemas has_many Movies
Movies has_many Ratings
Movies Has_many Genres through GenreMovie
and I want to test queries like
* Show me the all movies in NewYork
* Show me the all movies in NewYork order by the rating
* Show me the all movies in NewYork order by length_of_movie, in genre "Action"
* show me all movies in Cinema "X" order by rating, that are in Genre "SciFi"
Currently the way I am doing as below, using factory girl, and chaining a bunch of models together to have data to check against,
city = create(:city)
cinema = create(:cinema, city: city)
5.times do
movie = create(:movie, cinema: cinema, tags: ["sci fi", "action"]
3.times do
create(:rating, score: 2.3, movie: movie)
end
end
and repeating that 3-4 to generate enough data to query against but it seems so clunky.
Is there a better way ?
I normally test this using a very "minimalistic" approach:
e.g. for your first case I would create two movies, one in NY, and one outside. Your method should only return the one in NY
For the second, create three movies, both in NY, with different rating. Create them in a not logic way, so that, no matter what, they will be sorted. Check whether your method returns them in the right order
Similar for the other cases.
I would not just create 5x3 movies. Makes no sense, and only costs time...
There are several factory_girl constructs you could use to clean these up. create_list will create an array of objects, cleaning up your x.times do blocks. A with_ratings trait on your movie factory could allow you to opt in to having ratings automatically created when you create a movie (via FactoryGirl callbacks). You could even have that use a transient attribute in order to control the number and rating. So your result could look something like this:
cinema = create(:cinema)
movies = create_list(
:movie,
5,
:with_ratings,
cinema: cinema,
tags: [...],
ratings_count: 3,
ratings_value: 2.3
)
If you need access to the city, you can get it via cinema.city.
See:
transient attributes
traits

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

See if one person is before another in the alphabet, ruby, rails

I'm doing an app for a membership database.
Each person may have a partner. When it comes to displaying the list, I only want to have one row for each family, so at the moment I'm comparing first names and not displaying the row if the person's name is second. Like this
person.first_name != [person.first_name, person.partner.first_name].sort[0]
This means each family only gets displayed once, not twice - once for each partner.
And I'm doing this in the view.
There must be a better way of doing this, and it'd be really great if I could do it at the database level. I'm using postgresql if that makes a difference.
Edit
Sorry if it was unclear.
Say Person 1 has the first_name "Edward" and Person 2 has the first_name "Fay". Edward and Fay are married.
I only want to show them once in my list - I want a row to look like this
Surname First name Address etc
Mysurname Edward ....
Fay
I don't want to display it again with Fay first because I've got both Fay and Edward in list of people, so I use the ruby in the first part of the question to check if I should display the row - it compares their first names and only does the row if the person has a fist name that's before his/her partner's first name.
Here's the relevant part of my person model
class Person < ActiveRecord::Base
has_one :relationship_link, :foreign_key => :person_id, :dependent => :destroy, :include => :partner
has_one :partner, :through => :relationship_link, :source => :person_b, :class_name => "Person"
I hope that's clearer
You need to use DISTINCT ON or GROUP BY. In postgres you need to be careful to group by everything that you are selecting. If you only need to get the last names you can select("DISTINCT ON(last_name) last_name").pluck("last_name"). You will only get an array of last names though.
Maybe you can get records if you order by every other fields in your table, like this:
select("DISTINCT ON(people.last_name) people.*").order("people.last_name ASC, people.first_name ASC, people.field2 DESC, people.field3 ASC...")
You need to order by every attribute so the result is not ambigious.
For this case, i would create a data structure (a Hash) to store people instances given a specific surname. Something like this:
def build_surnames_hash(people_array)
surnames_hash = {}
people_array.each do |person|
last_name = person.last_name
surnames_hash[last_name] ||= []
surnames_hash[last_name] << person
end
surnames_hash
end
That way, you can iterate over the hash and display people using their surnames stored as hash's keys:
surnames_hash = build_surnames_hash(Person.all)
surnames_hash.each do |surname, person_instances_array|
# display the surname once
# iterate over person_instances_array displaying their properties
end

How to join multiple tables with one to one relationships in rails

Ruby on Rails is very new to me. I am trying to retrieve set of columns from 3 different tables. I thought I could use SQL view to retrieve my results but could not find a way to use views in Rails. Here are my tables.
1) User table --> user name, password and email
2) UserDetails table --> foreign key: user_id, name, address1, city etc.
3) UserWorkDetails --> foreign key: user_id, work address1, work type, etc
These 3 tables have one to one relationships. So table 2 belongs to table 1 and table 3 also belongs to table 1. Table 1 has one userdetails and one userworkdetails.
I want to get user email, name, address1, city, work address1, work type using joins.
What is the best way to handle this?
The data is (are) in the models. Everything else is just an optimization. So address1 is at user.user_detail.address1, for instance.
if you have
class User
has_one :user_detail
has_one :user_work_detail
end
class UserDetail
belongs_to :user
end
class UserWorkDetail
belongs_to :user
end
With user_id columns in tables named user_details and user_work_details then everything else is done for you.
If you later need to optimize you can :include the owned models, but it's not necessary for everything to work.
To get what you want done quickly use the :include option to include both the other tables when you query the primary table, so:
some_user_details = User.find(some_id, :include => [:user_details, :user_work_details])
This will just load all of the fields from the tables at once so there's only one query executed, then you can do what you need with the objects as they will contain all of the user data.
I find that this is simple enough and sufficient, and with this you're not optimising too early before you know where the bottlenecks are.
However if you really want to just load the required fields use the :select option as well on the ActiveRecord::Base find method:
some_user_details = User.find(some_id, :include => [:user_details, :user_work_details], :select => "id, email, name, address1, city, work_address1")
Although my SQL is a bit rusty at the moment.
User.find(:first, :joins => [:user_work_details, :user_details], :conditions => {:id => id})
I'd say that trying to just select the fields you want is a premature optimization at this point, but you could do it in a complicated :select hash.
:include will do 3 selects, 1 on user, one on user_details, and one on user_work_details. It's great for selecting a collection of objects from multiple tables in minimum queries.
http://apidock.com/rails/ActiveRecord/Base/find/class

Resources