Override model association conditional for particular functionality - ruby

I have a User model that will return user studies that have a status of true:
has_many :user_studies, -> { where(status: true) }, dependent: :destroy
I am using this for over 80% of my functionality but there are some instances where I want to override this line of code.
I have a private method in the User controller that does the work of getting a user study:
def set_user_study
#user_study = #user.user_studies.find(params[:id])
end
#user is set in another private method:
def set_user
#user = users_api.find_by_guid(params[:user_id])
rescue Longboat::Api::Users::NotFound => e
logger.error e.message
raise ActionController::RoutingError.new('Not Found')
end
The private method is called when I try to edit a user_study(among others):
before_action :set_user_study, only: [:edit, :update, :destroy]
I am getting the following error(obviously) when trying to edit a study that does not have a status of true:
Couldn't find UserStudy with 'id'=1 [WHERE "user_studies"."user_id" = ? AND "user_studies"."status" = 't']
How can I continue to call the private method but allow a user to be able to edit a user study whether the status is true or not?

If your aim is to just override the association, don't use #user.user_studies . Instead, query out the values and use UserStudy.where(user_id: #user.id) in the private method. However, if the usage of user_studies is less in existing code, then just remove the condition in association and chain scopes to add this condition.

Related

Calling a Associated Controller Method Not Working

I have 3 models of User, Role and UserRole with their respective controllers as UsersController, RolesController and UserRolesController.
I have a method in the UserRoles controller which I would want to access through the Users controller but I keep having errors as explained below.
I have tried various means of even moving the method def self.add_roles_to_user(user, role) from the UsersRoles controller into the UserRole model and call it but I keep having the same error.
I have gone through lots of similar questions and various blogs, including those on this platform such as Calling a method from controller, and others but to no good results.
class UserRole < ApplicationRecord
# many-to-many association using join table with roles and user
belongs_to :user, inverse_of: :user_roles
belongs_to :role, optional: true, inverse_of: :user_roles
end
class User < ApplicationRecord
has_many :user_roles, inverse_of: :user
has_many :roles, through: :user_roles
end
class Role < ApplicationRecord
# table associations between role and user
has_many :user_roles, inverse_of: :role
has_many :users, through: :user_roles
end
class UserRolesController < ApplicationController
def self.add_roles_to_user(user, role)
if ! user.nil?
if role.length > 0
role.each do |sel_role|
#u_role = UserRole.new
#u_role.user_id = user_id
#u_role.role_id = sel_role.role_id
#u_role.save
end
end
end
end
end
class Users::RegistrationsController < Devise::RegistrationsController
def create_user
respond_to do |format|
if #user.save
# add roles
UserRoles.add_user_roles(params[:user], params[:role])
end
end
end
end
I am calling the add_uer_to_role method in the User controller when I am adding or creating a new user.
What I have noticed is that I keep getting different errors based on how I call the method.
For example, I expect to have no errors when I call the method like; UserRoles.add_roles_to_user(params[:user], params[:role]) but it gives the error NameError (uninitialized constant Users::RegistrationsController::UserRoles):
Hoping a good samaritan will come to my aid as soon as possible. Thanks in advance
If it is a common function, you can define it in application controller and call it. Else you can define in helper.
Please verify Calling a method from another controller
You can use that function as a module and use it:
# lib/common_stuff.rb
module CommonStuff
def common_thing
# code
end
end
# app/controllers/my_controller.rb
require 'common_stuff'
class MyController < ApplicationController
include CommonStuff
# has access to common_thing
end

How to render the correct page/url with error messages

