I'm using the callback before_update to call a function on model which set the checkbox value on my variable.
The problem is the checkbox value which is on params[:mail_checker_issue] isn't accessible on the model layer.
The question is: How to access this params using the callback before_update ? Below my code:
module IssueSetChecketIssuePatch
def self.included(base)
base.send(:include, InstanceMethods)
base.class_eval do
before_save :before_mail_checker
end
end
end
module InstanceMethods
require_dependency 'issue'
def before_mail_checker
self.set_mail_checker_issue(params[:mail_checker_issue])
end
def set_mail_checker_issue(mail)
#mail_checker = mail
end
def get_mail_checker_issue
#mail_checker
end
end
Rails.configuration.to_prepare do
Issue.send(:include, IssueSetChecketIssuePatch)
end
params are a controller concern and are wholly separate from models. Consider what should happen if you tried to save that model from a console, for example.
You need to pass the param to the model after you instantiate it from your controller, then check the value set on the model in your before_save callback.
It's also worth noting that your code is somewhat un-Rubyish (and really, looks a lot like Java!) - you could get the same effect by just defining an attr on the model.
Rails.configuration.to_prepare do
require_dependency 'issue'
class Issue
attr_accessor :mail_checker_issue
end
end
Then, once you have an issue:
# Controller code
#issue = Issue.find(params[:id])
#issue.mail_checker_issue = params[:mail_checker_issue]
You don't, models don't know about controllers or params hash.
You should include this logic at your controller instead of forcing it in a callback.
Related
I would like to create something similar to ActiveRecord validation: before_validate do ... end. I am not sure how could I reference attributes of class instance from the block given. Any idea?
class Something
attr_accessor :x
def self.before_validate(&block)
#before_validate_block = block
end
before_validate do
self.x.downcase
end
def validate!
# how should this method look like?
# I would like that block would be able to access instance attributes
end
end
#3limin4t0r's answer covers mimicing the behavior in plain ruby very well. But if your are working in Rails you don't need to reinvent the wheel just because you're not using ActiveRecord.
You can use ActiveModel::Callbacks to define callbacks in any plain old ruby object:
class Something
extend ActiveModel::Callbacks
define_model_callbacks :validate, scope: :name
before_validate do
self.x.downcase
end
def validate!
run_callbacks :validate do
# do validations here
end
end
end
Featurewise it blows the socks off any of the answers you'll get here. It lets define callbacks before, after and around the event and handles multiple callbacks per event.
If validations are what you really are after though you can just include ActiveModel::Validations which gives you all the validations except of course validates_uniqueness_of which is defined by ActiveRecord.
ActiveModel::Model includes all the modules that make up the rails models API and is a good choice if your are declaring a virtual model.
This can be achieved by using instance_eval or instance_exec.
class Something
attr_accessor :x
# You need a way to retrieve the block when working with the
# instance of the class. So I've changed the method so it
# returns the +#before_validate_block+ when no block is given.
# You could also add a new method to do this.
def self.before_validate(&block)
if block
#before_validate_block = block
else
#before_validate_block
end
end
before_validate do
self.x.downcase
end
def validate!
block = self.class.before_validate # retrieve the block
instance_eval(&block) # execute it in instance context
end
end
How about this?
class Something
attr_accessor :x
class << self
attr_reader :before_validate_blocks
def before_validate(&block)
#before_validate_blocks ||= []
#before_validate_blocks << block
end
end
def validate!
blocks = self.class.before_validate_blocks
blocks.each {|b| instance_eval(&b)}
end
end
Something.before_validate do
puts x.downcase
end
Something.before_validate do
puts x.size
end
something = Something.new
something.x = 'FOO'
something.validate! # => "foo\n3\n"
This version allows us to define multiple validations.
For example, if I have a method in my model that accepts a string from my controller. Am I able to call it elsewhere in my model, which would return the boolean value? Or is there a better way, possibly by setting a variable?
class Object
def method_true?(args)
['a', 'b'].include?(args)
end
def do_stuff
method_true? #outcome of method_true?
end
end
Patching Object class is not the best idea.
More Rails way would be creating a module with these methods and mixing it into your models (or, into ActiveRecord::Base, if you are sure you need it everywhere):
module ActiveRecordExtentions
def method_true?(args)
['a', 'b'].include?(args)
end
def do_stuff
method_true? #outcome of method_true?
end
end
# /initializers/activerecord_extentions.rb
ActiveRecord::Base.send(:include, ActiveRecordExtentions) # or extend, you decide
Now methods are available in all models inheriting from ActiveRecord::Base.
I was playing around with implementing Rails model callbacks (after_save, before_save) etc. using alias method. All it does is it aliases the save method to save_with_callbacks. It works, except before_save has to be called after save is defined or alias keyword throws an error. I'm still in the process of understanding how Rails callbacks really works, but was wondering if there's a way to use before_filter anywhere in the model.
module ClassMethods
def before_save
class_eval do
# old_save points to save
# save points to save_with_callbacks
alias :old_save :save
alias :save :save_with_callbacks
end
end
end
module InstanceMethods
def save_with_callbacks
#save_with_callbacks_text = 'Saving with callbacks'
old_save
end
end
class Task
extend ClassMethods
include InstanceMethods
attr_reader :save_text, :save_with_callbacks_text
def save
#save_text = 'Saving'
end
# Needs to be called after save, save_with_callbacks are defined
before_save
end
I forgot users don't generally define 'save' method, but let ORM do it for you. Moved save method to InstanceMethods and that solves the problem.
By convention, should the following be defined as an instance method of my model or a helper method?
# app/models/user.rb
class User < ActiveRecord::Base
def full_name
"#{first_name} #{last_name}"
end
end
or
# app/helpers/users_helper.rb
module UsersHelper
def full_name
"#{#user.first_name} #{#user.last_name}"
end
end
Many thanks.
Go with the first (keep in model), also because you might want to do other stuff, like combined indexes :)
Everything directly related to your model should stay in your model.
This way, you keep coherent logic and tests.
I know I can add new methods to models but I can't seem to overwrite an existing method. Here's what I have
In my User.rb
include ExtraMethods
def is_invisible?
true unless self.active?
end
In my module
module ExtraMethods
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def user_extra_methods
include ExtraMethods::InstanceMethods
end
end
module InstanceMethods
def is_invisible?
true unless self.active? || self.admin?
end
end
end
ActiveRecord::Base.send(:include, ExtraMethods)
User.send(:user_extra_methods)
What I want to happen is for the method in the plugin to override the method in the model. Any thoughts or references would be great, can't seem to find a good reference for this.
thanks!
J
The order in which you declare the class members is important.
You're performing the plugin's include before the self.active? method is declared... The model declaration will always take precedence, since it was declared later.
You'll have to resort to something like this:
http://weblog.rubyonrails.org/2006/4/26/new-in-rails-module-alias_method_chain