Reputation: 3186
I'm building a domain-specific hierarchical inheritance structure similar to ActiveRecord, i.e. I have a Base
class, and then n
children classes declared under Base
.
I'm currently declaring specifics in the children as constants, and then calling those from the shared methods in the Base
class, but it's getting unwieldy. I would like to implement the Rails style of declaring class-specific, instantiated values via method calls.
If you're familiar Rails, I'm essentially trying to duplicate the generic structure of has_many
/belongs_to
from ActiveRecord, or before_action
from ActionPack.
A simplistic, contrived example of what I'm trying to accomplish...
class Widget < Base
important_value :foo, :bar
end
widget = Widget.new
widget.foo
# => :bar
If someone can explain to me what to put in class Base
to achieve the above, I'll be well on my way.
Upvotes: 0
Views: 57
Reputation: 3186
This doesn't precisely match the conditions of my original question, but it was extremely helpful in moving me along.
class Base
# defining variables happens at the class level
# the first line allows us to set a sane default (skip for a nil value)
# the second line makes the method a getter as well as a setter (this is required)
def self.title(val = nil)
@title ||= 'DEFAULT TITLE'
return @title if val.nil?
@title = val.upcase
end
# the instance-level reader
# if you want to change the values from the child, use attr_accessor
# in either case, the visibility of @title in the child is unchanged (we assume its not accessed directly)
attr_reader :title
# set the instance variable when the class is instantiated
def initialize
instance_variable_set("@title", self.class.title)
end
end
class Foo < Base
title "foo title"
end
class Bar < Base
# if left commented out, the value will be set to 'DEFAULT TITLE'
# title "BAR TITLE"
end
f = Foo.new
f.title
# => "FOO TITLE"
b = Bar.new
b.title
# => "DEFAULT TITLE"
Upvotes: 0
Reputation: 1874
class Base
class << self
def important_value(key, value)
# define method named `key` which returns `value`
define_method(key) { value }
end
end
end
class Widget < Base
important_value :foo, :bar
end
Widget.new.foo # => :bar
Or, if the number of "important value" methods are small and known beforehand:
class Base
def foo
self.class.foo
end
class << self
attr_reader :foo
def important_value(key, value)
self.instance_variable_set(:"@#{key}", value)
end
end
end
class Widget < Base
important_value :foo, :bar
end
Widget.new.foo # => :bar
Upvotes: 1