Compile data from different models into one list - ruby-on-rails

My app has a user model, as well as multiple other date related models/tables such as anniversaries, holidays, birthdays, and and "other dates" custom model.
I have a user dashboard view that lists them all separately as shown below. How can i display all of these lists as one (call it upcoming events or something) that is listed chronologically and shows them upcoming dates for a certain period of time.
View
*note - These are displayed in a table/list but i stripped html for clarity
<h1>Holidays</h1>
<% if #user.holidays.any? %>
<% #user.holidays.each do |hld| %>
<%= hld.name %>
<%= hld.date %>
<% end %>
<h1>Friends Birthdays</h1>
<% if #user.friends.any? %>
<% #user.friends.each do |frd| %>
<%= frd.name %>
<%= frd.dob %>
<% end %>
<h1> Anniversary </h1>
<% if #user.anniversaries.any? %>
<% #user.anniversaries.each do |ann| %>
<%= ann.spouse_name %>
<%= ann.anniversary_date %>
<% end %>
Thanks!
Models
class User < ActiveRecord::Base
has_many :friends
has_many :occasions
has_many :user_holidays
has_many :holidays, :through => :user_holidays
has_many :anniversaries
class Holiday < ActiveRecord::Base
has_many :user_holidays
has_many :users, :through => :user_holidays
end
class Friend < ActiveRecord::Base
belongs_to :user
end
class Anniversary < ActiveRecord::Base
belongs_to :user
end

Assuming you want to be efficient (you could just combine the arrays, sort them and be done with it), there is no direct way to do it through the relations. I am assuming you have an events model which has a foreign key to the user, in that case,
Events.where(:user_id => #user.id).where(<EVENT DATE FILTERS>).order("event_date DESC")
-- EDIT --
This is quite dirty, but I cant think of any other direct db way of accomplishing this.
events = #user.holidays.map{|h| [h.name, h.date, :holiday]} + \
#user.friends.map{|f| [f.name, f.dob, :birthday]} + \
#user.anniversaries.map{|a| [a.spouse_name, a.anniversary.date, :anniversary]}
events.map!{|event| {:name => event[0], :date => event[1], :event_type => event[2]}}
# You now have an array of hashes with the events name, date and type.
events.sort{|a, b| a[:date] <=> b[:date]} # sort / filter

Related

Querying and linking to from join tables

Two Questions:
1) I have a retreats/:id view where I can display the team names that are affixed to a specific retreat. I can view the team names with the following query in the view:
<p>Teams: <%= #retreat.teams.pluck(:name).to_sentence %></p>
However, instead of just displaying the name, how would I both display the name of the team and link to the team team/:id
2) In this retreats/:id view, I would also like to display the users that are part of a team, but I am really stuck trying to go through sql joins, etc.
models
retreat.rb
class Retreat < ApplicationRecord
belongs_to :user
delegate :name, to: :user, prefix: true
belongs_to :account
validates :name, presence: true
has_many :retreat_teams
has_many :teams, through: :retreat_teams
accepts_nested_attributes_for :retreat_teams
end
team.rb
class Team < ApplicationRecord
belongs_to :account
has_many :team_members
has_many :users, through: :team_members
accepts_nested_attributes_for :team_members
has_many :retreats
has_many :retreats, through: :retreat_teams
end
team_members.rb
class TeamMember < ApplicationRecord
belongs_to :team
belongs_to :user
end
First part can be done this way
<% retreat.teams.each do |team| %>
<%= link_to(team.name, team_path(team.id)) %> # or whatever the path helper is
<% end %>
Second part, you can run this query instead
#teams = Team.where(retreat_id: #retreat.id).includes(:users)
Then in UI you can show like this
<% #teams.each do |team| %>
Team: <%= link_to(team.name, team_path(team.id)) %> # or whatever the path helper is
Team Users: <%= team.users.pluck(:name).to_sentence %>
<% end %>
Hope that helps!
1) I have a retreats/:id view where I can display the team names that
are affixed to a specific retreat. ...However, instead of just
displaying the name, how would I both display the name of the team and
link to the team team/:id
Don't use .pluck unless you actually want just a single column as an array or the raw data from several columns without instantiating model instances. .pluck is both overused and misused. It makes zero sense to pluck something if you then need to fetch the rest of the columns later anyways.
Instead just iterate though the model instances:
<% #resort.teams.each do |team| %>
<%= link_to team.name, team %>
<% end %>
If you have declared the route with resources :teams. Rails will figure out the route all by itself - that's the power of convention over configuration.
2) In this retreats/:id view, I would also like to display the users
that are part of a team, but I am really stuck trying to go through
sql joins, etc.
You don't really have to do any work joining. Just eager_load the association to avoid n+1 queries:
def show
#resort = Resort.eager_load(teams: :users).find(params[:id])
end
<% #resort.teams.each do |team| %>
<div class="team">
<h3><%= link_to team.name, team %></h3>
<h4>Members:</h4>
<% if team.users.any? %>
<ul>
<%= team.users.each do |user| %>
<li><%= link_to user.name, user %></li>
<% end %>
<ul>
<% end %>
</div>
<% end %>
Another note about naming
The name TeamMember is unfortunate as it implies that its the actual person that's a member and not just a join model.
Membership or Position are better name choices.
class Team
has_many :memberships
has_many :members, through: :memberships
end
class Membership
belongs_to :team
belongs_to :member, class_name: 'User'
end
class User
has_many :memberships, foreign_key: :member_id
has_many :teams, through: :memberships
end
This will let you iterate through team.members and actually get the users instead of some join model. The above example would read after refactoring:
def show
#resort = Resort.eager_load(teams: :members).find(params[:id])
end
<% #resort.teams.each do |team| %>
<div class="team">
<h3><%= link_to team.name, team %></h3>
<h4>Members:</h4>
<% if team.members.any? %>
<ul>
<%= team.members.each do |member| %>
<li><%= link_to member.name, member %></li>
<% end %>
<ul>
<% end %>
</div>
<% end %>