I have created an app that splits up a Student's Information into multiple forms.
Everything works fine, but when I try to render Form_One after a Validation Error, it does not render the appropriate URL/Page.
EX.
adults/1/students/2/form_one
turns into
adults/1/students/2
I need to render the same url so I can proceed to form_2.
MODELS
class Adult < ActiveRecord::Base
has_many :students
end
class Student < ActiveRecord::Base
belongs_to :adult
validates :firstName, :presence => true,
length: { maximum: 50 }
validates :lastName, :presence => true,
length: { maximum: 50 }
end
CONTROLLER
def update
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
if #student.update_attributes(student_params)
###IF PASS REDIRECT TO THE NEXT FORM
if URI(request.referer).path == form_one_adult_student_path(#adult, #student)
redirect_to form_two_adult_student_path(#adult, #student)
elsif URI(request.referer).path == form_two_adult_student_path(#adult, #student)
redirect_to form_three_adult_student_path(#adult, #student)
else
redirect_to adult_path(#district, #adult)
end
else
error_messages = #student.errors.messages
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
#student.errors.messages.merge!(error_messages)
###IF ERROR AND ON FORM_ONE RENDER FORM_ONE
if URI(request.referer).path == form_one_adult_student_path(#adult, #student)
###FOR SOME REASON THIS RENDERS adults/1/students/2
###BUT I NEED IT TO RENDER adults/1/students/2/form_one
render 'form_one'
end
end
end
def form_one
#title = "Student Information"
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
end
def form_two
#title = "Parent Information"
#adult = Adult.find(params[:adult_id])
#student = Student.find(params[:id])
end
ROUTES
resources :adults do
resources :students do
member do
get :form_one, :form_two
end
end
end
###FOR SOME REASON THIS RENDERS adults/1/students/2
###BUT I NEED IT TO RENDER adults/1/students/2/form_one
render 'form_one'
Try redirect_to instead of render.
Keep in mind the difference between rendering a template, and making an HTTP request. Try pretending you are an HTTP client and think about the requests and responses. Sometimes it helps to look through your server log to see which controller actions happen, in which order. Look for lines like Processing students#update or Redirecting to ....
In the future, you may want to try resourceful routes like #show instead.

ruby on rails "before_save" in parent model to check attribut of child

I have a model 'place_detail' that has many a child 'emails'
has_many :emails, :dependent => :destroy
and in the email model:
belongs_to :place_detail
Now in the place_detail i want to make sur i added a email to check a attribut 'has_email'
so i added:
before_save :check_if_has_email
...
def check_if_has_email
if emails.count >0
self.has_email = true;
else
self.has_email = false;
end
end
the problem is that the attribute has_email does not check event if i created a email child. what i understand is that the parent is saved before the child
how can i get my has_email checked in place_detail when i create a child email?
EDIT:
I could simply put the has_email boolean in a method like
def has_email?
if self.emails.count >0
return true
..
but i prefer the boolean in the attribute because i use it in many scope and it would be a lot a change in the app
This will ensure that your model has at least one email (place it in your model file place_detail.rb)
has_many :emails, :dependent => :destroy
validates :emails, :length => { :minimum => 1 }
EDIT:
One suggestion would be just to check the trait place_detail.emails count when you need it. If you examine such data multiple times a request you can store it like so
def has_email?
(result ||= self.emails.count) > 0
end
That way it will only check your database once
If you're forced to use the 'has_email' attribute within the place_detail model, you could simply save the place_detail in the Create method of the Email controller.
#email = Email.new(email_params)
#email.place_detail.save

How do I "unscope" an edit path and/or action in Rails 3?

Currently I'm working on a simple Rails 3 web application with devise. When users sign up an Account is created. This the essence of the Account model:
class Account < ActiveRecord::Base
include Authority::UserAbilities
# Callbacks
after_create do |account|
account.create_person
account.add_role :owner, account.person
end
has_one :person, dependent: :destroy
end
And the essence of the Person model:
class Person < ActiveRecord::Base
belongs_to :account
default_scope where(published: true)
end
As you can see a person (which basically is a user profile) is created after the creation of an account, the default value of published is false. After signing up the user is signed in and redirected to the home page, which contains edit_person_path(current_account.person).
After setting the default_scope for Person a Routing Error: No route matches {:action=>"edit", :controller=>"people", :id=>nil} was thrown because of edit_person_path(current_account.person).
My solution to this was to change edit_person_path(current_account.person) into edit_person_path(Person.unscoped { current_account.person} ).
The issue that I'm having now is that although the page is rendering fine, when I click on the edit link the following exception is raised:
ActiveRecord::RecordNotFound in PeopleController#edit
Couldn't find Person with id=126 [WHERE "people"."published" = 't']
What's the best way to temporarily unscope the edit action, should I do this in PeopleController#edit?

Rails Model callback: Passing field as parameter to callback classes?

I want to implement before_validaton callback in a separate class so that it can be reused by multiple model classes.
Here in callback i want to strip field passed as parameter but i am not sure how to pass parameter to callback class. Also i want to pass this as reference rather than by value(not sure if this concept is in Ruby Rails). I am following the link http://guides.rubyonrails.org/active_record_validations_callbacks.html#callback-classes
Here is code which is not completely correct, please help for same
class StripFieldsCallback
def self.before_validation(field)
field = field.strip
end
end
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation StripFieldsCallback(name)
end
If i define method in model in itself rather than defining in separate callback class code is like this (which works fine)
class User < ActiveRecord::Base
validates_uniqueness_of :name, :case_sensitive => false
validates_length_of :name, :maximum => 50
before__validation :strip_blanks
protected
def strip_blanks
self.name = self.name.strip
end
end
Of course it is not good to replicate methods in all of models so i want to define method in callback classes.
You may do this or use normalize_attributes gem
module StripFieldsCallback
def before_validation_z(field)
write_attribute(field, read_attribute(field).strip) if read_attribute(field)
end
end
class User < ActiveRecord::Base
include StripFieldsCallback
before_validation lambda{|data| data.before_validation_z(:name)}
end

Resources