How do you access an instance variable within a mixin method? I can think of 2 ways, but both seem problematic.
Have the mixin method access the instance variable directly as any class method would, e.g self.text. Problem with this is that it places restrictions on where the mixin method can be used, and forces the class doing the mixing to have a particular instance method named in a particular way.
Pass the instance variable as a parameter to the mixin method, which would result in code like this:
example
self.do_something(self.text)
or
#thing.do_something(#thing.text)
which looks nasty to me, and doesn't conform to the principles of object orientation.
Is there any other way to do it?, am I right to be concerned?
In general, avoid having mixins access member variables: It's a very tight form of coupling that can make future refactoring unnecessarily difficult.
One useful strategy is for the Mixin to always access variables via accessors. So, instead of:
#!/usr/bin/ruby1.8
module Mixin
def do_something
p #text
end
end
class Foo
include Mixin
def initialize
#text = 'foo'
end
end
Foo.new.do_something # => "foo"
the mixin accesses the "text" accessor, which is defined by the including class:
module Mixin
def do_something
p text
end
end
class Foo
attr_accessor :text
include Mixin
def initialize
#text = 'foo'
end
end
Foo.new.do_something # => "foo"
What if you need to include the Mixin in this class?
class Foo
def initialize
#text = "Text that has nothing to do with the mixin"
end
end
Using generic and common data names in mixins can lead to conflicts when the including class uses the same name. In that case, have the mixin look for data with a less common name:
module Mixin
def do_something
p mixin_text
end
end
and let the including class define the appropriate accessor:
class Foo
include Mixin
def initialize
#text = 'text that has nothing to do with the mixin'
#something = 'text for the mixin'
end
def mixin_text
#something
end
end
Foo.new.do_something # => "text for the mixin"
In this way, the accessor acts as sort of "impedance matcher" or "translator" between the mix-in's data and the including class's data.
Instance variable names start in ruby with an # eg. #variable. You can acces them with this name from a Module you include
module M
def t
#t
end
end
class A
include M
def initialize(t)
#t= t
end
end
A.new(23).t # => 23
If you wan't to access #t when it's not defined in your class before you can do it this way
module M
def t
instance_variable_defined?("#t") ? #t : nil
end
end
You can provide this instance method yourself in this module, but you have to be careful not to overwrite existing method
Example (in module you are mixing in):
def text
#text ||= ""
end
Related
I am trying to define a DSL where some constants are defined within a block and must be copied into a fresh made module. I got this so far:
class Foo
def self.macros(&block)
mod = Module.new do
module_eval &block
end
self.const_set(:Macros, mod)
end
macros do
Point = Struct.new :x, :y
VALUE = 5
def self.bar
"bar"
end
def foo
"foo"
end
end
end
With the code above I managed to get Foo::Macros.bar however the constants are missing.
How can I get the constants defined within the block?
I want to access them through the new module embedded inside the class, like Foo::Macros::Point
Ruby constant lookup rules doesn't change with class_eval or module_eval, so you cannot make a constant defined in a block in Foo be part of Foo::Macros, sadly.
Simply:
Foo::Value
Foo::Point
Foo::Macros is just an alias for the anonymous module you defined, it doesn't change the scope of object defined.
If you define the module first, you can access it using const_get:
module Test
end
Test.module_eval do
ANSWER = 42
end
Test.const_get('ANSWER')
=> 42
Is there any clean way to initialize instance variables in a Module intended to be used as Mixin? For example, I have the following:
module Example
def on(...)
#handlers ||= {}
# do something with #handlers
end
def all(...)
#all_handlers ||= []
# do something with #all_handlers
end
def unhandled(...)
#unhandled ||= []
# do something with unhandled
end
def do_something(..)
#handlers ||= {}
#unhandled ||= []
#all_handlers ||= []
# potentially do something with any of the 3 above
end
end
Notice that I have to check again and again if each #member has been properly initialized in each function -- this is mildly irritating. I would much rather write:
module Example
def initialize
#handlers = {}
#unhandled = []
#all_handlers = []
end
# or
#handlers = {}
#unhandled = []
# ...
end
And not have to repeatedly make sure things are initialized correctly. However, from what I can tell this is not possible. Is there any way around this, besides adding a initialize_me method to Example and calling initialize_me from the extended Class? I did see this example, but there's no way I'm monkey-patching things into Class just to accomplish this.
module Example
def self.included(base)
base.instance_variable_set :#example_ivar, :foo
end
end
Edit: Note that this is setting a class instance variable. Instance variables on the instance can't be created when the module is mixed into the class, since those instances haven't been created yet. You can, though, create an initialize method in the mixin, e.g.:
module Example
def self.included(base)
base.class_exec do
def initialize
#example_ivar = :foo
end
end
end
end
There may be a way to do this while calling the including class's initialize method (anybody?). Not sure. But here's an alternative:
class Foo
include Example
def initialize
#foo = :bar
after_initialize
end
end
module Example
def after_initialize
#example_ivar = :foo
end
end
Perhaps this is a little hacky, but you can use prepend to get the desired behavior:
module Foo
def initialize(*args)
#instance_var = []
super
end
end
class A
prepend Foo
end
Here is the output from the console:
2.1.1 :011 > A.new
=> #<A:0x00000101131788 #instance_var=[]>
modules provides hooks, as Module#included. I suggest you check out ruby doc on the topic, or use ActiveSupport::Concern, which provides some helpers on modules.
I think there may be a simpler answer to this. The module should have an initializer that initialises the variables as you normally would do. In the initializer for the class that includes the module, invoke super() to invoke the initializer in the included module. This is simply following the method dispatch rules in Ruby.
On reflection, this will not work so well if the class including the module also has a superclass that needs to be initialised. The initializer in the module would need to accept a variable parameter list and pass this up to the superclass. It looks like a good avenue to explore though.
I am trying to build a simple little template parser for self-learning purposes.
How do I build something "modular" and share data across it? The data doesn't need to be accessible from outside, it's just internal data. Here's what I have:
# template_parser.rb
module TemplateParser
attr_accessor :html
attr_accessor :test_value
class Base
def initialize(html)
#html = html
#test_value = "foo"
end
def parse!
#html.css('a').each do |node|
::TemplateParser::Tag:ATag.substitute! node
end
end
end
end
# template_parser/tag/a_tag.rb
module TemplateParser
module Tag
class ATag
def self.substitute!(node)
# I want to access +test_value+ from +TemplateParser+
node = #test_value # => nil
end
end
end
end
Edit based on Phrogz' comment
I am currently thinking about something like:
p = TemplateParser.new(html, *args) # or TemplateParser::Base.new(html, *args)
p.append_css(file_or_string)
parsed_html = p.parse!
There shouldn't be much exposed methods because the parser should solve a non-general problem and is not portable. At least not at this early stage. What I've tried is to peek a bit from Nokogiri about the structure.
With the example code you've given, I'd recommend using composition to pass in an instance of TemplateParser::Base to the parse! method like so:
# in TemplateParser::Base#parse!
::TemplateParser::Tag::ATag.substitute! node, self
# TemplateParser::Tag::ATag
def self.substitute!(node, obj)
node = obj.test_value
end
You will also need to move the attr_accessor calls into the Base class for this to work.
module TemplateParser
class Base
attr_accessor :html
attr_accessor :test_value
# ...
end
end
Any other way I can think of right now of accessing test_value will be fairly convoluted considering the fact that parse! is a class method trying to access a different class instance's attribute.
The above assumes #test_value needs to be unique per TemplateParser::Base instance. If that's not the case, you could simplify the process by using a class or module instance variable.
module TemplateParser
class Base
#test_value = "foo"
class << self
attr_accessor :test_value
end
# ...
end
end
# OR
module TemplateParser
#test_value = "foo"
class << self
attr_accessor :test_value
end
class Base
# ...
end
end
Then set or retrieve the value with TemplateParser::Base.test_value OR TemplateParser.test_value depending on implementation.
Also, to perhaps state the obvious, I'm assuming your pseudo-code you've included here doesn't accurately reflect your real application code. If it does, then the substitute! method is a very round about way to achieve simple assignment. Just use node = test_value inside TemplateParser::Base#parse! and skip the round trip. I'm sure you know this, but it seemed worth mentioning at least...
Please help me out.
I need to use the same bunch of attributes in many classes. I would suggest to create module with predefined attributes and extend this module in every class
module Basic
#a=10
end
class Use
extend Basic
def self.sh
#a
end
end
puts Use.sh
but the output is empty. It seems like I missed something.
Maybe there is a better way to do that?
Your thoughts?
It's all about the self:
module Basic
#a=10
end
has self evaluating to Basic. You want it to evaluate to Use when the latter is extended:
module Basic
# self = Basic, but methods defined for instances
class << self
# self = Basic's eigenclass
def extended(base)
base.class_eval do
# self = base due to class_eval
#a=10
end
end
end
end
class Use
# self = Use, but methods defined for instances
extend Basic # base = Use in the above
class << self
# self = Use's eigenclass
def sh
#a
end
end
end
Use.sh # 10
What you're describing is the Flyweight design pattern. While some view this as rarely used in ruby ( http://designpatternsinruby.com/section02/flyweight.html ), others provide an implementation ( http://www.scribd.com/doc/396559/gof-patterns-in-ruby page 14 )
Personally, what I would do is to put all these attributes into a yaml file, and parse them either into a global variable:
ATTRIBUTES = YAML.load_file(File.expand_path('attributes.yml', File.dirname(FILE))
or a class method (with caching here, assuming you won't change the yml file while the app is running and need the new values). I'd suggest using ActiveSupport::Concern here as it's easier to read than the traditional way of mixing in class methods:
module Basic
extend ActiveSupport::Concern
module ClassMethods
def attributes_file
File.expand_path('attributes.yml', File.dirname(__FILE__))
def attributes
#attributes ||= YAML.load_file(attributes_file)
#attributes
end
end
module InstanceMethods
# define any here that you need, like:
def attributes
self.class.attributes
end
end
end
You can define methods for each of the attributes, or rely on indexing into the attributes hash. You could also get fancy and define method_missing to check if an attribute exists with that name, so that you don't have to keep adding methods as you want to add more attributes to the shared configs.
In what sort of situation is the code:
module M
extend self
def greet
puts "hello"
end
end
more beneficial to use over say something like:
module M
def self.greet
puts "hello"
end
end
In the top, one is an instance method being extended, and the latter is just a class method, but when calling either method, you'd have to M.greet , right? I was just curious if anyone could shed some light on when to use one code over the other. Thanks!
The first example is typically a way people achieve the functionality of module_function (when they do not know the existence of this method).
A module_function is both an instance method and a class method. In your second code example the method is just a class method.
It would be possible to do this with your first example, but not your second:
include M
greet
A module can be used as a namespace by writing module methods, and a module's instance methods can be mixed into another object.
The self-extending module concept allows a module to be used in both ways; either as a stand-alone namespace or as a mixin. Consider this module:
module M
def bar
puts "bar"
end
end
class C
include M
end
It has an instance method and can be mixed in to another object. It does not have a module method and cannot, therefore be used as a namespace:
puts M::bar # => undefined method `bar' for M:Module
puts C.bar # => this is bar
But, a module is an just an object of class Module, as we can demonstrate
puts M.class # => Module
This means that we can do something crazy. We can mix a module into itself so that its methods become both instance and module methods.
module M
extend self
def bar
puts "bar"
end
end
puts M::bar # => this is bar
puts C.bar # => this is bar