Reputation: 2723
I am trying to DRY my code by writing a dynamically generated class methods.
What I want is to have multiple error classes and I have the following codes
ruby
class ChatPolicy::Error < StandardError
ERROR_CLASSES = [
{ class_name: 'UserBlacklisted', message: 'Message 1' },
{ class_name: 'UserSuspended', message: 'Message 2' },
{ class_name: 'UserNotEligibleToRent', message: 'Message 3' },
{ class_name: 'MembershipTierNotAllowed', message: 'Message 4' }
]
ERROR_CLASSES.each do |cls|
Object.const_set(cls[:class_name], Class.new {
attr_reader :object
def initialize(object)
@object = object
@message = cls[:message]
end
})
end
end
However, since in the Class.new {}
block, the variable cannot be passed in. I can't initialize the message variable. I am wondering how I can achieve that?
Upvotes: 0
Views: 273
Reputation: 121020
However, since in the
Class.new {}
block, the variable cannot be passed in.
That is not about “variable can not be passed in,” this is about “inside a block, the receiver differs and cls
local variable can not be resolved.” There are two possible approaches to achieve the goal:
— lookup the message directly from what is visible:
@message = ChatPolicy::Error::ERROR_CLASSES.detect do |hash|
hash[:class_name] == self.class.name
end[:message]
— or use class_eval
:
ERROR_CLASSES.each do |cls|
Object.const_set(cls[:class_name], class_eval %Q|
Class.new {
attr_reader :object
def initialize(object)
@object = object
@message = '#{cls[:message]}' # ⇐ HERE !!!
end
}|)
end
UPD the approach by @matt is better; I leave this answer for historical purposes only.
Upvotes: 1
Reputation: 79813
When you define a method with def
, you cannot refer to local variables from the outer scope inside the method definition, i.e. the cls
variable in the initialize
method.
You can refer to such local variables from inside a block, and you can use a block to create a method with define_method
.
So in your example you can get it working by changing the line
def initialize(object)
to
define_method(:initialize) do |object|
Now since the method body is a block you can refer to cls
inside the body.
Upvotes: 2