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

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

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

Rails validates failed with a simple validation

I need validate the 'name' attribute of B class only when the attribute 'need_name' of A class is true. But I have a trouble with this validations.
My code:
class A
validates :need_name, presence: true
end
class B
validates :name, :presence => :need_name?
belongs_to :a
def need_name?
A.find(a).need_name
end
end
And my tests:
describe A do
context "validations" do
it { should validate_presence_of :need_name }
end
end
describe B do
context "validations" do
it { should validate_presence_of :name }
end
end
The test of class A works fine but I received this erro when I run the test of B class:
ActiveRecord::RecordInvalid:Validation failed: Need name can't be blank
The error disappears if I set true to the 'need_name' and I can't understand why this happens.
I really appreciate any help. Thank you guys.
Don't validate class A from within class B.
I'd validate the presence of the relation from class B, and then use validates_associated (described here) to trigger validations on A.
class A
validates :need_name, presence: true
end
class B
belongs_to :a
validates_presence_of :a
validates_associated :a
end
In the above code instances of class B will only validate if there's an association with a class A instance present and if the associated instance passes validation (i.e. has need_name set).
The solution was to change the way the validation is done to:
class A
validates :need_name, inclusion: { in: [true, false] }
end
class B
validates :name, :presence => { if: :need_name? }
belongs_to :a
def need_name?
a.need_name unless a.blank?
end
end

Validation not working on Class that extends ActiveModel::Naming, include ActiveModel::Validations

I and new to ruby and rails; I have the below ruby class definition that I am using in my rails 3 app. This class is simply used as a property container for contact information populated in my view on submit (form_for). I read a post where you can use ActiveModel directly apart from ActiveRecord, to perform validation, so I am trying it. I am getting the following exception when I check to see if the the object is valid? in my controller on postback. I assumed that valid? would be available being that I included ActiveModel::Validations; perhaps I am doing a few other things a$$ backwards. Any help would be appreciated:
undefined method `valid?' for #
Here's my class definition, further down is how I am handling it in my controller action:
require 'active_model'
class ContactModel
extend ActiveModel::Naming
include ActiveModel::AttributeMethods
include ActiveModel::Validations
include ActiveModel::Conversion
validates_presence_of :first_name, :last_name, :email_address, :email_address_confirmed, :subject, :contact_message
attr_accessor :first_name, :last_name, :email_address, :email_address_confirmed,
:telephone_number, :subject, :contact_message
Just messing around testing.
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == z
end
...
end
In my controller/action...
def send_email
##contact_model = ContactModel.new().initialize_copy(params[:contact_model])
#contact_model = params[:contact_model].dup
respond_to do |format|
if (#contact_model.valid?)
# Tell the UserMailer to send a welcome Email after save
ContactMailer.contact_email(#contact_model).deliver
format.html { redirect_to(#contact_model, notice: 'Email successfully sent.') }
format.json { render json: #contact_model, status: :created, location: #contact_model }
else
# What to do here?
end
end
end
In your controller you are setting #contact_model to a hash, params[:contact_model], and then calling valid? on it. You need create an instance of ContactModel and call valid on that. Like so:
#contact_model = ContactModel.new(params[:contact_model])
if (#contact_model.valid?)
...
I see commented out code that calls ContactModel.new(), but that's not how you want to do it anyway. Also, there is no reason to dup() or initialize_copy() on the params stuff.

How do I define associated Factories with FactoryBot?

Given two models, Alert and Zipcode, where one Alert must have 1 or more Zipcodes:
class Alert < ActiveRecord::Base
attr_accessible :descr, :zipcode
has_many :zipcode
validates :zipcode, :length => { :minimum => 1 }
end
class Zipcode < ActiveRecord::Base
attr_accessible :zip
belongs_to :alert
end
How do I write my FactoryBot factories so that:
Zipcode factories are defined in their own file
Alert factories are defined in their own file
Alert can rely on the factory defined by Zipcode?
All of the documentation and examples I read expect you to define the contained class inside the parent factory file, blob them all together, or make some other compromise or work-around. Isn't there a clean way to keep the spec factories separate?
The trick is to make sure the container class, that is, the one with a has_many statement in its definition, creates the contained class as an array in FactoryBot. For example:
In your spec/factories/zipcodes.rb:
FactoryBot.define do
factory :zipcode do
zip { 78701 + rand(99) }
end
end
And in spec/factories/alerts.rb:
FactoryBot.define do
factory :alert do
zipcode { Array.new(3) { FactoryBot.build(:zipcode) } }
end
end

Override a Mongoid model's setters and getters

Is there a way to override a setter or getter for a model in Mongoid? Something like:
class Project
include Mongoid::Document
field :name, :type => String
field :num_users, type: Integer, default: 0
key :name
has_and_belongs_to_many :users, class_name: "User", inverse_of: :projects
# This will not work
def name=(projectname)
#name = projectname.capitalize
end
end
where the name method can be overwritten without using virtual fields?
better use
def name=(projectname)
super(projectname.capitalize)
end
the method
self[:name] = projectname.capitalize
can be dangerous, cause overloading with it can cause endless recursion
def name=(projectname)
self[:name] = projectname.capitalize
end
I had a similar issue with needing to override the "user" setter for a belongs_to :user relationship. I came up with this solution for not only this case but for wrapping any method already defined within the same class.
class Class
def wrap_method(name, &block)
existing = self.instance_method(name)
define_method name do |*args|
instance_exec(*args, existing ? existing.bind(self) : nil, &block)
end
end
This allows you to do the following in your model class:
wrap_method :user= do |value, wrapped|
wrapped.call(value)
#additional logic here
end

Resources