I have a module including constants that I want to use in the unit tests across a test suite.
What I don't understand is why, if I include the module, constants are not available, but methods (if I add any to the module) are.
Example:
module Namespace
module Constants
K = 1
def mymethod; end
end
end
describe Namespace::Subject do
include Namespace::Constants
context "Context" do
it "should execute" do
mymethod # succeeds
K # fails
end
end
end
The same principle works in a console:
2.5.5 :008 > include Namespace::Constants
=> Object
2.5.5 :010 > K
=> 1
Constants defined within a namespace can be called by prefacing the constants from within the namespace.
describe Namespace::Subject do
include Namespace::Constants
context "Context" do
it "should execute" do
mymethod # succeeds
Namespace::Constants::K # succeeds
end
end
end
The reason this makes sense is that it should follow OOP design whereby a method defined within a class or module should be accessible only from within the namespace where it is defined. Here is a simple example:
class A
FOO = 'foo'
end
class B
FOO = 'bar'
end
a = A.new
b = B.new
FOO = 'baz'
a.class::FOO
=>"foo"
b.class::FOO
=>"bar"
FOO
=>"baz"
Related
I just got stuck on this for a while. Take this base:
module Top
class Test
end
module Foo
end
end
Later, I can define classes inside Foo that extends Test by doing this:
module Top
module Foo
class SomeTest < Test
end
end
end
However, if I try to minimize indentation by using :: to specify the module:
module Top::Foo
class Failure < Test
end
end
This fails with:
NameError: uninitialized constant Top::Foo::Test
Is this a bug, or is it just a logical consequence of the way Ruby resolves variable names?
Is this a bug, or is it just a logical consequence
It's a "quirk". Some consider it a bug.
Parent scopes used for looking up unresolved constants are determined by module nesting. It just so happens that when you use module Top::Foo, it creates just one level of nesting instead of two. Observe:
module Top
module Foo
class SomeTest
Module.nesting # => [Top::Foo::SomeTest, Top::Foo, Top]
end
end
end
module Top::Foo
class SomeTest
Module.nesting # => [Top::Foo::SomeTest, Top::Foo]
end
end
This is expected. Using :: changes the scope of constant lookup and expects Test to be defined under Top::Foo.
To get the expected result, you could write:
module Top::Foo
class SomeTest < Top::Test
end
end
or:
module Top
class Foo::SomeTest < Test
end
end
or even:
class Top::Foo::SomeTest < Top::Test
end
When creating anonymous classes through Class.new, they don't seem to have their own namespace for constants:
klass1 = Class.new do
FOO = "foo"
end
klass2 = Class.new do
FOO = "bar"
end
This gives warning: already initialized constant FOO and looks like it's right:
> klass1.const_get(:FOO)
"bar"
> klass2.const_get(:FOO)
"bar"
> FOO
"bar"
I was going to use this approach in a simple DSL for defining addons for an application, something like this:
class App
class AddonBase
attr_reader :session
def initialize(session)
#session = session
end
end
def self.addons
#addons ||= {}
end
def self.addon(name, &block)
addons[name] = Class.new(AddonBase, &block)
end
end
This works fine for simple add-ons but if defining constants, they will be under Object:: instead of becoming addons[name]::CONSTANT:
App.addon "addon1" do
PATH="/var/run/foo"
def execute
File.touch(PATH)
end
end
App.addon "addon2" do
PATH="/etc/app/config"
def execute
File.unlink(PATH)
end
end
# warning: already initialized constant PATH
The constants could be anything and the add-ons could even define their own utility subclasses, so it's not just about replacing PATH with a function.
Is there some way to work around this?
When creating anonymous classes through Class.new, they don't seem to have their own namespace for constants
They do, you can use const_set to define constants in anonymous classes:
klass1 = Class.new do
const_set(:FOO, 'foo')
end
klass2 = Class.new do
const_set(:FOO, 'bar')
end
klass1::FOO #=> "foo"
klass2::FOO #=> "bar"
Or via self::
klass1 = Class.new do
self::FOO = 'foo'
end
klass2 = Class.new do
self::FOO = 'bar'
end
klass1::FOO #=> "foo"
klass2::FOO #=> "bar"
When creating anonymous classes through Class.new, they don't seem to have their own namespace for constants
Sure, by the definition of the word “anonymous.” Compare two following snippets:
class C1; puts "|#{name}|"; end
#⇒ |C1|
C2 = Class.new { puts "|#{name}|" }
#⇒ ||
Unless assigned to the constant, the class has no name and hence all constants defined inside go to Object namespace. That said, the warning here is actually pointing out to error and Object::FOO = "bar" overrides Object::FOO = "foo" constant.
That said, one cannot use constants in this scenario. Use class-level instance variables instead, or construct unique constant names manually (I would advise avoiding polluting Object class with a bunch of unrelated constants, though.)
Actually the problem is how to define a class using a proc including constant definitions. As has already been said it is not possible the way you did it, since the proc gets class_eval'd and that doesn't allow to define constants.
I suggest another approach. Can you use modules instead of procs to define new addons mixing a module into a class?
Example:
module AddonModule
FOO = "foo"
end
klass = Class.new
klass.include AddonModule
klass::FOO # => "foo"
Usage in your DSL:
def self.addon(name, addon_module)
addon = Class.new(AddonBase)
addon.include addon_module
addons[name] = addon
end
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
Assuming a module is included, not extended, what's difference between module instance variable and class variable?
I do not see any difference between two.
module M
#foo = 1
def self.foo
#foo
end
end
p M.foo
module M
##foo = 1
def self.foo
##foo
end
end
p M.foo
I have been using # as ## within a module, and I recently saw other codes are using ## within a module. Then I thought I might have been using it incorrectly.
Since we cannot instantiate a module, there must be no difference between # and ## for a module. Am I wrong?
----------------------- added the following --------------------
To answer some of questions on comments and posts, I also tested the following.
module M
#foo = 1
def self.bar
:bar
end
def baz
:baz
end
end
class C
include M
end
p [:M_instance_variabies, M.instance_variables] # [#foo]
p [:M_bar, M.bar] # :bar
c = C.new
p c.instance_variables
p [:c_instance_variabies, c.instance_variables] # []
p [:c_baz, c.baz] :baz
p [:c_bar, c.bar] # undefined method
When you include a module within a class, module class variables and class methods are not defined in a class.
Class variables can be shared between modules and classes where these modules are included.
module A
##a = 5
end
class B
include A
puts ##a # => 5
end
Meanwhile, instance variables belong to self. When you include module A into class B, self object of A is not the same as self object of B, therefore you will not be able to share instance variables between them.
module A
#a = 5
end
class B
include A
puts #a # => nil
end
## refers to class variable and # refers to instance variable. While using this with module makes big difference. When you include a module you add class methods to your class while extending add instance methods to your class
Following is a nice article on this
http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
I found this SO post that talks about how to create class variables in modules, "they are supported natively." One commenter even says the name 'class variable' is misleading since classes are just modules with a few extra powers.
All I'm sure of is nearly every article I've read about class variables thinks they're evil and to avoid them at all costs because of the weird inheritance issues you can encounter.
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.