I have a class that has N methods.
class MyClass
def self.method_one(params)
#specific code
end
def self.method_two(params)
#specific code
end
end
I need to add the same code for each method created in that class. I need to put the code between "begin and rescue"
I tried the following, but I did not succeed:
class MyClass < BaseClass
add_rescue :method_one, :method_two, Exception do |message|
raise message
end
def self.method_one(params)
#specific code
end
def self.method_two(params)
#specific code
end
end
I created a method to change the methods
class BaseClass
def self.add_rescue(*meths, exception, &handler)
meths.each do |meth|
old = instance_method(meth)
define_method(meth) do |*args|
begin
old.bind(self).call(*args)
rescue exception => e
handler.call(e)
end
end
end
end
end
I always get the error message: undefined method `method_one 'for Myclass: Class
MyClass#method_one is a class method or, in other words, the instance method of MyClass.singleton_class. That said, we can Module#prepend the desired functionality to MyClass.singleton_class:
def self.add_rescue(*meths, exception, &handler)
mod =
Module.new do
meths.each do |meth|
define_method meth do |*args, &λ|
begin
super(*args, &λ)
rescue exception => e
handler.(e)
end
end
end
end
MyClass.singleton_class.prepend(mod)
end
Related
Getting the following error when trying to call a private method inside the same class:
undefined local variable or method a_private_method' for AClass (NameError)`
Here the class:
class AClass
def self.public_method
file = a_private_method
end
private
def a_private_method
do something
end
end
You are trying to call an instance method from a class method, this does of course not work.
Try this
class AClass
class << self
def public_method
file = a_private_method
end
private
def a_private_method
# do something
end
end
end
You can also use self as the receiver similar to what you already did but pleases be aware that moving the method to the private part of your class does not work in this case. You could use private_class_method though.
class AClass
def self.public_method
file = a_private_method
end
def self.a_private_method
# do something
end
private_class_method :a_private_method
end
end
See https://jakeyesbeck.com/2016/01/24/ruby-private-class-methods and https://dev.to/adamlombard/ruby-class-methods-vs-instance-methods-4aje.
In TestObserver class, I have self.delivered_email(message) method, the message is the action mailer instance, I'm calling get_category method in self.delivered_email method. But I received error message "undefined method `get_category' for TestObserver:Class". What is the problem and how could I solve it? I'm using observer pattern here in rails.
class TestObserver
def self.delivered_email(message)
begin
category = get_category(message)
# do something here
rescue => ex
# do something here
end
end
private
def get_category(message)
# do something here
end
end
ActionMailer::Base.register_observer(TestObserver)
This is because, you are trying to access a instance method from a class method
your self.delivered_email is a class level method, if it calls other methods those should be class methods too. In this case get_category(message) is a instance method.
To fix the error you could make the get_category(message) as a class method. (if it fits your context)
class TestObserver
def self.delivered_email(message)
begin
category = get_category(message)
# do something here
rescue => ex
# do something here
end
end
def self.get_category(message)
# do something here
end
end
I want to make my own attr_accessor like this:
class Class
def attr_accessor_with_onchange_callback(*args, &block)
raise 'Callback block is required' unless block
args.each do |arg|
attr_name = arg.to_s
define_method(attr_name) do
self.instance_variable_get("##{attr_name}")
end
define_method("#{attr_name}=") do |argument|
old_value = self.instance_variable_get("##{attr_name}")
if argument != old_value
self.instance_variable_set("##{attr_name}", argument)
self.instance_exec(attr_name, argument, old_value, &block)
end
end
end
end
end
It works if I put this definition in config/enviroment.rb before app initialization.
class MyCLass < ActiveRecord::Base
attr_accessor_with_onchange_callback :some_attr do |attr_name, value, old_value|
end
But I think it should be inside lib/ folder. If I put this
module ModelHelpers
class Class
def attr_accessor_with_onchange_callback(*args, &block)
raise 'Callback block is required' unless block
args.each do |arg|
attr_name = arg.to_s
define_method(attr_name) do
self.instance_variable_get("##{attr_name}")
end
define_method("#{attr_name}=") do |argument|
old_value = self.instance_variable_get("##{attr_name}")
if argument != old_value
self.instance_variable_set("##{attr_name}", argument)
self.instance_exec(attr_name, argument, old_value, &block)
end
end
end
end
end
end
to lib/model_helpers.rb and this
require 'model_helpers'
class MyCLass < ActiveRecord::Base
include ModelHelpers
attr_accessor_with_onchange_callback :some_attr do |attr_name, value, old_value|
end
to my_class.rb then I get an error: undefined method attr_accessor_with_onchange_callback.
What am I doing wrong?
Try to define your method attr_accessor_with_onchange_callback directly in ModelHelpers, without class Class. And use extend keyword instead include inside class defenition. Like this:
module ModelHelpers
def attr_accessor_with_onchange_callback(*args, &block)
...
require 'model_helpers'
class MyCLass < ActiveRecord::Base
extend ModelHelpers
Here is my example:
module ModelHelpers
def my_method
puts 'ModelHelpers::my_method called.'
puts "self is #{self}"
end
end
class MyCLass
extend ModelHelpers
my_method
end
And output is:
> ruby custom_method_inside_class.rb
ModelHelpers::my_method called.
self is MyCLass
I'm creating a module that extends the functionality of an ActiveRecord model.
Here's my initial setup.
My class:
class MyClass < ActiveRecord::Base
is_my_modiable
end
And Module:
module MyMod
def self.is_my_modiable
class_eval do
def new_method
self.mod = true
self.save!
end
end
end
end
ActiveRecord::Base(extend,MyMod)
What I would like to do now is extend the functionality of the new_method by passing in a block. Something like this:
class MyClass < ActiveRecord::Base
is_my_modiable do
self.something_special
end
end
module MyMod
def self.is_my_modiable
class_eval do
def new_method
yield if block_given?
self.mod = true
self.save!
end
end
end
end
This doesn't work though, and it makes sense. In the class_eval, the new_method isn't being executed, just defined, and thus the yield statement wouldn't get executed until the method actually gets called.
I've tried to assign the block to a class variable within the class_eval, and then call that class variable within the method, but the block was being called on all is_my_modiable models, even if they didn't pass a block into the method.
I might just override the method to get the same effect, but I'm hoping there is a more elegant way.
If I understood you correctly, you can solve this by saving passed block to an instance variable on class object and then evaling that in instance methods.
bl.call won't do here, because it will execute in the original context (that of a class) and you need to execute it in scope of this current instance.
module MyMod
def is_my_modiable(&block)
class_eval do
#stored_block = block # back up block
def new_method
bl = self.class.instance_variable_get(:#stored_block) # get from class and execute
instance_eval(&bl) if bl
self.mod = true
self.save!
end
end
end
end
class MyClass
extend MyMod
is_my_modiable do
puts "in my modiable block"
self.something_special
end
def something_special
puts "in something special"
end
attr_accessor :mod
def save!; end
end
MyClass.new.new_method
# >> in my modiable block
# >> in something special
You can do this by assigning the block as a method parameter:
module MyMod
def self.is_my_modiable
class_eval do
def new_method(&block)
block.call if block
self.mod = true
self.save!
end
end
end
end
I have a Model, which has method_1 to method_10. I also have ModelObserver.
I would like to notifiy ModelObserver before invoking method1 to method_9, but not method_10.
Is there a DRY way to write this, instead of repeating notify_observers(:after_something) in all 9 methods?
Add a file called monkey_patches.rb in config/initializers dirctory.
class Object
def self.method_hook(*args)
options = args.extract_options!
return unless (options[:before].present? or options[:after].present?)
args.each do |method_name|
old_method = instance_method(method_name) rescue next
define_method(method_name) do |*args|
# invoke before callback
if options[:before].present?
options[:before].is_a?(Proc) ? options[:before].call(method_name, self):
send(options[:before], method_name)
end
# you can modify the code to call after callback
# only when the old method returns true etc..
old_method.bind(self).call(*args)
# invoke after callback
if options[:after].present?
options[:after].is_a?(Proc) ? options[:after].call(method_name, self):
send(options[:after], method_name)
end
end
end
end
end
The patch enables you to add before and after callbacks on an instance method of a class. A hook can be:
The name of an instance method which accepts one parameter
A lambda accepting two parameters
Multiple hooks can be registered on a same method. The method being hooked should come before the hook.
E.g:
class Model < ActiveRecord::Base
def method1
end
def method2
end
def method3
end
def method4
end
def update_cache
end
# instance method name as `after` callback parameter
method_hook :method1, :method2, :after => :update_cache
# lambda as `before` callback parameter
method_hook :method1, :method2,
:before => lambda{|name, record| p name;p record}
# lambda as `after` callback parameter
method_hook :method3, :method4,
:after => lambda{|name, record|
Model2.increment_counter(:post_count, record.model2_id)}
end
How about something like this?
def notify; puts "Was notified."; end
def method1; end
def method2; end
def method3; end
def original
notify
method1
notify
method2
method3
end
def dry
[:method1, :method2].each do |m|
notify
send(m)
end
method3
end
original
dry