Sum 2 columns a value with array

Hello everybody.
I'm trying to sum 2 columns(amount_ensure and vehicle_Value) from different tables in my view
TABLES
|POLICIES|
|id| |amount_ensure|
|POLICY_VEHICLES|
|id| |policy_id|
|VEHICLES|
|Id| |vehicle_value|
Here is my controller
def view_policy
#obj_policy = Policy.find(params[:id])
end
Here is my model
class Policy < ActiveRecord::Base
has_many :policy_vehicles
has_many :vehicles, :through => :policy_vehicles
end
class PolicyVehicle < ActiveRecord::Base
belongs_to :vehicle
belongs_to :policy
end
class Vehicle < ActiveRecord::Base
belongs_to :policy
has_many :policy_vehicles
has_many :policies, :through => :policy_vehicles
end
Here is my view when in my partial view #obj_policy.vehicle is empty show only amount_ensure but when has value do SUM (but is an array from my partial view)
<% if #obj_policy.vehicles.empty? %>
Sum:
<%= #obj_policy.amount_ensure %>
<% else %>
Sum:
<%= #obj_policy.amount_ensure + #obj_policy.vehicles.vehicle_value.to_i %>
<% end %>
<%= render :partial=>"vehicles" %>
My partial view
<% #obj_policy.vehicles.each do |vehicle| %>
<%= vehicle.vehicle_value %>
<% end %>
How can i fix this problem?
I will appreciate help.
This should work
<% else %>
Sum:
<%= #obj_policy.amount_ensure + #obj_policy.vehicles.collect(&:vehicle_value).sum %>
<% end %>
#obj_policy.vehicles.collect(&:vehicle_value).sum will be 0 when vehicles array is empty
Actually it will be better to allow SQL to handle it.
<%= #obj_policy.amount_ensure + #obj_policy.vehicles.sum(:vehicle_value) %>
You can also wrap it into a instance method:
class Policy
def total_value
amount_ensure + vehicles.sum(:vehicle_value) # I don't remember if `to_i` is necessary here in case of empty collection, try it out
end
end
It's also possible to write a custom SQL query that will do this summing in database. In case of original solution proposed it fetches ALL vehicles with ALL fields that might not be necessary at all. Try to use SQL as much as possible for aggregation jobs.

Represent associated model with simple_form_for

I have the following models where UserTreatment is essentially a lookup table but it has another field called instance_cost which stores an integer against each relationship.
class User < ActiveRecord::Base
has_many :user_treatments
has_many :users, :through => :user_treatments
end
class UserTreatment < ActiveRecord::Base
attr_accessible :instance_cost
belongs_to :user
belongs_to :treatment
end
class Treatment < ActiveRecord::Base
attr_accessible :name
has_many :user_treatments
has_many :users, :through => :user_treatments
end
So I can do things like this to get the first instance cost for user id 14
1.9.3p429 :181 > User.find(14).user_treatments.first.instance_cost
=> 100
and this to get the name of the treatment
1.9.3p429 :181 > User.find(14).user_treatments.first.treatment.name
=> "Sports Massage"
However I have a problem representing them in a form using simple_form_for
<% simple_form_for #user do |f| %>
# This sucessfully gives us the checkboxes of all treatments and stores them in the UserTreatments table
<%= f.collection_check_boxes(:treatment_ids, Treatment.all, :id, :name) %>
<%= f.fields_for :user_treatments do |pt| %>
<tr>
<td>
<!-- I WANT THE NAME OF THE TREATMENT HERE -->
</td>
<td><span>£</span> <%= pt.input :instance_cost, :as => :string, wrapper: false, label: false %></td>
</tr>
<% end %>
end
There are two things I need to do.
Show the name of the treatment (how do I do express User.user_treatments.treatment.name with simple_form ?)
Set the instance_cost correctly. Its currently not getting set at all.
When you do fields_for for a nested association then you need to add the following in your User model:
accepts_nested_attributes_for :user_treatments
Otherwise, it will not save the information.
And to access the name of the treatment, you could go through the object method of the form creator object, like this:
<%= f.fields_for :user_treatments do |pt| %>
<tr>
<td><%= pt.object.treatment.name %></td>
...
</tr>
<% end %>

