I'm trying to write a program that dynamically defines ruby classes based on configuration read from a file. I know I can use Class.new to do this. Here's an example program:
x = [1,2,3]
Test = Class.new do
##mylist = x
def foo
puts ##mylist
end
end
Test.new.foo
When I run this I get the following output (running with ruby 1.9.3p0):
c:/utils/test.rb:4: warning: class variable access from toplevel
c:/utils/test.rb:7: warning: class variable access from toplevel
1
2
3
Does anyone know what causes these warnings and how I can get rid of them?
I've tried replacing the line tjhat does
##mylist = x
with this
class_variable_set(:##mylist, x)
But when I do that I get this error instead:
c:/utils/test.rb:7: warning: class variable access from toplevel
c:/utils/test.rb:7:in `foo': uninitialized class variable ##mylist in Object (NameError)
from c:/utils/test.rb:11:in `'
Thanks in advance!
This isn't doing what you think it's doing. Since you're not creating a class with the class keyword, your class variable is being set on Object, not Test. The implications of this are pretty huge, which is why Ruby is warning you. Class variables are shared between ancestors, and objects usually inherit from Object.
Just to remove this warning, you should use class_variable_set method:
x = [1,2,3]
Test = Class.new do
class_variable_set(:##mylist, x)
def foo
puts ##mylist
end
end
Rather than defining your "mylist" class variable on the class when declaring the class, you can declare class level variables on it later on as below. Two different methods are shown. The former only works in 1.9, the latter works in both versions, but is less idiomatic.
x = [1,2,3]
Test = Class.new do
def foo
puts ##mylist
end
end
# ruby 1.9.2
Test.class_variable_set(:##mylist, x)
# ruby 1.8.7
Test.class_eval {
##mylist = x
}
Test.new.foo
if you want only to suppress this warnings you can use
$VERBOSE = nil
in top of you code
Related
This part works:
class Example1
##var1= "var1 in the Example1"
def get_var1
##var1
end
end
example1 = Example1.new
example1.get_var1
# => "var1 in the Example1"
but if I try eigenclass:
def example1.get_var1
##var1
end
example1.get_var1
# NameError: uninitialized class variable ##var1 in Object
# from (pry):128:in `get_var1'
Ruby looks ##var1 in the Object instead of the Example.
I have tested this code in the Ruby 1.9.3 and 2.0 with the same result.
Why does it happening?
The second thing, can we turn it off (so example.get_var1 won't look for class variables in the Object)?
It appears as though the lexical scope for class variable lookup is kind of wacky. As near as I can tell, because you're not inside the
class Example1
end
block, ruby doesn't look up ##var in your class, but rather from Object. If you want it explicitly from your class, you can do:
def example1.get_var
self.class.class_variable_get(:##var1)
end
I stumbled across https://www.ruby-forum.com/topic/1228428 while searching for the answer. They're talking about 1.8.7, but it appears to apply to later versions as well.
I'm trying to use reflective methods in Ruby, and running into a behavior that I find really surpising.
The following examples seems to work differently in IRB and when called a ruby script:
Example 1:
def myfun; end
p respond_to?(:myfun)
In IRb, this says 'true',
In script: 'false'.
Example 2:
ml = methods
def myfun; end
p methods - ml
In IRb, this says [:myfun].
In script: [].
I found this under 1.8, 1.9 MRI, JRuby 1.5.6, etc - so I assume this is normal.
Why is the difference?
I was pretty sure 'respond_to?' is the way to see if a method is available - why is that not working in the above case?
This function - method on "main" object - is defined as private in ruby script.
You can check this easily:
ml = private_methods
def myfun; end
p private_methods - ml #=> [:myfun]
p respond_to?(:myfun, true) #=> true
If you call it explicitly on self you will get an error:
self.myfun
# NoMethodError: private method ‘myfun’ called for main:Object
On the other hand, in IRB your method is defined as public. Under the hood it looks something like this:
class Object
def irb_binding
# this is where your entered code is evaluated
def myfun; :ok; end # you can define methods in other methods
self.myfun # and they are public by default
end
end
p irb_binding # :ok
IRB could easily evaluate at the top level but instead it creates a separate environment with the method so that local variables are not shared:
require "irb"
foo = :ok
IRB.start
#>> foo
# NameError: undefined local variable or method `foo' for main:Object
I think methods being public is just a coincidence due to the implementation and it doesn't matter much. These methods are temporary anyway.
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.
I'm using Struct.new to create new classes on the fly (we're using some entity modelling middleware, and I want to generate concrete types on the fly for serialization).
In essence I have this code:
module A
def self.init_on(target)
target.foo = 123
end
end
$base_module = A
module Test
C = Struct.new(:id) do
include $base_module
##base = $base_module
def initialize
##base.init_on(self)
end
attr_accessor :foo
end
end
c = Test::C.new
puts c.foo
I get this error when I run my test:
test2.rb:17:in initialize': uninitialized class variable ##base in Test::C (NameError)
from test2.rb:24:innew'
from test2.rb:24:in `'
From my understanding of Struct.new, the block is executed with the context of the class being created, so ##base should be resolvable.
Thanks for your time!
Edit:
Thanks - I made init_on self.init_on and used class_variable_set rather than instance_variable_set. It now works!
Why not try to use something like self.instance_variable_set(:##base, $base_module). I think that may work, since you are just setting an instance variable of the class object.
class Foo
def initialize
bar = 10
end
fiz = 5
end
Is there a possibility to get these local values (outside the class) ?
The local variable in initialize would be lost.
You are able to get the value fiz outside of the class, but only upon defining that class, and recording the return of the definition of the class.
return_of_class_definition = (class A ; fiz = 5 ; end) would assign the value of fiz to the variable.
You can also use binding but of course, this means changing the class, which may not be allowed for the exercise.
class A
bin = 15
$binding = binding
end
p eval 'bin', $binding
No. Once a local variable goes out of scope (for bar that is when the initialize method has run - for fiz when the end of the class definition has been reached), it's gone. No trace left.
While a local variable is still in scope you can see it (well, its name) with local_variables and get and set its value with eval (though that's definitely not recommended for sanity reasons), but once it's out of scope, that's it. No way to get it back.
In ruby we have something we could call scope gates - places when a program written in ruby leaves the previous scope. Those gates are: class, module and method (def keyword). In other words after class, module of def keyword in the code you're immediately entering into a new scope.
In ruby nested visibility doesn't happen and as soon as you create a new scope, the previous binding will be replaced with a new set of bindings.
For example if you define following class:
x = 1
class MyClass
# you can't access to x from here
def foo
# ...from here too
y = 1
local_variables
end
end
local_variables method call will return [:y]. It means that we don't have an access to the x variable. You can workaround this issue using ruby's technique called Flat Scopes. Basically instead defining a class using class keyword you can define it using Class.new and pass a block to this call. Obviously a block can take any local variables from the scope where it was defined since it's a closure!
Our previous example could be rewritten to something like like that:
x = 1
Foo = Class.new do
define_method :foo do
i_can_do_something_with(x)
y = 1
local_variables
end
end
In this case local_variables will return [:x, :y].