Ruby class based on BasicObject can't access code in other module - ruby

I'm using method_missing to define a class for namespacing constants in a vocabulary. To be effective, I need the vocabulary class to inherit from BasicObject, otherwise none of the standard object methods are available as vocabulary terms (because the method isn't missing :). However, when I inherit from BasicObject, I find I can't call utility methods in another module. The following code illustrates the issue in condensed form:
module Foo
class Bar
def self.fubar( s )
"#{s} has been fubar'd"
end
end
end
class V1
def self.method_missing( name )
Foo::Bar.fubar( "#{name} in v1" )
end
end
class V2 < BasicObject
def self.method_missing( name )
Foo::Bar.fubar( "#{name} in v2" )
end
end
# this works
puts V1.xyz
# => xyz in v1 has been fubar'd
# this doesn't
puts V2.xyz
# => NameError: uninitialized constant V2::Foo
What would I need to add to V2 so that it doesn't produce an unitialized constant error when I try to call the helper module?

It works if you change the method in V2 like this so that name resolution starts in the global scope.
def self.method_missing( name )
::Foo::Bar.fubar( "#{name} in v2" )
end
I looked it up in the documentation for you:
BasicObject does not include Kernel (for methods like puts) and
BasicObject is outside of the namespace of the standard library so
common classes will not be found without a using a full class path.
...
Access to classes and modules from the Ruby standard library can be
obtained in a BasicObject subclass by referencing the desired constant
from the root like ::File or ::Enumerator.

Related

How do I access Ruby type names from within BasicObject

How do I access Ruby type names from within BasicObject? I understand that "... common classes will not be found without using a full class path", but I don't know the syntax for specifying the full class path.
The code below fails because Hash isn't imported into BasicObject.
class Basic < BasicObject
def flexible(data)
if (data.is_a?(Hash))
puts "It's a hash!"
end
end
end
foo = Basic.new
foo.flexible({})
To answer your immediate question, you can access Hash like so:
if (data.is_a?(::Hash))
puts "It's a hash!"
end
This will still fail for a different reason, which is because BasicObject doesn't include Kernel, so puts is not available:
undefined method `puts' for #<Basic:0x000055c405afbc88>
If you do this as well:
class Basic < BasicObject
include ::Kernel
or this instead:
::Kernel.puts "It's a hash!"
Then it should work as expected.

What is the difference between `Class` and `class`

While creating a class, we use the keyword class like:
class Abc
Z = 5
def add
puts "anything here"
end
end
In console, Abc.class # => Class
How does Abc internally become a class? What is the difference between class and Class?
It would be great if anyone could explain how class constants and method are internally called, and if a method is not defined, then how we get the exception "undefined class method". What is the internal logic behind it?
There are three different things here:
class is a keyword, which is used to define or reopen a class
Object#class is a method, which returns the class of a given object
Class is the class which all classes are an instance of (including Class itself)
ndn's answer gives a nice overview of the different things "class" could refer to.
To answer your specific question:
How does Abc internally become a class?
Almost like any other object.
class Abc
end
is equivalent to:
Abc = Class.new do
end
It creates a new instance of Class and assigns it to the constant Abc.
To see what the different "class" things in Ruby mean, check out my other answer.
As for how methods are looked up:
There are multiple places a method can come from:
The class of the object
The parent of the class of the object
Modules, which are included/prepended
The singleton class of the object
The order of lookup is the following:
If it exists, the singleton class
Prepended modules
Methods defined in the class
Included modules
Recursively, the above rules for the parent class
There are a few things that have to be noted here:
If multiple modules are included/prepended, they will be looked up in the reverse order of how they were included/prepended
If a module was already included/prepended in one of the parrents, it won't be included/prepended again
If using these rules the method was not found til the very start of the hierarchy (BasicObject), the ancestor chain is searched again for a different method, called method_missing
BasicObject#method_missing is defined so that it throws a NoMethodError and that is where the error comes from
module M1
def foo
puts 'Defined in M1'
end
end
module M2
def foo
puts 'Defined in M2'
end
end
class C
include M1
prepend M2
def foo
puts 'Defined in C'
end
def method_missing(method_name)
puts 'Method missing' if method_name == :foo
end
end
c = C.new
# singleton method
def c.foo
puts "Defined in c's singleton"
end
puts c.singleton_class.ancestors
# => [#<Class:#<C:0xa2d0f8>>, M2, C, M1, Object, Kernel, BasicObject]
# ^ the singleton class,
# the prepended module,
# the C class itself,
# the included module,
# the rest of the hierarchy
# (Object is the implicit parent of all classes with no explicit parent)
with class Abc you define a class.
Abc.class is returning the Type, and the type of Abc is a Class
another example:
1337.class
=> Fixnum
"hi babe!".class
=> String
12.55.class
=> Float
(1..12).class
=> Range
so as you can see, each "datatype" is a class. in your case Abc is also a Datatype. And for the end of that chain, the class of a class is Class! :-)
Answering second par of the question, undefined class method happens when method you called is not present in such class - classes are just objects in Ruby, and as such they have their own set of methods. Ruby has several ways to define class methods, most common is probably
class Klass
def self.klass_method
...
end
end
the other is
class Klass
# some ordinary instance methods here
class << self
def klass_method
# this is class method
end
end
end
Some Rubyists prefer the latter, as it keeps all class methods in single block, but they are equivalent.

Refer to a class without explicitly mentioning its namespace

I have a class within several modules: This::Is::A::Long::ClassName. Is there any way, within one script or method, to make ClassName available without having to reference the namespace? Instead of writing:
This::Is::A::Long::ClassName.do_something
This::Is::A::Long::ClassName.do_something_else
This::Is::A::Long::ClassName.do_something_different
is anything as below possible?
include This::Is::A::Long
ClassName.do_something
ClassName.do_something_else
ClassName.do_something_different
If you are using modules for namespacing, the code you posted should work, see this example:
module Long
module Name
class ClassName
end
end
end
ClassName
# => ... uninitialized constant ClassName (NameError)
include Long::Name
ClassName
# => Long::Name::ClassName
Ruby has no equivalent to C++ using namespace, and you can not reference a class without being in the right namespace, but you can always make it a variable since a class is also an object
long_class = This::Is::A::Long::ClassName
long_class.do_something
long_class.do_something_else
# and so on
EDIT
An include does not put you in the right namespace, it includes the methods & classes in the module you are including (that is, it puts the module in the classes ancestors) and is therefore most certainly not suitable for your needs: Consider the following:
module This
module Is
module A
def foo
puts 'A#foo'
end
def bar
puts 'A#bar'
end
class ClassName
end
end
end
end
Now, you may not want to write This::Is::A::ClassName in another class, let's say:
class C
def foo
puts 'C#foo'
end
end
class B < C
include This::Is::A
end
Now, B.new.foo still puts out C#foo, right? Wrong. Since you included the module, the method has been overwritten.

Accessing module methods with ::

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.

Modules in Ruby

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.

Resources