Understanding namespaces in Ruby - ruby

In the code below:
::Trace.tracer = ::Trace::ZipkinTracer.new()
what is the relation between Trace and ZipkinTracer?

ZipkinTracer is inside of Trace namespace, like this:
module Trace
class ZipkinTracer
# ...
end
end
The :: before constant name means that you point to the root. For example in the following code:
class Class1
end
module Module1
class Class1
end
def foo
::Class1
end
end
::Class1 ensures that you refer to the "root" Class1. If you had:
def foo
Class1
end
the Module1::Class1 would be referred.

This code does the following thing. First, it instantiates class ZipkinTracer:
new_instance = Trace::ZipkinTracer.new()
Then, it calls #tracer= method of the Trace module:
Trace.tracer=( new_instance )
Ruby syntax allows this to be rewritten as
Trace.tracer = new_instance
In this case, no assignment is happening, but a method ending in = is called. Methods ending in = are allowed in Ruby, used generally for attribute assignment, and they are special in that they always return the assigned value (that is, their argument), regardless of what other return value you might be trying to prescribe:
class Foo
def bar=( value )
puts "Method #bar= called!"
#bar = value
puts "Trying to return Quux!"
return "Quux!"
end
def bar; #bar end
end
foo = Foo.new
foo.bar #=> nil
foo.bar = "Baz!"
#=> Method #bar= called!
#=> Trying to return Quux!
#=> "Baz!" -- attempt to explicitly return "Quux!" failed
foo.bar #=> "Baz!"

Related

How to call instance variables from one function to another function in same class?

