Controller issue in relation with 2 models - ruby-on-rails

I have a attachment model and controller for al my images on the site.
The relations are (polymorphic):
class House
has_many :attachments, :as => :attachable
end
class Apartment
has_many :attachments, :as => :attachable
end
The attachment controller looks like this to store the right variables.
class AttachmentController
#appartment = Appartment.find(params[:apartment_id])
#attachments = #appartment.attachments
end
this works on the apartment page/path. (apartment/1/assets) But on the house page (house/1/assets) I got the error message "Couldn't find Appartment without an ID"
How can I deal with this/best approach ? Conditions in the controller?

You'll need to check which keys are present first, something like this:
class AttachmentController
before_filter :prepare_attachable
def index
#attachments = #attachable.attachments
end
private
def prepare_attachable
if params.kas_key?(:apartment_id)
#attachable = Apartment.where(:id => params[:apartment_id]).first
elsif params.kas_key?(:house_id)
#attachable = House.where(:id => params[:house_id]).first
end
raise ActiveRecord::RecordNotFound if #attachable.blank?
end
end

Possible problem...
"Appartment" vs Apartment

Related

Rails undefined method

Why is this undefined? Does it have something to do with the #current_user?
I'm trying to create tasks for my challenges. And the created task should get /achievements. However, I get a GET 500 error.
This is the error I get:
NoMethodError at /achievements
==============================
> undefined method `achievements' for #<User:0x00000105140dd8>
app/controllers/achievements_controller.rb, line 5
--------------------------------------------------
``` ruby
1 class AchievementsController < ApplicationController
2
3
4 def index
> 5 #achievements = #current_user.achievements
6 render :json => #achievements
7 end
8
9 def new 10 #achievement = Achievement.new
This is my code in my controller
class AchievementsController < ApplicationController
def index
#achievements = #current_user.achievements
render :json => #achievements
end
def new
#achievement = Achievement.new
render :json => #achievement
end
#create a new achievment and add it to the current user
#check then set the acheivments pub challenge id to the current pub challenge
def create
#achievement = Achievement.new achievement_params
#achievement.user = #current_user.id
#achievement.pub_challenge = params[:id]
if #achievement.save
# render :json => #achievement #{ status: 'ok'}
else
render :json => {:errors => #achievement.errors}
end
end
def show
#achievement = Achievement.find params[:id]
render :json => #achievement
end
def destroy
#achievement = Achievement.find params[:id]
#achievement.destroy
end
private
def achievement_params
params.require(:achievement).permit(:pub_challenges)
end
end
You are missing the has_many :achievements relation in your User model.
You'll need to create the ActiveRecord associations you require:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :achievements
end
#app/models/achievement.rb
class Achievement < ActiveRecord::Base
belongs_to :user
end
This will give you the ability to call the achievements method on any User objects you have.
Error
The error you have is described as such:
undefined method `achievements' for #<User:0x00000105140dd8>
This basically means that you're trying to call an undefined method on a User object. Might sound simple, but really, most people don't understand it.
To explain properly, you have to remember that Rails, by virtue of being built on Ruby is object orientated. This means that everything you do in Rails should be structured around objects - which are defined in your Models:
This means that each time you call an object, you're actually above to invoke a series of "methods" which will give you the ability to either manipulate the object itself, or any of the associated functionality it has.
The problem you have is that your User object doesn't have the achievements method. Whilst you could simply do the following to fix the issue, because it's Rails, you'll need to populate the record with associative data:
#app/models/user.rb
class User < ActiveRecord::Base
has_many :achievements #-> what you need
def achievements
#this will also fix the error you see, although it's fundamentally incorrect
end
end
Something that helped me with this type of error was that the database table was missing the relevant column. Adding the required column to the database fixed the issue.

Using Active Record Reputation System gem, no sorting happens when I sort by votes

Following the RailsCast for the reputation system gem, I added the following code to my microposts_controller
def index
#microposts = Micropost.paginate(page: params[:page]).find_with_reputation(:votes, :all, order: "votes desc")
#micropost = current_user.microposts.build
end
But no sorting happens in my index action aside from the default scope I set in my model
In my micropost model I have
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :retweets
has_reputation :votes, source: :user, aggregated_by: :sum
default_scope -> { order('created_at DESC') }
If I change the default scope to
default_scope -> { order('votes DESC') }
It works how I want it to for the index page only but breaks all of my other pages.
I tried removing the default scope and leaving in the find_with_reputation method but it still doesn't order by votes.
I also tried defining the scope in a method in the micropost model like this
def self.popular
find_with_reputation(:votes, :all, {:order => 'votes desc'})
end
And make the code in the microposts_controller like this
def index
#microposts = Micropost.paginate(page: params[:page]).popular
#micropost = current_user.microposts.build
end
It still does not sort by votes.
Here is a copy of the log output from visiting the micropost index page
https://gist.github.com/anonymous/9745552
Here is a link to the gem https://github.com/NARKOZ/activerecord-reputation-system/tree/rails4
My routes.rb for microposts looks like this
resources :microposts, only: [:create, :destroy, :index] do
member { post :vote }
member { post :retweet}
end
Any guidance is appreciated.
Update
My home page feed is designed differently from what I'm doing for the Micropost Index feed. Maybe comparing what works to what doesn't will help pinpoint the issue.
I have a Static Pages Controller which sets its scope for the home action like this
def home
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
In the user model I define the feed method used in the static pages controller like so
def feed
Micropost.from_users_followed_by_including_replies(self)
end
the from_users_followed_by_including_replies(self) method is a scope i set in the micropost model
scope :from_users_followed_by_including_replies, lambda { |user| followed_by_including_replies(user) }
def self.followed_by_including_replies(user)
followed_ids = %(SELECT followed_id FROM relationships
WHERE follower_id = :user_id)
where("user_id IN (#{followed_ids}) OR user_id = :user_id OR to_id = :user_id",
{ :user_id => user })
end
Maybe I need to adapt a similar approach to the Index action for the Microposts controller
EDIT
In getting my hands on the code, I've found the real problem stems from the use of default_scope.
The original order() clause specified in your default scope is still being applied, even when adding your own order().
As a side note, this issue was kind of fixed in Rails 4.0, but the behavior was reverted in 4.0.1.
The solution was to apply a reorder()
# model
def self.popular
reorder('votes desc').find_with_reputation(:votes, :all)
end
# controller
def index
#microposts = Micropost.page(params[:page]).popular
end
ORIGINAL ANSWER
It seems that using the paginate method directly may not work with activerecord-reputation-system,
However, I found some examples showing that you can use the will_paginate page and per methods:
Perhaps it will work like this:
Micropost.page(params[:page]).per(30).find_with_reputation(:votes, :all, order: "votes desc")
Or with the model scope like this:
def self.popular
find_with_reputation(:votes, :all, order: 'votes desc')
end
you could do this:
Micropost.page(params[:page]).per(30).popular
Also, as a side note, your routes file is a little strange with multiple member blocks, when only one is necessary. I would make it look like this:
resources :microposts, only: [:create, :destroy, :index] do
member do
post :vote
post :retweet
end
end

ActiveRecord::RecordNotFound - in a descendant class' associated_controller#index

I am attempting to locate a parent object in a nested controller, so that I can associate the descendant resource with the parent like so:
# teams_controller.rb <snippet only>
def index
#university = Univeresity.find(params[:university_id])
#teams = #university.teams
end
When I call find(params[:university_id]) per the snippet above & in line 6 of teams_controller.rb, I receive ActiveRecord::RecordNotFound - Couldn't find University without an ID.
I'm not only interested in fixing this issue, but would also enjoy a better understanding of finding objects without having to enter a University.find(1) value, since I grant Admin the privilege of adding universities.
The Rails Guides say the following about the two kinds of parameters in a website:
3 Parameters
You will probably want to access data sent in by the user or other
parameters in your controller actions. There are two kinds of
parameters possible in a web application. The first are parameters
that are sent as part of the URL, called query string parameters. The
query string is everything after “?” in the URL. The second type of
parameter is usually referred to as POST data. This information
usually comes from an HTML form which has been filled in by the user.
It’s called POST data because it can only be sent as part of an HTTP
POST request. Rails does not make any distinction between query string
parameters and POST parameters, and both are available in the params
hash in your controller:
It continues a little further down, explaining that the params hash is an instance of HashWithIndifferentAccess, which allows usage of both symbols and strings interchangeably for the keys.
From what I read above, my understanding is that Rails recognizes both parameters (URL & POST) and stores them in the same hash (params).
Can I pass the params hash into a find method in any controller action, or just the create/update actions? I'd also be interested in finding a readable/viewable resource to understand the update_attributes method thats called in a controller's 'update' action.
Please overlook the commented out code, as I am actively searching for answers as well.
Thanks in advance.
Here are the associated files and server log.
Webrick
teams_controller.rb
class TeamsController < ApplicationController
# before_filter :get_university
# before_filter :get_team
def index
#university = University.find(params[:univeristy_id])
#teams = #university.teams
end
def new
#university = University.find(params[:university_id])
#team = #university.teams.build
end
def create
#university = University.find(params[:university_id])
#team = #university.teams.build(params[:team])
if #team.save
redirect_to [#university, #team], success: 'Team created!'
else
render :new, error: 'There was an error processing your team'
end
end
def show
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def edit
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
end
def update
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
if #team.update_attributes(params[:team])
redirect_to([#university, #team], success: 'Team successfully updated')
else
render(:edit, error: 'There was an error updating your team')
end
end
def destroy
#university = University.find(params[:university_id])
#team = #university.teams.find(params[:id])
#team.destroy
redirect_to university_teams_path(#university)
end
private
def get_university
#university = University.find(params[:university_id]) # can't find object without id
end
def get_team
#team = #university.teams.find(params[:id])
end
end
team.rb
class Team < ActiveRecord::Base
attr_accessible :name, :sport_type, :university_id
has_many :home_events, foreign_key: :home_team_id, class_name: 'Event'
has_many :away_events, foreign_key: :away_team_id, class_name: 'Event'
has_many :medias, as: :mediable
belongs_to :university
validates_presence_of :name, :sport_type
# scope :by_university, ->(university_id) { where(team_id: team_id).order(name: name) }
# scope :find_team, -> { Team.find_by id: id }
# scope :by_sport_type, ->(sport_type) { Team.where(sport_type: sport_type) }
# scope :with_university, joins: :teams
# def self.by_university(university_id)
# University.where(id: 1)
# University.joins(:teams).where(teams: { name: name })
# end
def self.by_university
University.where(university_id: university_id).first
end
def self.university_join
University.joins(:teams)
end
def self.by_sport_type(sport_type)
where(sport_type: sport_type)
end
def self.baseball
by_sport_type('Baseball/Softball')
end
end
university.rb
class University < ActiveRecord::Base
attr_accessible :address, :city, :name, :state, :url, :zip
has_many :teams, dependent: :destroy
validates :zip, presence: true, format: { with: /\A\d{5}(-\d+)?\z/ },
length: { minimum: 5 }
validates_presence_of :name, :address, :city, :state, :url
scope :universities, -> { University.order(name: 'ASC') }
# scope :by_teams, ->(university_id) { Team.find_by_university_id(university_id) }
# scope :team_by_university, ->(team_id) { where(team_id: team_id).order(name: name)}
def sport_type
team.sport_type
end
end
views/teams/index.html.erb
Placed in gists for formatting reasons
rake routes output: (in a public gist)
enter link description here
rails console
You're not going to want to have both:
resources :universities #lose this one
resources :universities do
resources :teams
end
As for params... you have to give a param. So, when you go to http://localhost:3000/teams there are no params, by default. If you go to http://localhost:3000/teams/3 then params[:id] = 3 and this will pull up your third team.
Keep in mind the nomenclature of an index. The index action of Teams, is going to list all of the teams. All of them. There is no one University there, so what are you actually trying to find? If anything, you'd have, for your University controller:
def show
#university = University.find(params[:id])
#teams = #university.teams
end
so, the address bar will be showing http://localhost:3000/universities/23, right? params[:id] = 23, then you can find the teams associated with that university.

Rails update related model data in update method

I have such code:
def update
#oil = Oil.find(params[:id])
#product_types = ProductType.all
if #oil.update_attributes(params[:oil])
if #oil.other_products_cross_lists.update_attributes(:cross_value => #oil.model.to_s.gsub(/\s+/, "").upcase)
redirect_to admin_oils_path
end
else
render :layout => 'admin'
end
end
but when i run it i get:
undefined method `update_attributes' for #<ActiveRecord::Relation:0x007f7fb4cdc220>
and my other_products_cross_lists isn't updated... Also i try update_attribute and get the same error.
What i do wrong?
Also when i run my destroy method
def destroy
#oil = Oil.find(params[:id])
if #oil.destroy
if #oil.other_products_cross_lists.destroy
redirect_to admin_oils_path
end
else
render :layout => 'admin'
end
end
other_products_cross_lists didn't destroy...
How can i solve this problem?
model:
class Oil < ActiveRecord::Base
has_many :other_products_cross_lists, :foreign_key => 'main_id'
class OtherProductsCrossList < ActiveRecord::Base
belongs_to :oil
other_products_cross_lists is an association on your Oil model.
You cannot use update_attributes on an Array or ActiveRecord:Relation object.
What you should do is
#oil.other_products_cross_lists.each {|list| list.update_attributes(:cross_value => #oil.model.to_s.gsub(/\s+/, "").upcase)}
for destroying
you can use
#oil.other_products_cross_lists.delete_all
or
#oil.other_products_cross_lists.destroy_all
You should check out the difference between delete_all and destroy_all for clarity.
as the error says other_products_cross_lists is a relation (I assume your model oil has_many other_products_cross_lists).
update_attribute is a method of an instance of a model, not a method of a relation.
I don't really understand, what you want to do with you update_attribute, but if user nested_attributes, then
#oil.update_attributes(params[:oil])
takes care of updating the relation.
Also, if you define your relation beween Oil and OtherProducts as dependend: :destroy Rails handles the removel of dependent records.

Skipping version creation of paper_trail when creating child records

I have a simple model with an after_create filter that creates association records.
class Subject
after_create :create_topics!
has_paper_trail :on => [:create, :update],
:ignore => [:topics]
private
def create_topics!
self.account.default_topics_for_subject_type(self.subject_type).each do |topic|
self.topics.create!({:name => topic.name})
end
end
end
However, creating a Subject now which e.g will create two topics results in two versions for the same subject, a create before and an update after the topics have changed.
Any ideas on how to solve this?
update
The topic model is not a subclass from subject, but belongs to it. They also have a paper_trail and should be versioned right from the beginning of the creation process through subject.
class Topic
belongs_to :subject
end
private
def create_topics!
account.default_topics_for_subject_type(subject_type).each_with_index do |topic, index|
if index == 0
create_topic!(topic)
else
without_versioning { create_topic!(topic) }
end
end
end
def create_topic!(topic)
self.topics.create!({:name => topic.name})
end

Resources