Im trying to dynamically require and then include modules inside a initialize method.
# file: object.rb
class Object
attr_accessor :array
def initialize array
#array = array
#array.each do |f|
require_relative "#{f}"
include f.capitalize # the method name is the same as the filename
puts #variable # property from included method
end
end
end
object = Object.new ['a','b','c']
with these module files
# file: a.rb
module A
#variable = 'string A'
end
and so on for b and c
i keep getting an error :
`block in initialize': undefined method `include'
What am i doing wrong here and is there a better way to achieve what i'm trying to do?
The reason that you can't call include in initialize like that is that include is a method that's only defined on classes and modules, but inside of an instance method like initialize the implicit receiver is an object of your class, not the class itself.
Since you only need the methods to be available on the newly created object, you can just use extend instead of include. extend is like a per-object version of include in that it adds the methods of the given module as singleton methods to an object rather than adding them as instance methods to a module or class.
require_relative "#{f}"
Note the quotation marks. '#{f}' is not interpolated.
Related
I'm used to extending classes to provide my own encapsulated functionnalities on top of a standard class.
As I'm building a more complete object than a basic URI, I have the option to just reopen the module and add my own class or instance method.
Though it could work, I don't think it is the most elegant way for that.
Let's say I'd like to call it Location, and behave like URI but with some extra class/instance methods. Let's take a basic example, a name method:
require 'uri'
PATH = 'http://example.com/dir1/dir2/file.txt'
module Location
extend URI
class Generic
attr_reader :dummy
def name
File.basename(#path)
end
end
end
e = URI.parse PATH
puts e.path
f = Location.parse PATH
puts f.path
puts f.name
puts f.dummy = "doh"
Here, the problem is that URI module's methods like parse are not available on my now Location module:
$ undefined method `parse' for Location:Module (NoMethodError)
Same error using include URI or extend URI
What is the correct way to extend URI module the way I described above, and to add instance variables (like dummy, here) along existing ones to add functionality ?
Thanks for any tips and advice.
URI.parse is defined as a class method on a module. when you include/extend a module, only its instance methods are copied over. If you use include, they're copied over as instance methods and with extend, as class methods.
That being said, with some metaprogramming it's possible to write a method that copies class methods from one module to another:
# This returns a list of symbols (all the added methods)
def copy_class_methods(from_klass, to_klass)
from_klass.methods(false).each do |method_name|
method_obj = from_klass.method(method_name)
to_klass.define_singleton_method(method_name, &method_obj)
end
end
Then, for example:
module Location
copy_class_methods(URI, self)
end
Location.parse("http://example.com/dir1/dir2/file.txt")
# => #<URI::HTTP http://example.com/dir1/dir2/file.txt>
module Add
def addition
sum=1+2
puts sum
end
a=Add.addition
Can anyone tell me what I'm missing and why I am getting this error->
undefined method `addition' for Add:Module (NoMethodError)
You are confusing class methods and instance methods. Your definition:
module Add
def addition
...
end
end
defines methods on instances of Add whereas you called a method on the module Add. If you want to define a class/module method, you need to define like:
module Add
def self.addition
...
end
end
If you want to be able to call it directly, define it as a directly accessible method:
def self.addition
# ...
end
Or you can always rework this using:
module Add
# ...(methods)...
extend self
end
Where that will automatically promote all mixin-type methods as being directly accessible.
You can also tag them more selectively like this:
module Add
def addition
# ...
end
module_method :addition
end
That method is then available either as Add.addition or if some other module or class calls include Add.
For example I have following custom class and module:
module SimpleModule
def hello_world
puts 'i am a SimpleModule method'
end
def self.class_hello_world
puts 'i am a SimpleModule class method'
end
end
class SimpleClass
def hello_world
puts 'i am SimpleClass method'
end
def self.class_hello_world
puts 'i am a SimpleClass class method'
end
end
I tried to called those methods inside class and module by using method send
SimpleClass.send(class_hello_world) # work
SimpleClass.new.send(hello_world) # work
SimpleModule.send(class_hello_world) # work
SimpleModule.new.send(hello_world) # not work
SimpleModule.send(hello_world) # not work
In other word, I don't know how to invoke hello_world from SimpleModule. It is possible if that method is defined with self before.
I need to do this because I want to implement a "custom-include": that include all methods from module to another class.
Please tell me how to do this.
The five statements
Let's consider those five statements one at a time (but in a different order than as presented). Note that send's argument must be the name of the method expressed as a string or symbol.
SimpleModule.send("class_hello_world")
# i am a SimpleModule class method
This is normal, though such methods are normally called module methods. Some common built-in modules, such as Math, contain module methods only.
SimpleClass.send(:class_hello_world)
# i am a SimpleClass class method
Since a class is a module, the behaviour is the same as above. class_hello_world is usually referred to as a class method.
SimpleClass.new.send(:hello_world)
# i am SimpleClass method
This is the normal invocation of an instance method.
SimpleModule.send("hello_world")
#=> NoMethodError: undefined method `hello_world' for SimpleModule:Module
There is no module method hello_world.
SimpleModule.new.send(hello_world)
#=> NoMethodError: undefined method `new' for SimpleModule:Module
One cannot create an instance of a module.
include vs prepend
Suppose one wrote
SimpleClass.include SimpleModule
#=> SimpleClass
SimpleClass.new.hello_world
# i am SimpleClass method
so SimpleClass' original method hello_world is not overwritten by the module's method by the same name. Consider SimpleClass' ancestors.
SimpleClass.ancestors
#=> [SimpleClass, SimpleModule, Object, Kernel, BasicObject]
Ruby will look for hello_world in SimpleClass--and find it--before considering SimpleModule.
One can, however, use Module#prepend to put SimpleModule#hello_world before SimpleClass#hello_world.
SimpleClass.prepend SimpleModule
#=> SimpleClass
SimpleClass.new.hello_world
# i am a SimpleModule method
SimpleClass.ancestors
#=> [SimpleModule, SimpleClass, Object, Kernel, BasicObject]
Binding unbound methods
There is one other thing you do. SimpleModule's instance methods (here just one) are unbound. You could use UnboundMethod#bind to bind each to an instance of SimpleClass and then execute it using call or send.
sc = SimpleClass.new
#=> #<SimpleClass:0x007fcbc2046010>
um = SimpleModule.instance_method(:hello_world)
#=> #<UnboundMethod: SimpleModule#hello_world>
bm = um.bind(sc)
#=> #<Method: SimpleModule#hello_world>
bm.call
#=> i am a SimpleModule method
sc.send(:hello_world)
#=> i am a SimpleModule method
Module's can't have instance methods because they aren't classes. If you define an instance method in a module it is called a mixin. These "mixins" can be included in another class and are then available for use.
The full explanation is here in the docs
Edit:
For example, you could do something like this:
module SimpleModule
def hello_world
p 'hello world'
end
end
class Example
include SimpleModule
end
Example.new.send(:hello_world)
This is one way to call the mixin of a module.
Since you seem to want to create your own version of include.
Take a look at Module.append_features documentation
When this module is included in another, Ruby calls append_features in
this module, passing it the receiving module in mod. Ruby's default
implementation is to add the constants, methods, and module variables
of this module to mod if this module has not already been added to mod
or one of its ancestors. See also Module#include.
So this is what you have to do if you want to rewrite include yourself.
And since you're adventurous, maybe you will enjoy reading Ruby's source code to see how exactly this is implemented internally. See https://github.com/ruby/ruby...class.c#L853:L934
You also can use constantize:
def call_method(method)
"SimpleModule::#{method}".constantize
end
Please explain why self is used in def self.included (klass) below.
module A
def self.included(klass)
puts "A -> #{klass}"
puts A
puts self
end
end
class X
include A
end
By writing def self.included you are defining a method that is part of the singleton class of module A. In general, singleton methods can only be called by doing A.included() but this singleton method has a special name included that causes the Ruby interpreter to call when the module gets included in to a class.
A normal method in a module (defined with def foo) can only be called if the module gets included in to something else.
This is how you declare a module method that can be called directly. Normally methods defined within a module are only usable if another class or module includes them, like class X in this example.
module Example
def self.can_be_called
true
end
def must_be_included
true
end
end
In this case you will see these results:
Example.can_be_called
# => true
Example.must_be_included
# => NoMethodError: undefined method `must_be_included' for Example:Module
The self declared methods are not merged in to the classes or modules that include it, though. They are special-purpose that way.
I have a Ruby module for constants. It has a list of variables and one method which applies formatting.
I can't seem to access the method in this module. Any idea why?
If you include the module the method becomes an instance method but if you extend the module then it becomes a class method.
module Const
def format
puts 'Done!'
end
end
class Car
include Const
end
Car.new.format # Done!
Car.format # NoMethodError: undefined method format for Car:Class
class Bus
extend Const
end
Bus.format # Done!
Bus.new.format # NoMethodError: undefined method format
module Foo
def self.hello # This is a class method
puts "self.hello"
end
def hello # When you include this module, it becomes an instance method
puts "hello"
end
end
Foo.hello #=> self.hello
class Bar
include Foo
end
Bar.new.hello #=> hello
Generally, with modules, these things should be happening :
Autoload path in application.rb, add:
config.autoload_paths += %W(#{config.root}/lib)
Place module in /lib
Include module with include NAMEOFMODULE
If the module name has an underscore like "game_engine", you need to use include GameEngine