Actually i am new to the oops concepts in ruby, here i want to call the instance variables from one function to another function in same class.but im getting undefined method for second function "one_object"
module User
class Object
class << self
def add_object(items)
#data={}
#data.merge!(items)
end
# i want to use above #data = {} for next one_object method after adding items to the add_object method
def one_object
#data.merge!({"ext"=>"1.0"})
end
end
end
end
a = User::Object.add_object({"txt"=>"text_file","csv"=>"csv_file"})
p a.one_object
Expected Output:
{"txt"=>"text_file", "csv"=>"csv_file", "ext"=>"1.0"}
In Ruby, Object is the root class of all objects. Although you can have your own Object class within User, it could cause a lot of confusion.
Let's simplify your problem by removing the User module (it's not relevant to the example) and by renaming Object to Foo (you'll find a better name). To initialize instance variables you can use the initialize method which is invoked by default every time you construct an object via new:
class Foo
def initialize
#data = {}
end
end
foo = Foo.new
#=> #<Foo:0x00007fb551823d78 #data={}>
# ^^^^^^^^
That hash you assign to #data will be shared among all instance methods. In addition, each Foo instance will have its own #data hash. To merge! another hash into it, you can use:
class Foo
def initialize
#data = {}
end
def add(hash)
#data.merge!(hash)
end
end
foo = Foo.new
#=> #<Foo:0x00007fbc80048230 #data={}>
foo.add({"abc"=>123})
#=> {"abc"=>123}
foo.add({"def"=>456})
#=> {"def"=>456}
foo
#=> #<Foo:0x00007fbc80048230 #data={"abc"=>123, "def"=>456}>
In order to chain multiple add calls (a so-called fluent interface), you have to return self from within the method:
class Foo
def initialize
#data = {}
end
def add(hash)
#data.merge!(hash)
self # <- like this
end
end
foo = Foo.new
#=> #<Foo:0x00007ff7408003d8 #data={}>
foo.add({"abc"=>123}).add({"def"=>456})
#=> #<Foo:0x00007ff7408003d8 #data={"abc"=>123, "def"=>456}>
Finally, to add static data, you could simply call your own method:
class Foo
def initialize
#data = {}
end
def add(hash)
#data.merge!(hash)
self
end
def add_more
add({"more" => 789})
end
end
foo = Foo.new
#=> #<Foo:0x00007f99b20f8590 #data={}>
foo.add({"abc"=>123}).add({"def"=>456}).add_more
#=> #<Foo:0x00007f99b20f8590 #data={"abc"=>123, "def"=>456, "more"=>789}>
You assign the result of add_object to a (i.e. your a is now a Hash), but next you are going to call one_object on a (but one_object is part of User::Object and not of your Hash instance).
If you add to add_object another line, containing self, you will receive your expected output.
With the change, add_object will return User::Object, and you won't run into your initial issue.

What is the double colon before module names in Ruby stand for? [duplicate]

In the code below:
::Trace.tracer = ::Trace::ZipkinTracer.new()
what is the relation between Trace and ZipkinTracer?
ZipkinTracer is inside of Trace namespace, like this:
module Trace
class ZipkinTracer
# ...
end
end
The :: before constant name means that you point to the root. For example in the following code:
class Class1
end
module Module1
class Class1
end
def foo
::Class1
end
end
::Class1 ensures that you refer to the "root" Class1. If you had:
def foo
Class1
end
the Module1::Class1 would be referred.
This code does the following thing. First, it instantiates class ZipkinTracer:
new_instance = Trace::ZipkinTracer.new()
Then, it calls #tracer= method of the Trace module:
Trace.tracer=( new_instance )
Ruby syntax allows this to be rewritten as
Trace.tracer = new_instance
In this case, no assignment is happening, but a method ending in = is called. Methods ending in = are allowed in Ruby, used generally for attribute assignment, and they are special in that they always return the assigned value (that is, their argument), regardless of what other return value you might be trying to prescribe:
class Foo
def bar=( value )
puts "Method #bar= called!"
#bar = value
puts "Trying to return Quux!"
return "Quux!"
end
def bar; #bar end
end
foo = Foo.new
foo.bar #=> nil
foo.bar = "Baz!"
#=> Method #bar= called!
#=> Trying to return Quux!
#=> "Baz!" -- attempt to explicitly return "Quux!" failed
foo.bar #=> "Baz!"

How can I call a class method without the class?

I have a homework that has to accomplish something similar:
module Foo
def self.bar
yield
end
def helper (number)
p number
end
end
Foo.bar do
helper 5
end
Which of course gives an error, because 'helper' is not defined in Object. But in the task, it says straightforward that Foo has to be used this way:
Foo.bar do
helper 5
end
It does not say where 'helper' is defined though. How can I call this method like this above?
Write code as below :
module Foo
def self.bar
yield
end
end
def helper (number)
p number
end
Foo.bar do
helper 5
end
Put the method helper on the top level. Then helper will become a private instance method of the class Object. You are passing a block to the call Foo.bar, and block being a closure, have the access to its surroundings. So helper 5 will called implicitly by main, top level Object class instance. Now the code will work.
Another way is using Module#include method in the top level to include the Foo module to the class Object.
module Foo
def self.bar
yield
end
def helper (number)
p number
end
end
# this will make available `helper` method as an instance method to the Object class.
include Foo
Foo.bar do
helper 5
end
You could use instance_eval to evaluate the block in the context of your object. Something like this:
class Foo
def bar(&block)
instance_eval(&block)
end
def helper(number)
p number
end
end
Foo.new.bar do
helper 5
end
You could also make bar a class method and call:
class Foo
def self.bar(&block)
new.instance_eval(&block)
end
# ...
end
Foo.bar { helper 5 }
Or returning the instance:
class Foo
def self.bar(&block)
new.tap { |foo| foo.instance_eval(&block) }
end
# ...
end
foo = Foo.bar { helper 5 }

Use method in different context, instance_eval adds unwanted argument

I'm trying to call a method of an object foo as if it was an method of object bar. I tried two approaches:
1. unbind and bind - fails because of different classes
class Foo
def initialize
#name = "John"
end
end
class Bar
def out
puts #name
end
end
foo = Foo.new
bar = Bar.new
m = bar.method :out
foo.instance_eval m.unbind.bind(foo)
2. instance_eval on proc made from method
This fails on the fact that instance_eval passes a reciever as an additional argument instead of the real reciever (afaik)
class Foo
def initialize
#name = "John"
end
end
class Bar
def out
puts #name
end
end
foo = Foo.new
bar = Bar.new
m = bar.method :out
proc = m.to_proc
foo.instance_eval &proc
It says: in `out': wrong number of arguments (1 for 0) (ArgumentError) in the stacktrace.
However when I use this instead of the last line it works fine:
foo.instance_eval {
puts #name
}
The problem is that #instance_eval sends to the block a parameter that is the object it self. So you can do it:
# ...
class Bar
def out(foo_object)
[#name, foo_object, self]
end
end
# ...
m = bar.method :out
foo.instance_eval &m # => ["John", #<Foo:0x1c11b10>, #<Bar:0x1bb2470>]
The argument is place where the method is called and self is from here the method is. I don't know how to call the method without parsing this extra argument.

Ruby - How to use the method parameter as the name of the variable?

How would I use the parameter value as the instance variable name of an object?
This is the object
Class MyClass
def initialize(ex,ey)
#myvar = ex
#myothervar = ey
end
end
I have the following method
def test(element)
instanceofMyClass.element #this obviously doesnt work
end
How can I have the test method return either myvar or myothervar value depending on the element parameter. I don't want to write an if condition though, I want to pass myvar or myother var via element to the object instance if possible.
def test(element)
instanceofMyClass.send(element.to_sym)
end
You'll get a missing method error if instanceofMyClass doesn't respond to element.
def test(element)
instanceofmyclass.instance_variable_get element
end
test :#myvar # => ex
test :#myothervar # => ey
I like the simplicity of send(), though one bad thing with it is that it can be used to access privates. The issue is still remains solution below, but at least then it's explicitly specified, and reader can see which methods are to be forwarded. The first one just uses delegation, while the second one uses more dynamic way to define methods on the fly.
require 'forwardable'
class A
extend Forwardable
def_delegators :#myinstance, :foo, :bar
class B
def foo
puts 'foo called'
end
def bar
puts 'bar called'
end
def quux
puts 'quux called'
end
def bif
puts 'bif called'
end
end
def initialize
#myinstance = B.new
end
%i(quux bif).each do |meth| # note that only A#quux and A#bif are defined dynamically
define_method meth do |*args_but_we_do_not_have_any|
#myinstance.send(meth)
end
end
end
a = A.new
a.foo
a.bar
a.quux
a.bif

Resources