How to reference attributes in deep associations - ruby-on-rails

Summary
Rails 3.2 with RefineryCMS 2.0. These are my pseudocode models:
Industry
name
has_many companies
has_many works through companies
Company
name
has_many works
belongs_to industry
Work
name
belongs to company
From an instance of Work, I can say work.company.name and get a the name of the associated company. I would expect it to follow that I could also say company.industry.name without a problem. However, I am getting an unhelpful error:
wrong constant name Refinery:Industries
What I would ultimately like to do is follow my associations all the way up ie work.company.industry.name, but the chain is broken between company and industry it seems.
What am I doing wrong here? Here's my code in more detail.
Code
Here are my models. Any idea what would prevent me from accessing industry attributes from an associated company given that industries have_many companies (companys lol) and companies belong_to an industry? Any help would be much appreciated.
Industry Model
module Refinery
module Industries
class Industry < Refinery::Core::BaseModel
...
attr_accessible :name, :description, :position
has_many :companys, :class_name => '::Refinery::Companys::Company', :dependent => :nullify
has_many :works, :through => :companys
end
end
end
Company Model
module Refinery
module Companys
class Company < Refinery::Core::BaseModel
...
attr_accessible :name, :position, :industry_id
has_many :works, :class_name => '::Refinery::Works::Work', :dependent => :destroy
belongs_to :industry, :class_name => '::Refinery:Industries::Industry'
end
end
end
Work Model
module Refinery
module Works
class Work < Refinery::Core::BaseModel
...
attr_accessible :name, :description, :position, :company_id
belongs_to :thumbnail, :class_name => '::Refinery::Image'
belongs_to :Company, :class_name => '::Refinery::companys::company'
end
end
end
Then in my erb file I'm doing this:
<% #works.each do |work| %>
...
<h5>
<%= work.company.name %>
</h5>
<% end %>
That one works.
This one gives me an error though:
<% #clients.each do |client| %>
<h5>
<%= client.industry.name %>
</h5>
<% end %>
That error reads:
wrong constant name Refinery:Industries

There is at least a double colon missing in your Company model:
belongs_to :industry, :class_name => '::Refinery:Industries::Industry'
should be
belongs_to :industry, :class_name => '::Refinery::Industries::Industry'
I haven't really looked at the rest of the code, but this is a first error.

Related

Rails 5.1, search scope within a group

