Here is my code:
module Star
def Star.line
puts '*' * 20
end
end
module Dollar
def Star.line
puts '$' * 20
end
end
module At
def line
puts '#' * 20
end
end
include At
Dollar::line # => "####################"
Star::line # => "$$$$$$$$$$$$$$$$$$$$"
Dollar::line # => "####################"
line # => "####################"
Can anyone explain how I get this result? I do not understand the method lookup flow here.
This is how I see it:
Dollar::line
There is no such method defined in this module so It's calling At::line because you included this module.
Star::line
It uses last defining from Dollar module(it goes after original Star definition so it's overridden).
Dollar::line
Third call is the same as the first one.
line
And the last one is At::line because You made an include.
Is
module Dollar
def Star.line
intentional or is a typo?
Looks like Dollar.line is not defined, and the method line in At is used instead.
First you need to understand that Ruby looks up constants somewhat similarly to methods. It starts by looking for the constant in the current lexical scope. If it doesn't find the constant there, it goes up one level and looks there, and so on. If it can't find the constant anywhere else, it eventually searches the top level, which is why you can access modules like Kernel from anywhere in your code.
module Star
end
Star.object_id # 20
module Dollar
Star.object_id # 20. No Star in current scope, so gets the top-level star
end
module At
module Star
end
Star.object_id # 10. There is now a Star in this scope, so we don't get the top-level one
end
The next thing to understand is that methods defined at the top level in Ruby are made instance methods of Object. Since everything in Ruby is an instance of Object, such methods can always be called.
Finally, consider what include does: it takes instance methods from a module and makes them instance methods in the current scope. So if you include something at the top level, all of those methods get added to Object!
So your code is essentially equivalent to this:
module Star
def self.line
puts '*' * 20
end
# this overwrites the previous definition
def self.line
puts '$' * 20
end
end
# because of the way constants are looked up, the def ends up in Star
module Dollar
end
module At
def line
puts '#' * 20
end
end
# the include does this, so now every object (including Dollar) can call line
def line
puts '#' * 20
end
# except Star already has its own line method, so the one from Object won't be called for it
Star.line # "$$$$$$$$$$$$$$$$$$$$"
Related
I'm working on dynamically patching a bunch of classes and methods(most of the time these methods are not simple "puts" like a lot of examples I've been able to find on the internet)
Say for instance I have the following code:
foo.rb
module Base
class Foo
def info
puts 'Foo#info called'
end
end
end
& I also have the following class:
test.rb
module Base
class Test
def print
puts "Test#print called"
Foo.new.info
end
end
end
Then in main.rb I have the following where I want to add a method that uses a class within the same module(Foo in this case)
require_relative './foo'
require_relative './test'
new_method_content = "puts 'hi'
Foo.new.info"
Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
Which, when executed will net the following:
Uncaught exception: uninitialized constant Foo
Which sort of makes sense to me because the main.rb file doesn't know that I want Base::Foo, however, I need a way to maintain lookup scope because Base::Test should be able to find the class Foo that I want.
Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
I've done a fair bit of googling and SO'ing but haven't found anything about how to maintain constant lookup scope while class_eval/instance_eval/module_eval/define_method(I've tried a lot of Ruby's dark magic methods all of which have ended in varying degrees of failure lol)
https://cirw.in/blog/constant-lookup
Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).
& also this:
https://bugs.ruby-lang.org/issues/6838
Evaluates the string or block in the context of mod, except that when
a block is given, constant/class variable lookup is not affected.
So my question is:
How can I redefine a method BUT maintain constant/class scope?
I've been trying a bunch of other things(in the context of main.rb):
Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
Base::Test.new.asdf
=>
Test#asdf called
Uncaught exception: uninitialized constant Base::Test::Foo
Did you mean? Base::Foo
(which is a diff problem in that it's trying to look it up from the evaluated module nesting? I'm not sure why it doesn't try all module paths available from Base::Test though, I would think it would try Base::Test::Foo which doesn't exist, so then it would go up the module tree looking for the class(Base::Foo) which would exist)
When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.
But you could do it in this way:
module Base
Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
end
module Access
def last
self[-1]
end
def start_end
self[0] + last
end
end
module StringExt
refine String do
include Access
end
end
using StringExt
puts 'abcd'.last # => d
puts 'abcd'.start_end
When a class being refined with too many connected methods, I think it is better to extract them to a module. However, in above example which demos a problem when one method calls another(see the last statement), and it produces following error.
in 'start_end': undefined local variable or method 'last' for "abcd":String (NameError)
Similar issue was solved using a global variable, which also works for my example. But I'm seeking another better way to organize inter-called methods being refined and avoid a global thing.
How would advice a better way to organize those methods?
Here's a general pattern I ended up using. Basically I found no workaround for using global identifiers at some level. But this can be done fairly cleanly by making those globals classes/modules. This will be more clear as an example:
module StringPatches
def self.non_empty?(string)
!string.empty?
end
def non_empty?
StringPatches.non_empty?(self)
end
def non_non_empty?
!StringPatches.non_empty?(self)
end
refine String do
include StringPatches
end
end
class Foo
using StringPatches
puts "asd".non_empty? # => true
puts "asd".non_non_empty? # => false
end
The class methods on StringPatches don't get exported to using. But since classes/modules are constants (globals) they can be accessed from anywhere.
Inside the body of a class, I'd like to pass a block to a method called with. For the lifetime of the block, I would like a with_value method to be available.
Otherwise, everything inside the block should behave as if it were outside the block.
Here's an example:
class C
extend M
with "some value" do
do_something_complicated
do_something_complicated
do_something_complicated
end
end
We can almost get this with:
module M
def with(str, &block)
Object.new.tap do |wrapper|
wrapper.define_singleton_method :with_value do # Here's our with_value
str # method.
end
end.instance_eval &block
end
def do_something_complicated # Push a value onto an
(#foo ||= []).push with_value # array.
end
end
but there's a problem: since we're evaluating the block passed to with inside the context of a different object, do_something_complicated isn't available.
What's the right way to pull this off?
This will make with_value available only within the block. However, _with_value will be defined within or outside of the block.
module M
def _with_value
...
end
def with(str, &block)
alias with_value _with_value
block.call
undef with_value
end
...
end
I cannot tell from the question whether this is a problem. If it is a problem, you need to further describe what you are trying to do.
Basically, the idea is to use method_missing to forward method calls from the dummy class to the calling class. If you also need to access instance variables, you can copy them from the calling class to your dummy class, and then back again after the block returns.
The Ruby gem docile is a very simple implementation of such a system. I suggest you read the source code in that repository (don't worry, it's a very small codebase) for a good example of how DSL methods like the one in your example work.
Here is a way that is closer to your attempt:
module M
def with(str, &block)
dup.tap do |wrapper|
wrapper.define_singleton_method :with_value do
...
end
end.instance_eval &block
end
...
end
dup will duplicate the class from where with is called as a class method.
Documentation I've read tells me to use Module.method to access methods in a module. However, I can use Module::method as well. Is this syntactic sugar, or am I confused?
module Cat
FURRY_LEVEL = 4
def self.sound
%w{meow purr hiss zzzz}.sample
end
end
puts Cat.sound # This works.
puts Cat::sound # This also works. Why?!
puts Cat.FURRY_LEVEL # Expected error occurs here.
puts Cat::FURRY_LEVEL # This works.
Constant resolution always requires that you use ::.
Method invocation is idiomatically and usually a period (.), but :: is also legal. This is not just true for so-called module methods, but for invoking any method on any object:
class Foo
def bar
puts "hi"
end
end
Foo.new::bar
#=> hi
It's not so much "syntax sugar" as it is simply alternative syntax, such as the ability to write if or case statements with either a newline, then and newline, or just then.
It is specifically allowed because Ruby allows methods with the same name as a constant, and sometimes it makes sense to think that they are the same item:
class Foo
class Bar
attr_accessor :x
def initialize( x )
self.x = x
end
end
def self.Bar( size )
Foo::Bar.new( size )
end
end
p Foo::Bar #=> Foo::Bar (the class)
p Foo::Bar(42) #=> #<Foo::Bar:0x2d54fc0 #x=42> (instance result from method call)
You see this commonly in Ruby in the Nokogiri library, which has (for example) the Nokogiri::XML module as well as the Nokogiri.XML method. When creating an XML document, many people choose to write
#doc = Nokogiri::XML( my_xml )
You see this also in the Sequel library, where you can write either:
class User < Sequel::Model # Simple class inheritance
class User < Sequel::Model(DB[:regular_users]) # Set which table to use
Again, we have a method (Sequel.Model) named the same as a constant (Sequel::Model). The second line could also be written as
class User < Sequel.Model(DB[:regular_users])
…but it doesn't look quite as nice.
The :: is called scope resolution operator, which is used to find out under what scope the method, class or constant is defined.
In the following example, we use :: to access class Base which is defined under module ActiveRecord
ActiveRecord::Base.connection_config
# => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"}
We use :: to access constants defined in module
> Cat::FURRY_LEVEL
=> 4
> Cat.FURRY_LEVEL
=> undefined method `FURRY_LEVEL' for Cat:Module (NoMethodError)
The . operator is used to call a module method(defined with self.) of a module.
Summary: Even though both :: and . does the same job here, it is used for different purpose. You can read more from here.
Was there a hook in ruby that is called every time the value of a certain variable changes?
If you write a C extension for Ruby, you can actually make a global variable that triggers a setter hook whenever someone sets it.
But you probably don't want to do that because you'd have to write some C and it could be a pain to manage that.
A better strategy would be to make it so that the variable is read and set through appropriate methods. Then when the setter method is called you can do whatever you want. Here is an example that encapsulates a variable inside an object:
class Foo
def bar=(v)
#bar = v
# do some stuff
end
def bar
#bar
end
end
Similarly you could encapsulate the variable in a module or class instead of an object.
Preamble: I this is not a solution (pst already wrote: There is none), but perhaps it can help in special situations.
My first idea was to use freeze to get a solution:
a = "aa"
a.freeze
a << 'b' #can't modify frozen string (RuntimeError)
Now we must redefine freeze and we get a hint, when the variable changes the value:
module FreezeWarning
def freeze
puts "#{self}: I am changed"
end
end
a = "aa"
a.extend(FreezeWarning)
a.freeze
a << 'b' #aa: I am changed
First problem: There is no way to get the variable name.
You may solve this with an additional variable (You can define your own variable identification, it must not be the name)
module FreezeWarning
def change_warning(name)
#varname = name
self.freeze
end
def freeze
puts "<#{#varname}> (#{self}): I am changed"
end
end
a = "aa"
a.extend(FreezeWarning)
a.change_warning('a')
a << 'b' #<a> (aa): I am changed
But the bigger problem: This works only with changes of the value, not with new assignments:
a = 5
a.freeze
a = 4
p a # -> 4
So this is only a very restricted 'solution'.