I defined one class SomeClass that includes methods with basic functionality. This class is defined in the Foo module.
# foo/
# |--some_class.rb
module Foo
class SomeClass
def some_method
puts "Hi!"
end
end
end
I also have two other modules: Bar and Baz. I want to include the entire namespacing of Foo inside of Bar and Baz, and inherit all methods defined under Foo, and thus to be able to overwrite Foo methods.
I also want to be able to overwrite this method only by specifying a new file in the right folder:
# baz/
# |--foo/
# |--some_class.rb
module Baz
module Foo
class SomeClass < ::Foo::SomeClass
def some_method
puts "Hey!"
end
end
end
end
This works fine. However, I want to be able to use Bar::Foo::SomeClass.some_method without having to create bar/foo/some_class.rb, and define empty modules and an empty SomeClass.
The same applies to inheritance of modules (by including).
Is there a way do accomplish this easily by using include, extend, or require, or something else? Since this is part of a Rails project (under the lib folder), can I use autoloading in a specific way?
The reason that I want to accomplish this is because I have many classes and modules with classes in it under Foo. The Bar and Baz modules should be able to inherit all modules/classes under Foo, and overwriting instance or class methods where it is needed by creating an explicit file. I would like to avoid creating a file for each class or module Foo has for Bar and Baz.
I have the following solution so far. I defined two new files: bar.rb and baz.rb. In bar.rb:
module Bar
def self.const_missing(name)
klass = Object.const_get(name.to_s) if name.to_s =~ /Foo/
return klass if klass
raise
rescue
super
end
end
> Bar
=> Bar # as expected
> Foo
=> Foo # as expected
> Bar::Foo
=> Foo # works!
> Bar::Foo::SomeClass
=> Foo::SomeClass # right
> Baz::Foo::SomeClass
=> Foo::SomeClass # not right...
I am not sure if I am on my way to a good solution now. Any feedback is highly appreciated.
Related
I recently did some code refactoring in a Rails codebase in which I converted some code that defined some classes like this (class bodies elided):
foo/user_bar.rb:
module Foo
class UserBar; end
end
foo/sub_user_bar.rb:
module Foo
class SubUserBar < UserBar; end
end
...to:
In foo/bar/user.rb:
module Foo
module Bar
class User; end
end
end
In foo/bar/sub_user.rb:
module Foo
module Bar
class SubUser < User; end
end
end
This introduced a subtle problem in that there was already an existing top-level class called User (a Rails model), and SubUser was being derived from that, not from User in the same module. I'm guess that this is because the order that Rails loads classes is unpredictable...?
Anyway, I found that I could disambiguate by declaring the SubUser class like this:
class SubUser < self::User
...which seems to work, although I couldn't find any examples of it in a modest amount of searching. It does look a little weird, though. And now I'm concerned that I really ought to be doing this for every class in my module, just in case a top-level class with the same name is ever introduced.
There are two styles of in-module class declarations throughout my company's codebase:
class A::B::Foo; end
And:
module A; module B; class Foo; end; end; end
I've always preferred the latter method since it allows for access to other names in the module without fully-qualified names, but now I've found that there's potentially a trap when I use it. So what's the best practice here?
Don't declare classes within a module; always use fully qualified names (class A::B::C::Foo < A::B::C::Bar)
Declare classes in a module but always fully qualify superclass names (module A; module B; module C; class Foo < A::B::C::Bar)
Declare classes in a module and use unqualified names but always use self:: when extending a class in the same module
Declare classes in a module and use unqualified names but stay aware of possible name collisions in the top-level namespace (seems risky)
Or do Ruby and/or Rails offer some other, cleaner way to resolve the issue?
Generally where there's ambiguity you specify full namespace paths:
module Foo
module Bar
class SubUser < Foo::Bar::User; end
end
end
That might seem verbose, but it's also specific and unambiguous.
Where you want to refer to literal top-level User you'd specify ::User.
Modules are identified by constants.1 The resolution of module look-ups is therefore determined by the procedure for looking up constant references. According to "The Ruby Programming Language", by David Flanagan and Yukihero Matsumoto, 1st Ed. 2008 (p. 261-262), constant look-ups are performed in the following order, where mod is the innermost module that encloses the reference to the constant:
mod.
the next enclosing module, continuing until there are no more enclosing modules. (Top-level/global constants are not considered at this step). The class method Module::nesting returns an array of modules that are so searched, in the order in which they are searched.
the elements of mod.ancestors, in index order (i.e., the inheritance hierarchy).
top-level constant definitions.
the value returned by the method Module#const_missing, with mod being the receiver and the constant expressed as a symbol being the argument, provided the method has been defined.
Let's see what we may learn by inserting puts Module.nesting.inspect and puts ancestors statements into the code.
module Foo
class UserBar
puts "nesting for UserBar=#{Module.nesting.inspect}"
puts "#{self}.ancestors=#{ancestors}"
end
end
# nesting for UserBar=[Foo::UserBar, Foo]
# Foo::UserBar.ancestors=[Foo::UserBar, Object, Kernel,
# BasicObject]
module Foo
class SubUserBar < UserBar
puts "nesting for SubUserBar=#{Module.nesting.inspect}"
puts "#{self}.ancestors=#{ancestors}"
end
end
# nesting for SubUserBar=[Foo::SubUserBar, Foo]
# Foo::SubUserBar.ancestors=[Foo::SubUserBar,
# Foo::UserBar, Object, Kernel, BasicObject]
module Foo
module Bar
class User
puts "nesting for User=#{Module.nesting.inspect}"
puts "#{self}.ancestors=#{ancestors}"
end
end
end
# nesting for User=[Foo::Bar::User, Foo::Bar, Foo]
# Foo::Bar::User.ancestors=[Foo::Bar::User, Object, Kernel,
# BasicObject]
module Foo
module Bar
class SubUser < User
puts "nesting for SubBar=#{Module.nesting.inspect}"
puts "#{self}.ancestors=#{ancestors}"
end
end
end
# nesting for SubBar=[Foo::Bar::SubUser, Foo::Bar, Foo]
# Foo::Bar::SubUser.ancestors=[Foo::Bar::SubUser,
# Foo::Bar::User, Object, Kernel, BasicObject]
This tells us that if there is a class User that is not contained within Foo (as in Rails), that class would the subject of references to User in the classes Foo::UserBar and Foo::SubUserBar, but not in Foo::Bar::User or Foo::Bar::SubUser. This understanding should help in organising modules to avoid namespace problems.
1 As classes are modules, whenever I reference "module" below I am referring to classes as well as modules that are not classes.
Let's say I have two classes with the same name ("Bar") in different modules (the global namespace / no namespace and "Foo") in a Rails application.
Both classes live in "app/models/bar.rb" or in "app/models/foo/bar.rb" respectively and are thereby autoloaded by rails.
# "app/models/foo/bar.rb"
module Foo
class Bar
def self.some_method
end
end
end
# "app/models/bar.rb"
class Bar
end
I have another class in the Foo namespace that uses the Bar class in a class method.
module Foo
class Qux
class << self
def something
Bar.some_method # This raises a NoMethod error because
# it uses the Bar defined in the global namespace
# and not the one in Foo
end
end
end
end
Rails autoloading tries to load Bar using the current classes name, which is nil in the singleton class, and defaults to "Object".
klass_name = name.presence || "Object" # from active_support/dependencies.rb
This causes Rails to load Bar from "app/models/bar.rb" instead of "app/models/foo/bar.rb".
If I define Qux.something with def self.something then .name is "Foo::Qux" instead of nil and the autoloading works correctly.
I see 3 options for at the moment to work around this problem:
1) Rename one of the Bar classes
2) use the self. syntax everywhere
3) Namespace Bar with Foo::Bar explicitly everywhere
I don't like any of these options because:
1) Bar is just the name that fits the best
2) class << self is perfectly fine by itself and used by most ruby developers, so the chance is high that the next poor guy stumbles over the same issues again soon
3) Same as 2), it doesn't really fix the underlying "Problem" and somebody will waste some time in the future to figure out why his code doesn't work as expected.
Is there another, better option you can think of?
I have a folder structure like the following in one of my projects:
lib
bar.rb
bar
other_bar.rb
another_bar.rb
next_bar.rb
...
bar.rb
require File.expand_path(File.dirname(__FILE__) + "/bar/other_bar.rb")
class Bar
puts "running BarBase"
end
bar/other_bar.rb
module Bar
class OtherBar
puts "running module Bar with class OtherBar"
end
end
If I now run ruby bar.rb I get this:
running module Bar with class OtherBar
bar.rb:3:in `': Bar is not a class (TypeError)
I'd like to have a similar structure to a rails model inheritance structure. How can I fix this? So far as I know ruby does not support this out of the box. Is there a workaround for such a situation?
Bar can't be a module and a class, they are different things.
Change bar.rb to module Bar or change other_bar.rb to class Bar.
Whichever it is, it has to be consistent. You can't change one to the other. The question is which should it be? If Bar is a container for other classes and only has a few global singleton methods? Then it's a module. But if it can be instantiated, then it's a class.
And yes, you can nest classes. This is totally acceptable:
class Bar
class OtherBar
puts "running module Bar with class OtherBar"
end
end
Bar::OtherBar.new # yay!
Modules and Classes can be nested inside either other in any way you see fit.
Edit with some commented examples to help clear this all up:
module Foo
# Foo::A
class A
# simple namespaced class
end
# Foo::B, inherits from Foo::A
class B < A
# inherting from a class in the same namespace
end
# modify Foo::B
class B
# When modifying an existing class you don't need to define the superclass
# again. It will raise an error if you reopen a class and define a different
# superclass. But leaving it off is fine.
end
# nested module Foo::Inner
module Inner
# Foo::Inner::C
class C
# simple more deeply namespaced class
end
# Foo::Inner::D, inherits from Foo::A
class D < A
# inherits from a class in a parent namespace
# works because ruby looks upward in the nesting chain to find missing constants.
end
# Foo::Inner::Foo
class Foo
# simple nested class with the same name as something in a parent namespace
# This is a totally different Foo, because it's in a different namespace
end
# Foo::Inner::E, inherits from Foo::Inner::Foo
class E < Foo
# class inhereting from another class in the same namespace
# Foo::Inner::Foo is "closer" than the global Foo, so that gets found as the superclass
end
# Foo::Inner::F, which mixes in the gloabl module Foo
class F
# the :: constant prefix says to start looking in the global namespace
# so here we include the top level module Foo, and not the "closer" in namespace Foo::Inner::Foo
include ::Foo
# This is an error. This attempts to include the class Foo::Inner::Foo since thats the closest by namespace
# thing that matches the constant Foo. (you can't include classes, only modules)
# You need the :: prefix to grab the global Foo module
include Foo
end
end
end
# Z decalred in the global namespace, which inherits from the deeply nested class Foo::Inner::C
class Z < Foo::Inner::C
# Any class anywhere can inherit from any other class in any namespace.
# Just drill in!
end
# the following 2 declarations at this point would be identical
# This defines a class deep with in a namespace
class Foo::Inner::Foo::Bar < Foo::A
end
# same as above, but reopens each namespace
module Foo
module Inner
class Foo
class Bar < ::Foo::A
end
end
end
end
Just use class Bar instead of module Bar. In Ruby, classes can be reopened and added to.
It is recommended not to use a class as a namespace, especially if you use Rails.
If you want to keep the Bar class in lib/bar.rb, an option is to move the other classes in the Bars namespace in lib/bars.rb.
Read more as to why here: https://blog.jetbrains.com/ruby/2017/03/why-you-should-not-use-a-class-as-a-namespace-in-rails-applications/
Update: this behavior is fixed in ruby 2.5
See: https://blog.bigbinary.com/2017/10/18/ruby-2.5-has-removed-top-level-constant-lookup.html
I am trying to be dry by moving common methods into a module or a class and have it included/inherited in new classes that are namespaced under different modules. If I have two classes namespaces under the same module, then I can call them without including module name as long as I am under the same namespace. But if i have a method included from different module than my namespace scope changes and I dont know why or how to avoid it.
For example. this code works and returns 'bar':
module Foo
class Bar
def test_it
Helper.new.foo
end
end
end
module Foo
class Helper
def foo
'bar'
end
end
end
Foo::Bar.new.test_it
but if I move out method test_it into a module, then it doesnt work anymore: NameError: uninitialized constant Mixins::A::Helper.
module Mixins; end
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Helper.new.foo
end
end
end
end
module Foo
class Bar
include Mixins::A
end
end
module Foo
class Helper
def foo
'bar'
end
end
end
Foo::Bar.new.test_it
Moreover if class_eval is evaling string instead of block, scope becomes Foo::Bar instead of Foo.
module Mixins; end
module Mixins::A
def self.included(base)
base.class_eval %q{
def test_it
Helper.new.foo
end
}
end
end
module Foo
class Bar
include Mixins::A
end
end
module Foo
class Helper
def foo
'bar'
end
end
end
Foo::Bar.new.test_it
anyone got ideas?
EDIT:
Thanks to Wizard and Alex, I ended up with this code which is not beautiful but does the job (note it is using Rails helper constantize):
module Mixins; end
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
_nesting::Helper
end
def _nesting
#_nesting ||= self.class.name.split('::')[0..-2].join('::').constantize
end
end
end
end
module Foo
class Helper
end
class Bar
include Mixins::A
end
end
module Foo2
class Helper
end
class Bar
include Mixins::A
end
end
Foo::Bar.new.test_it #=> returns Foo::Helper
Foo2::Bar.new.test_it #=> returns Foo2::Helper
To understand this problem, you need to understand how constant lookup works in Ruby. It's not the same as method lookup. In this code:
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Helper.new.foo
end
end
end
end
Helper refers to a constant called "Helper" which is either in A, Mixins, or defined at the top level (ie. in Object), not a constant called "Helper" which is defined in Bar. Just because you class_eval this code with class Bar, doesn't change that. If you know the difference between "lexical binding" and "dynamic binding", then you can say that constant resolution in Ruby uses lexical binding. You are expecting it to use dynamic binding.
Remember that the block which you are passing to base.class_eval is compiled to bytecode once, and subsequently, every time the included hook is called, that same precompiled block (including the reference to Helper) is executed with a different class (base) as self. The interpreter does not parse and compile the block afresh every time you execute base.class_eval.
On the other hand, if you pass a String to class_eval, that string is parsed and compiled afresh every time the included hook runs. IMPORTANT: code which is evaled from a String is evaluated in a null lexical environment. That means that local variables from the surrounding method are not available to the code being evaled from the string. More to the point for you, it also means that the surrounding scope will not affect constant lookup from within the evaled code.
If you do want a constant reference to be resolved dynamically, an explicit constant reference will never work. That's simply not the way the language is intended to work (and for good reason). Think about it: if constant references were resolved dynamically, depending on the class of self, you could never predict how references to things like Array or Hash would be resolved at runtime. If you has code like this in a module...
hash = Hash[array.map { |x| ... }]
...And the module was mixed in to a class with a nested Hash class, Hash.[] would refer to the nested class rather than Hash from the standard library! Clearly, resolving constant references dynamically has just too much potential for name clashes and related bugs.
Now with method lookup, that's a different thing. The whole concept of OOP (at least the Ruby flavor of OOP) is that what a method call (ie. a message) does depends on the class of the receiver.
If you do want to find a constant dynamically, depending on the class of the receiver, you can do it using self.class.const_get. This is arguably cleaner than evaling a String to achieve the same effect.
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
Foo::Helper.new.foo
EDIT:
After getting a chance to play around with code a bit I see more of the problem. I don't think you'll be able to do exactly what you were attempting, but this is close:
module Mixins::A
def self.included(base)
base.class_eval do
def test_it
self.class.const_get(:Helper).new.foo
end
end
end
end
module Foo
class Bar
include Mixins::A
end
end
module Foo
class Bar::Helper
def foo
'bar'
end
end
end
Note that the Helper class needed to be namespaced under Foo::Bar due to way constants get resolved in Ruby.
Constant-lookup in Ruby has changed with respect to #class_eval over the past few major releases. See this post for more information: http://jfire.posterous.com/constant-lookup-in-ruby
I'm hoping to set up a default path for logging, relative to the path of the file using the log, something like this:
# /path/to/lib/bar.rb
class Bar
def settings_file_path
File.dirname(File.expand_path(__FILE__))
end
end
# /path/to/app/models/foo.rb
class Foo < Bar
end
Foo.new.settings_file_path
Ideal output:
# => /path/to/app/models
Actual output:
# => /path/to/lib
Because FILE references the file where it's written, not where it's being called from, it's returning the bar.rb file, but I want something like this to return the path of the foo.rb file, even though the method is defined in Bar.
Anybody have any suggestions?
The simplest would be something like this:
# foo.rb
class Foo
def self.my_file
#my_file
end
end
# bar.rb
class Bar < Foo
#my_file = __FILE__
end
# main.rb
require_relative 'foo'
require_relative 'bar'
p Bar.my_file
#=> "/Users/phrogz/Desktop/bar.rb"
However, you could parse the caller in a self.inherited hook like so:
# foo.rb
class Foo
class << self
attr_accessor :_file
end
def self.inherited( k )
k._file = caller.first[/^[^:]+/]
end
end
# bar.rb
class Bar < Foo
end
# main.rb
require_relative 'foo'
require_relative 'bar'
p Bar._file
#=> "/Users/phrogz/Desktop/bar.rb"
I'm not certain how robust or portable that parsing is; I suggest you test it.
N.B. My Bar inherits from Foo, the reverse of your question. Be not confused by the differences in our setups.
caller.first[/^[^:]+/] will not work on Windows because absolute path there looks like $DRIVE:$PATH (example C:/Windows/system32)
Instead caller.first[/^[^:]+/] use caller_locations.first.absolute_path
Ruby 2.7 adds Module#const_source_location
const_source_location(:Foo)
Use $0 instead of __ FILE__ :)