Last active
August 29, 2015 13:56
-
-
Save johnlane/9079353 to your computer and use it in GitHub Desktop.
The One Module (TOM) is an example Ruby module that demonstrates setting of module, class, class instance and instance variables for classes that include or extend the module.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# TOM : The One Module | |
# | |
# An example Ruby module that demonstrates setting of module, class, class instance | |
# and instance variables for classes that include or extend the module | |
# | |
# (c) John Lane 20140218 | |
# | |
module Tom | |
extend self # makes module methods available as class and instance methods. | |
@moduleinstvar = "mv1" # this sets the module instance variable (unique to the module) | |
@@classvar = "cv1" # this sets the class variable (common to module and all classes) | |
# Callback when a class includes the module (which defines instance methods). | |
# It extends the same module so that class methods are defined | |
# It defines the class instance variable in the new class (an instance of 'Class') | |
def included(c) | |
c.instance_eval do | |
@classinstvar ||= "civ1" # define class instance variable in including class | |
end | |
c.extend self # make module (instance) methods also exist as class methods | |
# self.classvar = "cv2" # this sets the class variable | |
attr_accessor :instvar | |
end | |
# Callback when a class extends the module (which defines class methods). | |
# It includes the same module so that instance methods are defined | |
# It defines the class instance variable in the new class (an instance of 'Class') | |
def extended(c) | |
m = self # need to refer to module (self) inside block below | |
# executes inside the class being extended into (self = c) | |
unless included_modules.include? m | |
c.instance_eval do | |
@classinstvar ||= "civ2" # define class instance variable in extending class | |
include m unless included_modules.include? m # include is a private method of c | |
end | |
# self.classvar = "cv3" # alternate place to set the class variable (see note) | |
end | |
end | |
# Callback when a class that includes/extends the module is instantiated | |
# It defines the instance variable within the instance. | |
def initialize | |
@instvar = "iv1" # this sets @cv, the instance variable | |
end | |
# Callback when a class that includes/extends the module is inherited | |
# It defines the class instance variable within the inherited class | |
def inherited(c) | |
c.instance_eval do | |
@classinstvar ||= "civ3" # define class instance variable in inheriting class | |
end | |
end | |
# The methods below are accessors that can be used as both class and instance methods. | |
# (due to the "extend" at the top of the module. They can be used on a class that | |
# includes/extends the module or on an instance of such a class. | |
# | |
# Accessors for the class variable | |
def classvar=(v) | |
@@classvar = v | |
end | |
def classvar | |
@@classvar | |
end | |
# Accessors for the class instance variables | |
def classinstvar=(v) | |
if defined?(@classinstvar) | |
@classinstvar = v | |
elsif self.class.respond_to? :classinstvar= | |
self.class.classinstvar = v | |
else | |
@classinstvar = v | |
end | |
end | |
def classinstvar | |
if defined?(@classinstvar) | |
@classinstvar | |
elsif self.class.respond_to? :classinstvar | |
self.class.classinstvar | |
else | |
nil | |
end | |
end | |
end | |
# Notes ############################################################################## | |
# | |
# Module Instance Variable | |
# this is an instance variable in the scope of the module. It does not exist in any | |
# classes (they have class instance varaibles instead) or instances (they have | |
# instane variables instead) | |
# mv1 : Default value. Set when the module is first defined | |
# | |
# Class Variable | |
# this is shared between the module and all classes that include the module | |
# the default value is reset at every include/extend | |
# cv1 : Default value. Set when the module is first defined | |
# cv2 : Changes current value whenever module is included into a class. Commented out | |
# cv3 : Changes current value whenever module is extended into a class. Commented out | |
# | |
# Class Instance Variable | |
# this is defined when the module is included/extended into a class | |
# each class that includes/extends the module gets its own class instance variable | |
# each class that inherits such a class also gets its own class instance variable | |
# civ1 : set when module is included into class. | |
# civ2 : set when module is extended into class. | |
# civ3 : set when class inherits a class that includes/extends the module | |
# | |
# Instance Variable | |
# this is defined with a class that included/extends the module is instantiated | |
# iv1 : set when the instance is created. | |
# | |
#######################################################################JL20140218##### |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# TOM : The One Module | |
# | |
# An example Ruby module that demonstrates setting of module, class, class instance | |
# and instance variables for classes that include or extend the module | |
# | |
# rspec test suite | |
# | |
# (c) John Lane 20140218 | |
# | |
require_relative 'tom' | |
class TomIncluded; include Tom; end | |
class TomExtended; extend Tom; end | |
class InheritedA < TomIncluded; end | |
class InheritedB < TomIncluded; end | |
class InheritedC < TomExtended; end | |
class InheritedD < TomExtended; end | |
describe Tom do | |
it "is a module" do | |
Tom.class.should == Module | |
end | |
it "has a module instance variable called @moduleinstvar" do | |
Tom.instance_variables.should include(:@moduleinstvar) | |
Tom.instance_variable_get(:@moduleinstvar).should == "mv1" | |
end | |
it "has a class variable called @@classvar" do | |
Tom.class_variables.should include(:@@classvar) | |
Tom.class_variable_get(:@@classvar).should == "cv1" | |
end | |
describe "when included in a class" do | |
it "includes the module" do | |
TomIncluded.included_modules.include? Tom.should be_true | |
InheritedA.included_modules.include? Tom.should be_true | |
InheritedB.included_modules.include? Tom.should be_true | |
end | |
it "does not have an instance variable called @moduleinstvar" do | |
TomIncluded.instance_variables.should_not include(:@moduleinstvar) | |
TomIncluded.class.instance_variables.should_not include(:@moduleinstvar) | |
InheritedA.instance_variables.should_not include(:@moduleinstvar) | |
InheritedA.class.instance_variables.should_not include(:@moduleinstvar) | |
InheritedB.instance_variables.should_not include(:@moduleinstvar) | |
InheritedB.class.instance_variables.should_not include(:@moduleinstvar) | |
end | |
it "has a class variable called @@classvar" do | |
TomIncluded.class_variables.should include(:@@classvar) | |
TomIncluded.class_variable_get(:@@classvar).should == "cv1" | |
TomIncluded.class_variable_get(:@@classvar).should == Tom.class_variable_get(:@@classvar) | |
InheritedA.class_variables.should include(:@@classvar) | |
InheritedA.class_variable_get(:@@classvar).should == "cv1" | |
InheritedA.class_variable_get(:@@classvar).should == Tom.class_variable_get(:@@classvar) | |
InheritedB.class_variables.should include(:@@classvar) | |
InheritedB.class_variable_get(:@@classvar).should == "cv1" | |
InheritedB.class_variable_get(:@@classvar).should == Tom.class_variable_get(:@@classvar) | |
end | |
it "has a class instance varaible called @classinstvar" do | |
TomIncluded.instance_variables.should include(:@classinstvar) | |
TomIncluded.instance_variable_get(:@classinstvar).should == "civ1" | |
InheritedA.instance_variables.should include(:@classinstvar) | |
InheritedA.instance_variable_get(:@classinstvar).should == "civ3" | |
InheritedB.instance_variables.should include(:@classinstvar) | |
InheritedB.instance_variable_get(:@classinstvar).should == "civ3" | |
end | |
it "doesn't change the module variable's value" do | |
Tom.instance_variable_get(:@moduleinstvar).should == "mv1" | |
end | |
it "has a class variable accessor called classvar" do | |
TomIncluded.methods.should include(:classvar) | |
TomIncluded.classvar.should == "cv1" | |
InheritedA.methods.should include(:classvar) | |
InheritedA.classvar.should == "cv1" | |
InheritedB.methods.should include(:classvar) | |
InheritedB.classvar.should == "cv1" | |
end | |
it "has a class variable accessor called classvar=" do | |
TomIncluded.methods.should include(:'classvar=') | |
InheritedA.methods.should include(:'classvar=') | |
InheritedB.methods.should include(:'classvar=') | |
end | |
it "has a class instance variable accessor called classinstvar" do | |
TomIncluded.methods.should include(:classinstvar) | |
TomIncluded.classinstvar.should == "civ1" | |
InheritedA.methods.should include(:classinstvar) | |
InheritedA.classinstvar.should == "civ3" | |
InheritedB.methods.should include(:classinstvar) | |
InheritedB.classinstvar.should == "civ3" | |
end | |
it "has a class instance variable accessor called classinstvar=" do | |
TomIncluded.methods.should include(:'classinstvar=') | |
InheritedA.methods.should include(:'classinstvar=') | |
InheritedB.methods.should include(:'classinstvar=') | |
end | |
end | |
describe "when extended in a class" do | |
it "includes the module" do | |
TomExtended.included_modules.include? Tom.should be_true | |
InheritedC.included_modules.include? Tom.should be_true | |
InheritedD.included_modules.include? Tom.should be_true | |
end | |
it "does not have an instance variable called @moduleinstvar" do | |
TomExtended.instance_variables.should_not include(:@moduleinstvar) | |
TomExtended.class.instance_variables.should_not include(:@moduleinstvar) | |
InheritedC.instance_variables.should_not include(:@moduleinstvar) | |
InheritedC.class.instance_variables.should_not include(:@moduleinstvar) | |
InheritedD.instance_variables.should_not include(:@moduleinstvar) | |
InheritedD.class.instance_variables.should_not include(:@moduleinstvar) | |
end | |
it "has a class variable called @@classvar" do | |
TomExtended.class_variables.should include(:@@classvar) | |
TomExtended.class_variable_get(:@@classvar).should == "cv1" | |
TomExtended.class_variable_get(:@@classvar).should == Tom.class_variable_get(:@@classvar) | |
InheritedC.class_variables.should include(:@@classvar) | |
InheritedC.class_variable_get(:@@classvar).should == "cv1" | |
InheritedC.class_variable_get(:@@classvar).should == Tom.class_variable_get(:@@classvar) | |
InheritedD.class_variables.should include(:@@classvar) | |
InheritedD.class_variable_get(:@@classvar).should == "cv1" | |
InheritedD.class_variable_get(:@@classvar).should == Tom.class_variable_get(:@@classvar) | |
end | |
it "has a class instance varaible called @classinstvar" do | |
TomExtended.instance_variables.should include(:@classinstvar) | |
TomExtended.instance_variable_get(:@classinstvar).should == "civ2" | |
InheritedC.instance_variables.should include(:@classinstvar) | |
InheritedC.instance_variable_get(:@classinstvar).should == "civ3" | |
InheritedD.instance_variables.should include(:@classinstvar) | |
InheritedD.instance_variable_get(:@classinstvar).should == "civ3" | |
end | |
it "doesn't change the module variable's value" do | |
Tom.instance_variable_get(:@moduleinstvar).should == "mv1" | |
end | |
it "has a class variable accessor called classvar" do | |
TomExtended.methods.should include(:classvar) | |
TomExtended.classvar.should == "cv1" | |
InheritedC.methods.should include(:classvar) | |
InheritedC.classvar.should == "cv1" | |
InheritedD.methods.should include(:classvar) | |
InheritedD.classvar.should == "cv1" | |
end | |
it "has a class variable accessor called classvar=" do | |
TomExtended.methods.should include(:'classvar=') | |
InheritedC.methods.should include(:'classvar=') | |
InheritedD.methods.should include(:'classvar=') | |
end | |
it "has a class instance variable accessor called classinstvar" do | |
TomExtended.methods.should include(:classinstvar) | |
TomExtended.classinstvar.should == "civ2" | |
InheritedC.methods.should include(:classinstvar) | |
InheritedC.classinstvar.should == "civ3" | |
InheritedD.methods.should include(:classinstvar) | |
InheritedD.classinstvar.should == "civ3" | |
end | |
it "has a class instance variable accessor called classinstvar=" do | |
TomExtended.methods.should include(:'classinstvar=') | |
InheritedC.methods.should include(:'classinstvar=') | |
InheritedD.methods.should include(:'classinstvar=') | |
end | |
end | |
describe "when instantiated" do | |
@ci = TomIncluded.new | |
@ce = TomExtended.new | |
instances = [@ci, @ce] | |
instances.each do |i| | |
it "has a class instance varaible called @classinstvar" do | |
i.class.instance_variables.should include(:@classinstvar) | |
end | |
it "has an instance varaible @instvar" do | |
i.instance_variables.should include(:@instvar) | |
end | |
it "has a class variable accessor called classvar" do | |
i.methods.should include(:classvar) | |
end | |
it "has a class variable accessor called classvar=" do | |
i.methods.should include(:'classvar=') | |
end | |
it "has a class instance variable accessor called classinstvar" do | |
i.methods.should include(:classinstvar) | |
end | |
it "has a class instance variable accessor called classinstvar=" do | |
i.methods.should include(:'classinstvar=') | |
end | |
it "has an instance variable accessor called instvar" do | |
i.methods.should include(:instvar) | |
end | |
it "has an instance variable accessor called instvar=" do | |
i.methods.should include(:'instvar=') | |
end | |
end | |
end | |
describe "changing the module instace variable" do | |
it "can be changed" do | |
Tom.instance_variable_set('@moduleinstvar','woohoo') | |
Tom.instance_variable_get('@moduleinstvar').should == 'woohoo' | |
Tom::instance_variable_set('@moduleinstvar','woohoo') | |
Tom::instance_variable_get('@moduleinstvar').should == 'woohoo' | |
end | |
end | |
describe "changing variable values" do | |
instances = [ | |
TomIncluded.new, | |
TomExtended.new, | |
InheritedA.new, | |
InheritedA.new, | |
InheritedB.new, | |
InheritedB.new, | |
InheritedC.new, | |
InheritedC.new, | |
InheritedD.new, | |
InheritedD.new, | |
TomIncluded.new, | |
TomExtended.new, | |
InheritedA.new, | |
InheritedA.new, | |
InheritedB.new, | |
InheritedB.new, | |
InheritedC.new, | |
InheritedC.new, | |
InheritedD.new, | |
InheritedD.new | |
] | |
describe "changing the class variable" do | |
instances.each do |i| | |
it "can be changed" do | |
i.classvar = 'changed value' | |
i.classvar.should == 'changed value' | |
Tom.classvar.should == 'changed value' | |
TomIncluded.classvar.should == 'changed value' | |
TomExtended.classvar.should == 'changed value' | |
InheritedA.classvar.should == 'changed value' | |
InheritedB.classvar.should == 'changed value' | |
InheritedC.classvar.should == 'changed value' | |
InheritedD.classvar.should == 'changed value' | |
end | |
it "is the same in all classes and instances" do | |
Tom.classvar = 'class value' | |
Tom.classvar.should == 'class value' | |
TomIncluded.classvar.should == 'class value' | |
TomExtended.classvar.should == 'class value' | |
InheritedA.classvar.should == 'class value' | |
InheritedB.classvar.should == 'class value' | |
InheritedC.classvar.should == 'class value' | |
InheritedD.classvar.should == 'class value' | |
instances[0].classvar.should == 'class value' | |
instances[1].classvar.should == 'class value' | |
instances[2].classvar.should == 'class value' | |
instances[3].classvar.should == 'class value' | |
instances[4].classvar.should == 'class value' | |
instances[5].classvar.should == 'class value' | |
instances[6].classvar.should == 'class value' | |
instances[7].classvar.should == 'class value' | |
instances[8].classvar.should == 'class value' | |
instances[9].classvar.should == 'class value' | |
instances[10].classvar.should == 'class value' | |
instances[11].classvar.should == 'class value' | |
instances[12].classvar.should == 'class value' | |
instances[13].classvar.should == 'class value' | |
instances[14].classvar.should == 'class value' | |
instances[15].classvar.should == 'class value' | |
instances[16].classvar.should == 'class value' | |
instances[17].classvar.should == 'class value' | |
instances[18].classvar.should == 'class value' | |
instances[19].classvar.should == 'class value' | |
end | |
end | |
end | |
describe "changing the class instance variable" do | |
instances.each do |i| | |
it "can be changed" do | |
i.classinstvar = 'changed value' | |
i.classinstvar.should == 'changed value' | |
end | |
end | |
it "is unique per class" do | |
instances[0].classinstvar = "value TomIncluded" | |
instances[1].classinstvar = "value TomExtended" | |
instances[2].classinstvar = "value InheritedA" | |
instances[4].classinstvar = "value InheritedB" | |
instances[6].classinstvar = "value InheritedC" | |
instances[8].classinstvar = "value InheritedD" | |
instances[0].classinstvar.should == "value TomIncluded" | |
instances[1].classinstvar.should == "value TomExtended" | |
instances[2].classinstvar.should == "value InheritedA" | |
instances[3].classinstvar.should == "value InheritedA" | |
instances[4].classinstvar.should == "value InheritedB" | |
instances[5].classinstvar.should == "value InheritedB" | |
instances[6].classinstvar.should == "value InheritedC" | |
instances[7].classinstvar.should == "value InheritedC" | |
instances[8].classinstvar.should == "value InheritedD" | |
instances[9].classinstvar.should == "value InheritedD" | |
instances[10].classinstvar.should == "value TomIncluded" | |
instances[11].classinstvar.should == "value TomExtended" | |
instances[12].classinstvar.should == "value InheritedA" | |
instances[13].classinstvar.should == "value InheritedA" | |
instances[14].classinstvar.should == "value InheritedB" | |
instances[15].classinstvar.should == "value InheritedB" | |
instances[16].classinstvar.should == "value InheritedC" | |
instances[17].classinstvar.should == "value InheritedC" | |
instances[18].classinstvar.should == "value InheritedD" | |
instances[19].classinstvar.should == "value InheritedD" | |
end | |
end | |
describe "changing the instance variable" do | |
instances.each do |i| | |
it "can be changed" do | |
i.instvar = 'changed value' | |
i.instvar.should == 'changed value' | |
end | |
end | |
it "is unique per instance" do | |
(0..19).each { |i| instances[i].instvar = "value #{i}" } | |
(0..19).each { |i| instances[i].instvar.should == "value #{i}" } | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment