Reputation: 1065
I have two classes A and B, both of them have some options, normally I will use one Hash to store options like @options[:name]='xxxx'
; now I want refactored it using meta programming,
class A
set_option :name, "james"
set_option :address, "some street"
def hello
puts @options[:name]
puts @options[:address]
end
end
class B
set_option :age, 18
def greeting
put @options[:age]
end
end
Here I want to use set_option
to set key&value pairs to one hash instance @options
, how can I do it?
Further I want to wrap the solution to a separate Module.
UPDATED:
thanks first, all your answers are valuable to me and make me clearer, now I realize that some thing I want is not so correctly, so what if I make the question like this?
option
to replace the @options
?@@options
will not work? I want different classes can have different hash instance.``` like this:
class A
set_option :name, "james"
set_option :provider, 'twitter'
def hello
puts option[:name]
end
end
class B
set_option :name, "not james"
def greeting
put option[:name]
end
end
After much thought I think What I really want is different option hash instance for different class, not class's instance.
Here is what I want and it can work.
module HasOptions
def self.included(cls)
cls.class_eval do
def self.set_option(key, value)
options[key] = value
end
def self.options
@options ||= {}
end
def options
self.class.options
end
end
end
end
class Baz
include HasOptions
set_option :name, "bad"
def greeting
puts options[:name]
end
end
class Foo
include HasOptions
set_option :name, "foo"
def greeting
puts options[:name]
end
end
Thanks for all your help.
Upvotes: 2
Views: 406
Reputation: 30445
Using an instance variable, as you are trying to do, won't work. However, you can use a method like this:
module HasOptions
def self.included(cls)
cls.class_eval do
def self.set_option(key, value)
(@@options ||= {})[key] = value
end
end
end
def options
@options ||= @@options.dup
end
end
This implementation allows you to set per-instance options, without overwriting the common options for all instances.
Upvotes: 5
Reputation: 121000
It looks like you are messing things a little bit. Let me try to list the questions you want to be solved:
Hash
(did I got it correct?—one hash for all the instances of your classes);Nothing of the above requires meta-programming actually. To satisfy first condition you are simply to find the first common ancestor of all your classes and [monkey]patch it with your @@options
var and set_option
method. If you did not decide to provide your own superclass for all your classes, let’s patch the very ancestor (e.g. Kernel
module):
module Kernel
def set_option name, value
(@@options ||= {})[name.to_sym] = value
end
end
Now any class may include set_option
“instruction” to put option in shared options hash (consider using instance variable @options
instead of @@options
to make options instance-specific, or use the Alex D’s solution.)
If you want to have curly fancy syntax for option setting (with defaults, inplace checkers, ruches and luxury goods), you are to use DSL here. The first example in this article shows how to implement pure set_option
with DSL. The further tuning is only limited by your imagination.
Upvotes: 2
Reputation: 5929
In case you want init instance options you should use instance methods to do it.
module OptionSetter
def set_option(key, value)
@options[key] = value
end
end
class Base
def initialize(options = {})
@options = {}
options.each do |key, value|
set_option key, value
end
end
end
class A < Base
include OptionSetter
def hello
puts @options[:name]
puts @options[:address]
end
end
class B < Base
include OptionSetter
def greeting
puts @options[:age]
end
end
A.new(name: "james", address: "some street").hello
B.new(age: 18).greeting
More common way to make it in ruby is using attr_accessor
's
class Base
attr_accessor :options
def initialize(options = {})
@options = options
end
end
class A < Base
def hello
puts @options[:name]
puts @options[:address]
end
end
class B < Base
def greeting
puts @options[:age]
end
end
A.new(name: "james", address: "some street").hello
B.new(age: 18).greeting
# another approach
james = A.new
james.options[:name] = "james"
james.options[:address] = "some street"
james.hello
Upvotes: 1