I have a little issue with a search scope.
Users can join groups by providing a token. User can also create spendings and they can share them in a group. We are in the show view of a group. This is where I loop through spendings that each user has in this group. It looks something like that :
<% group_spendings_paginate_search.each do |groupspending| %>
<%= groupspending.member.user.firstname %>
<%= groupspending.spending.title %>
<%= groupspending.spending.description %>
<%= '%.02f' % groupspending.spending.amount %>
<%= groupspending.spending.currency.symb %>
<%= groupspending.created_at.strftime("%d/%m/%Y") %>
<% end %>
The group_spendings_paginate_search comes from a helper which represent this :
def group_spendings_paginate_search
#search.scope.order('created_at DESC').paginate(:page => params[:page], :per_page => 10)
end
The search form looks like this and is just above the loop :
<%= form_tag group_path(#group), method: :get do %>
<%= date_field_tag "search[date_from]", #search.date_from %>
<%= date_field_tag "search[date_to]", #search.date_to %>
<%= select_tag "search[user_id]", options_from_collection_for_select(#group.users.all, :id, :firstname, params[:user_id]), include_blank: "All Users" %>
<%= select_tag "search[currency_id]", options_from_collection_for_select(Currency.all, :id, :name, params[:currency_id]), include_blank: "All Currencies" %>
<%= submit_tag "Search", name: nil %>
<% end %>
Note that here I had to go for Currency.all which is not the currencies the user are using. They may only use euro for example and here the all list is going to pop, which is also not very cool. Would prefer to show only the currencies the users are using according to the group and spending. But hey it's already complicated enough so for now I'll keep it simple and come back on that later. But if anybody has a idea, feel free.
My controller :
def show
#search = GroupspendingSearch.new(params[:search])
end
And finally my GroupspendingSearch.rb
class GroupspendingSearch
attr_reader :date_from, :date_to, :user_id, :currency_id
def initialize(params)
params ||= {}
#date_from = parsed_date(params[:date_from], 1.month.ago.to_date)
#date_to = parsed_date(params[:date_to], Date.tomorrow)
#user_id = params[:user_id]
#currency_id = params[:currency_id]
end
def scope
launch = Groupspending.where('groupspendings.created_at BETWEEN ? AND ?', #date_from, #date_to)
launch = launch.where(member_id: find_member) if find_member.exists?
launch = launch.where(spending_id: find_currency) if find_currency.exists?
launch
end
private
def parsed_date(date_string, default)
Date.parse(date_string)
rescue ArgumentError, TypeError
default
end
def find_member
Member.where(user_id: #user_id)
end
def find_currency
Spending.where(currency_id: #currency_id)
end
end
The scope as is is actually working. Only problem is that it goes for all the Groupspending and not #group.groupspendings. It means that if I go in any group I will see all the spendings and I want to avoid that of course. Also I had to be smart about how I find the user_id and the currency_id. Using the table Groupspending gives me only member_id and spending_id. With the group_id or token in this model everything would be way easier...
Basically I don't know how to specify in the scope to look for the #group if this makes any sense.
I thought about a couple of things. The first is in the controller and specify a other param like so #search = GroupspendingSearch.new(params[:search],#group) and add it in the model def initialize(params, group) but to be honnest I don't really know what I'm doing so yea...
I'm dry here, anybody to help ? Maybe I'm totally wrong here and there is a other approach.
Models relations (don't pay attention to link and notification, it's a other story ^^) :
class Currency < ApplicationRecord
has_many :spendings, dependent: :destroy
has_many :users, through: :spendings
end
class Group < ApplicationRecord
has_secure_token :auth_token
has_many :members, :dependent => :destroy
has_many :users, through: :members, source: :user
belongs_to :owner, class_name: "User"
has_many :links, through: :grouplinks
has_many :grouplinks, through: :members
has_many :spendings, through: :groupspendings
has_many :groupspendings, through: :members
has_many :notifications, dependent: :destroy
def to_param
auth_token
end
end
class Groupspending < ApplicationRecord
belongs_to :spending
belongs_to :member
end
class Member < ApplicationRecord
belongs_to :user
belongs_to :group
has_many :grouplinks, dependent: :destroy
has_many :groupspendings, dependent: :destroy
validates :user_id, :presence => true
validates :group_id, :presence => true
validates :user_id, :uniqueness => {:scope => [:user_id, :group_id]}
end
class Spending < ApplicationRecord
belongs_to :user
belongs_to :currency
has_many :groupspendings, dependent: :destroy
end
class User < ApplicationRecord
has_secure_password
has_many :spendings, dependent: :destroy
has_many :currencies, through: :spendings
has_many :links, dependent: :destroy
has_many :notifications, dependent: :destroy
has_many :members, :dependent => :destroy
has_many :groups, :through => :members
has_one :owned_group, foreign_key: "owner_id", class_name: "Group"
end
EDIT :
Well I just realized that actually the scope isn't correct... I look for member_id: find_member and this goes for all the member with a member_id: X. Which means that if a user is a member of many groups well... it shows multiple entries. I'm getting crazy ^^
Ok I think I got it but I want to be sure that there is no mistake or security issues...
Here is what I changed :
Helper
def group_spendings_paginate_search
m = Member.where(group_id: #group.id)
gs = Groupspending.where(member_id: m)
#search.scope.where(member_id: gs.all.map(&:member_id) ).order('created_at DESC').paginate(:page => params[:page], :per_page => 10)
end
I didn't change the view and didn't change the GroupspendingSearch either. With this new helper I retrieve only the members within the group which "blocks" the search query to that. I guess...
I don't know if it's the best, and I don't know if it's safe. In the URL if I try to change the user_id to someone else, if he/she is not part of the group it doesn't show up. So it looks ok. Of course I have to secure the show view by restricting only to the members of the group. But appart from that, would that be ok ?

Traversing Through Multiple Associations (Rails)

I am a little stuck on this one. I would appreciate input.
Overview
I am trying to get the output I require in this friendship model i.e. the files the current user's friends have uploaded.
Models
Like all other friendship models, User is self-referencing as :friend.
class Share < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User"
end
This is my Paperclip model:
class Upload < ActiveRecord::Base
belongs_to :user
has_attached_file :document
end
This one is generated through Devise:
class User < ActiveRecord::Base
attr_accessor :login
has_attached_file :image, :styles => { :medium => "120x120!" }
has_many :uploads
has_many :shares
has_many :friends, :through => :shares
has_many :inverse_shares, :class_name => "Share", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_shares, :source => :user
end
Attempts
The furthest I have gotten into would be the level in which I am able to output the user's friends' :username(s), where #check = current_user in the controller:
<% #check.shares.each |j| %>
<%= j.friend.username %>
<% end %>
Question
How do I output the :file_name, :file_type, and :document of each of the current user's friends? I intend to have my page look like this:
User's Friend 1 Files
File Name
File Type
Document
User's Friend 2 Files
File Name
File Type
Document
Thank you.
Per the Paperclip documentation :
Paperclip will wrap up to four attributes (all prefixed with that
attachment's name, so you can have multiple attachments per model if
you wish) and give them a friendly front end. These attributes are:
<attachment>_file_name
<attachment>_file_size
<attachment>_content_type
<attachment>_updated_at
Given that, and with your attachment name of "document" :
<% current_user.shares.each do |share|%>
<%= share.friend.username %>
<% share.friend.uploads.each do |upload| %>
<%= upload.document_file_name %>
<%= upload.document_content_type %>
<% end %>
<% end %>

Rails: How to add a sum attribute from polymorphically associated model to retrieved records

I have the following model structure: A Carnival has many Events, which has many Competitors. A competitor has a polymorphic Participant, which is either a Student, or a Team (which has many students). In code:
class Carnival < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :event_type
belongs_to :division
has_and_belongs_to_many :genders
has_and_belongs_to_many :grades
has_many :competitors
has_many :students, through: :competitors, :source => :participant, :source_type => 'Student'
belongs_to :carnival
end
class Competitor < ActiveRecord::Base
belongs_to :event
belongs_to :participant, polymorphic: true
belongs_to :grade
belongs_to :house
end
class Team < ActiveRecord::Base
has_one :competitor, :as => :participant
has_one :event, :through => :competitor
has_and_belongs_to_many :students
end
class Student < ActiveRecord::Base
belongs_to :grade
belongs_to :house
belongs_to :gender
has_many :competitors, :as => :participant
has_many :events, :through => :competitors
has_many :carnivals, :through => :events
has_and_belongs_to_many :teams
end
There is a points attribute on Competitor, so that after an Event is run points are assigned to them.
What I would like is a series of methods that would give me all Students along with their total points.
Student.with_total_points_for_carnival(c)
Student.with_individual_points_for_carnival(c)
Student.with_team_points_for_carnival(c)
I got some SQL that does total points (though I'm a little uncomfortable with how/if it works properly, since I only really stumbled upon it):
SELECT
name,
SUM(points) as points
FROM
public.students_teams,
public.students,
public.teams,
public.competitors,
public.events
WHERE
students_teams.team_id = teams.id AND
students.id = students_teams.student_id AND
competitors.participant_id = teams.id AND
events.id = competitors.event_id AND
carnival_id = 1
GROUP BY
public.students.id
ORDER BY
points DESC
This gives total points. My Rails attempts give me results, but they aren't correct, and they aren't student records either:
def self.with_total_points_for(carnival)
Student.
joins(:competitors, :events, teams: { competitor: :event}).
where('events.carnival_id = ?', [carnival.id]).
group('students.id').
sum('competitors.points')
end
Any guidance as to how to get this working? So that I could go:
<% Student.with_total_points_for(#carnival).each do |s| %>
<%= s.name %>: <%= s.total_points %> <br>
<% end %>
Even better would be a function or scope like Student.with_all_points_for which would let me do:
<% Student.with_all_points_for(#carnival).each do |s| %>
<%= s.name %>: <%= s.total_points %> / <%= s.individual_points %> / <%= s.team_points %> <br>
<% end %>
but baby-steps for now...
I am not sure I caught your aim correctly but I think it might help you
Add needed fields to select
def self.with_total_points_for(carnival)
Student.
joins(:competitors, :events, teams: { competitor: :event}).
where('events.carnival_id = ?', [carnival.id]).
group('students.id').
select('students.*, SUM(competitors.points) AS total_points')
end
Some notes:
(I don't check it but I have similar issue before) if you use Postgres you shouldn't use students.* you should list all needed columns directly: select('students.id, students.name, ..., total_points')
(it can be useful) you can list in select any columns from involved tables but remember that you use group in the query
PS I am not sure that there is an easy way to get individual_points and team_points in one simple query. I think you should use subqueries for this purposes and insert them into main query. Be careful because it might be a heavy request to DB and an optimization will be required.

Getting a value from a table through a foreign key in Rails?

Hi I have created two models,
class Fixture < ActiveRecord::Base
attr_accessible :away_score, :away_team_id, :home_score, :home_team_id, :result, :week
belongs_to :team, :class_name => Team
end
class Team < ActiveRecord::Base
attr_accessible :form, :name
has_many :fixtures, :class_name => Fixture, :foreign_key => :home_team_id
has_many :fixtures, :class_name => Fixture, :foreign_key => :away_team_id
end
In my fixtures table I store a team_id in the home_team_id and away_team_id columns.
Then in my fixtures/show.html.erb I show the id stored
<p>
<b>Home team:</b>
<%= #fixture.home_team_id %>
</p>
How can I show the team.name from the teams table by getting the team.id stored in the fixtures table?
I need to change this line <%= #fixture.home_team_id %> to something else but not sure what?
http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference
With belongs_to you can access it with: #fixture.team since your defining a one to many relationship it should never have both home_team_id and away_team_id and will access the appropriate one.

Rails, Nested Models, Arrays, Iterating through a "for" loop

I'm new to Ruby and following this novawave tutorial (a little older) on creating an internal messaging system.
http://www.novawave.net/public/rails_messaging_tutorial.html
I'm currently attempting to list the sent messages, but I am unable to properly link to the message "recipients." Here is the for loop:
<% for message in #messages %>
<tr>
<td><%= message.user.name %></td>
<td><%= message.subject %></td>
<td><%= message.recipients.map(&:user).to_sentence %></td>
I'm getting an "unitialized constant" error for "recipients" I have three models: User, Message_Copy, and Message.
Message Model
belongs_to User,
has_many :recipients, :through => :message_copies
Message_Copy model
belongs_to Message and recipient
User model
has_many of the other models.
I've noticed this same problem in other applications. When I iterate through the array, if the primary model "belongs to" another model, I can access that model. For example, I can access the User model because the Message model "belongs_to" the User model. Hence I can use message.user.name. But I cannot access the attributes of a model that "belongs_to" the primary model. So I cannot access the Message_Copy model attributes in this array. So message.recipient.id will return an error. This question may have already been answered, and I may just be making a series of easy mistakes. But I'm confused and was hoping someone could help and possibly explain what I'm missing. Please let me know if more informaiton is required. Thank you
Apologies, here is more code from the models:
User Model
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :name, :email, :password, :password_confirmation
has_many :sent_messages, :class_name => "Message"
has_many :received_messages, :class_name => "MessageCopy", :foreign_key =>"recipient_id"
has_many :folders
class MessageCopy < ActiveRecord::Base
belongs_to :message
belongs_to :recipient
belongs_to :folder
delegate :user, :created_at, :subject, :body, :recipients, :to => :message
end
class Message < ActiveRecord::Base
belongs_to :user
has_many :message_copies
has_many :recipients, :through => :message_copies
before_create :prepare_copies
attr_accessor :to # array of people to send to
attr_accessible :subject, :body, :to
def prepare_copies
return if to.blank?
to.each do |recipient|
recipient = User.find(recipient)
#recipient=recipient
message_copies.build(:recipient_id => recipient.id, :folder_id => recipient.inbox.id)
end
end
end
I noticed this worked:
<%= message.message_copies %>
Instead of the above code:
<td><%= message.recipients %></td>
I don't understand this because the tutorial can use "recipients" and also because I can reference the Message_Copy model through Recipients in other methods.
But
<%= message.message_copies %>
also just returns an array, and I'm unclear how I can reach the specific attributes in the message_copy model, since
<%= message.message_copies.content %>
does not work.
Thank you! I'll post more code if necessary

Resources