Inherit class-level instance variables in Ruby?
Use a mixin:
module ClassLevelInheritableAttributes
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def inheritable_attributes(*args)
@inheritable_attributes ||= [:inheritable_attributes]
@inheritable_attributes += args
args.each do |arg|
class_eval %(
class << self; attr_accessor :#{arg} end
)
end
@inheritable_attributes
end
def inherited(subclass)
@inheritable_attributes.each do |inheritable_attribute|
instance_var = "@#{inheritable_attribute}"
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
end
end
end
end
Including this module in a class, gives it two class methods: inheritable_attributes and inherited.
The inherited class method works the same as the self.included method in the module shown. Whenever a class that includes this module gets subclassed, it sets a class level instance variable for each of declared class level inheritable instance variables (@inheritable_attributes).
Ruby class instance variable vs. class variable
Instance variable on a class:
class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end
class Child < Parent
@things = []
end
Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new
p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]
Class variable:
class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end
class Child < Parent
end
Parent.things << :car
Child.things << :doll
p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]
mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new
[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
With an instance variable on a class (not on an instance of that class) you can store something common to that class without having sub-classes automatically also get them (and vice-versa). With class variables, you have the convenience of not having to write self.class
from an instance object, and (when desirable) you also get automatic sharing throughout the class hierarchy.
Merging these together into a single example that also covers instance variables on instances:
class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class
def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end
attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end
class Child < Parent
@shared_things = []
end
And then in action:
mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new
Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks
p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]
p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]
p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]
Confused about instance variable scope, top-level vs. class
Look below:
@x = 'foo'
def show_var
puts @x,self
p defined? @x
end
show_var
# >> foo
# >> main
# >> "instance-variable"
In the first case you defined @x
in the main
scope. And then when call the method show_var
,from the main
,thus you got the output of @x
,which is not the case of the other.
class Test
@x = "foo"
def show_var
p @x,self
p defined? @x
end
end
Test.new.show_var
# >> nil
# >> #<Test:0x9b6fcd4>
# >> nil
Should I use class variables or class-instance variables for class static variables in Ruby?
I recently discovered ActiveSupport defines class_inheritable_accessor
, which does what the class-instance variables do with the advantage that objects are not shared across inheritance, and you can have a default value for the variable when subclassing.
class Foo
class_inheritable_accessor :x, :y
end
Foo.x = 1
class Bar < Foo
end
Bar.x #=> 1
Bar.x = 3
Bar.x #=> 3
Foo.x #=> 1
More info here
Just for completeness: of the two presented options, I prefer going with the class-instance variables, since is often the expected behavior.
Related Topics
Get Chromes Console Log via Ruby Webdriver
Ruby: Array Contained in Array, Any Order
What Is the Purpose of 'Kernel'
Ruby on Rails - Link_To Button/Css
Keyword Arguments Unpacking (Splat) in Ruby
How to More Elegantly Remove Duplicate Items Across All Elements of a Ruby Array
Your Ruby Version Is 2.1.0, But Your Gemfile Specified 2.0.0
Rails 3. Simple_Format Do Not Wrap Result in Paragraph Tags
Aws Cognito User Authentication Missing Required Parameter Srp_A
Ruby on Rails: Confirmation Page for Activerecord Object Creation
Why Doesn't Relative_Require Work on Ruby 1.8.6
Ruby on Rails 4 Select Multiple
How to Create Email with CSS and Images from Rails
Why Is This Not a Syntax Error
Param Is Missing or the Value Is Empty
What Are the Ruby Win32API Parameters | How to Pass a Null Pointer