How do I save many-to-many relationships in a model

I have the following setup in my rails app:
A user registers and he chooses from a set of check boxes for Music Styles.
The Music Styles are only 4 right now but should be extendable. I'd like to have a list of MusicStyles that I can extend and change easily.
My approach would be to create a model 'MusicStyles' and a model 'UserMusicStyles' and then use a has_many_through association similar to:
class User < ActiveRecord::Base
has_many :user_music_styles
has_many :music_styles, :through => :user_music_styles
end
class UserMusicStyle < ActiveRecord::Base
belongs_to :user
belongs_to :music_style
end
class MusicStyle < ActiveRecord::Base
has_many :music_styles
has_many :users, :through => :user_music_styles
end
Now, during registration I would do something like MusicStyle.all.each do |m| ... to display the checkboxes but how do I save it to the database correctly in the user controller?
Any help much appreciated!
You can do it like this:
<%= form_for #user do |f| %>
<!-- User stuff -->
...
<% MusicStyle.all.each do |m| %>
<%= check_box_tag('user[music_style_ids][]', m.id, #user.music_styles.include?(m)) %>
<% end %>
<%= f.submit 'Save' %>
<% end %>

Searchlogic and :has_many, :through =>

I'm using Searchlogic to search on many fields in a database. One of those fields is a :has_may, :through => relationship, and I can't get it to work.
Here are the relevant parts of the models:
Source.rb:
class Source < ActiveRecord::Base
has_many :authorships
has_many :authors, :through => :authorships
end
Authorship.rb:
class Authorship < ActiveRecord::Base
belongs_to :source
belongs_to :author
end
Author.rb:
class Author < ActiveRecord::Base
has_many :authorships
has_many :sources, :through => :authorships
end
Then, in my view, I have:
<% form_for #search do |f| %>
<fieldset><legend>Search the Online Medieval Sources Bibliography</legend>
<% f.fields_for #search.conditions do |sources| %>
<%= sources.hidden_field :live_not_null %>
<p>
Text Name:
<%= sources.text_field :text_name_like, :size => 95 %> <br />
<% sources.fields_for :author do |authors| %>
Medieval Author:
<%= authors.text_field :name_like, :size => 90 %>
<% end %> <br />
Modern Editor/Translator:
<%= sources.text_field :editor_like, :size => 80 %> <br />
</p>
<% end %>
</fieldset>
<p>
<%= f.submit "Search" %>
</p>
<% end %>
The search page loads just fine, but hitting the "submit" button gives me the following error:
Searchlogic::Search::UnknownConditionError in SourcesController#index
The author is not a valid condition. You may only use conditions that map to a named scope
Here is the code from SourcesController:
class SourcesController < ApplicationController
def index
query = if params[:search] then
params[:search][:hash]
end
#search = Source.search(query)
#sources = #search.all
end
And here are the parameters:
Parameters: {"commit"=>"Search", "search"=>{"hash"=>{"text_name_like"=>"canterbury", "date_begin_greater_than_or_equal"=>"", "author"=>{"name_like"=>""}, "editor_like"=>"", "link_not_blank"=>"0", "trans_none_not_null"=>"0", "trans_other_not_null"=>"0", "trans_english_not_null"=>"0", "trans_french_not_null"=>"0", "region_like"=>"", "live_not_null"=>"", "app_facsimile_not_null"=>"0", "date_end_less_than_or_equal"=>""}, "order"=>""}}
Does anyone have any ideas about what is happening here? Do you need to see more of the error message?
Many thanks!
I too have come across this limitation on searchlogic. there is a 'hack' of a solution that i got. its simple really.
search for items you looking for on the join model. this will give you the ids of the models that have that search.
this is a bit quirky but its the closest solution i could offer you.
hope this helps.
:)

Resources