Rails mistakenly nesting called namespace into caller namespace - ruby-on-rails

Bar calls Foo and Rails thinks that Foo must be within the Bar namespace
module Foo
class Lofatook
def oops
puts 'buckets of fun'
end
end
end
module Bar
class Thedoor
def theyhaveacavetroll
Foo::Lofatook.new.oops
end
end
end
Bar::Thedoor.new.theyhaveacavetroll
This works fine as raw ruby. But when I put Foo in lib/foo/lofatook.rb and Bar in app/models/bar/thedoor.rb it throws this error:
uninitialized constant Bar::Foo
We are using the golden lib loading hammer of
config.autoload_paths += Dir["#{config.root}/lib/"]
config.autoload_paths += Dir["#{config.root}/lib/**/"]
with all its glorious drawbacks.
But this should not mean it mistakenly assumes Foo must be part of Bar?

Problem was its not enough to have
module Foo
class Lofatook
end
end
in lib/foo/lofatook.rb. You must also have lib/foo.rb
module Foo
end
Thanks to #ma_il, using ::Foo::Lofatook raised the error uninitialized constant Foo which pointed me in the right direction

Related

How to access a class defined in module B from module A

Error message:
NameError: uninitialized constant FinancialFunctions::ComputationSteps::TwoBranchesPerPeriod::Strategy1
I'd like Rails to automatically look through the included Strategies module and see the class defined within it instead of just looking within TwoBranchesPerPeriod.
This is not a one-time problem and is part of a broader architecture restructuring. Solving this problem will help in many places throughout the codebase.
There are two specific solutions I'm not looking for:
Having a require or load statement at the top of the file, as I want Rails autoloading to be the core solution for this problem and not have to require several files.
Explicitly state the scope when initializing the class. I'm aware that simply calling Strategies::Strategy1.new will work, but this solution does not work for me because of the length of several classes. I'd like to simply define the dependencies at the top of the file with include statements, and have access to the methods/classes defined in the included modules.
I'm open to autoload statements in a file such as strategies.rb but from what I've test this does not work either.
app/lib/financial_functions/computation_steps/two_branches_per_period.rb:
module FinancialFunctions
module ComputationSteps
module TwoBranchesPerPeriod
include Strategies
def a_method
#The below line is resulting in the error
# NameError: uninitialized constant FinancialFunctions::ComputationSteps::TwoBranchesPerPeriod::Strategy1
a=Strategy1.new
end
end
end
end
/app/lib/financial_functions/computation_steps/strategies/strategy1.rb:
module FinancialFunctions
module ComputationSteps
module Strategies
class Strategy1
def initialize
puts "this should work"
end
end
end
end
I'd like to simply define the dependencies at the top of the file with
include statements, and have access to the methods/classes defined in
the included modules.
Your expectations are not realistic in Ruby. Ruby does not have a package system and includes is not equivalent to imports in languages such as Python or ES6.
As stated in the comments by #maxpleaner includes only copies the methods of the module.
So to do this you would have to define new constants in the consuming class/module:
module FinancialFunctions
module ComputationSteps
module TwoBranchesPerPeriod
include Strategies
Strategy1 = ComputationSteps::Strategies::Strategy1
def a_method
Strategy1.new
end
end
end
end
You can do this dynamically through the Module#included hook:
module Foo
module Bar
end
module Baz
end
def self.included(base)
self.constants.each do |c|
base.constant_set(c, self.constant_get(c))
end
end
end
module X
includes Foo
end
irb(main):002:0> X::Bar
=> Foo::Bar
But aside from being an fun example of meta-programming I don't really think its that wise as the there is no way to handle namespace conflicts. You could also construct a makeshift equivalent of an imports construct:
module Importer
def import(*constants, from:)
constants.each do |c|
const_set(c, from.const_get(c))
end
end
end
module Foo
module Bar
module A
end
module B
end
end
end
class Thing
extend Importer
import :A, :B, from: Foo::Bar
end
irb(main):001:0> Thing::A
=> Foo::Bar::A
irb(main):002:0> Thing::B
=> Foo::Bar::B
But this also smells like an example of fighting the language and will probably introduce more problems then it solves.

Rails can't find my classes after a while

I have a custom module with a class in Rails. I put this in my autoload inside the application.rb
config.autoload_paths << "#{Rails.root}/classes"
The module has a reference to a file in a deeper folder:
a.rb
require_relative './b/b.rb'
module A
def self.abc content
return A::B.new
end
end
B has nothing fancy:
b.rb
module A
class B
def initialize
end
end
end
What's happening right now is that in the beginning this works perfectly, but after some requests, or a certain amount of time, it stops working and I get an: uninitialized constant A::B error.
Any ideas? The fact that it works in the beginning is confusing me. Thanks in advance.

Rails - including module containing a class gives Uninitialized Constant error

My application has lib/project/errors which contains a bunch of Exception classes, one of which is ServiceException
module Project
module Errors
class ServiceException < Exception
def initialize(message = nil)
super message
end
end
end
end
I am trying to use this in my GameService:
module GameMan
class GameService
Blah blah
def validate(score)
raise Project::Errors::ServiceException.new('blah')
end
end
end
This works,
however I hate writing the full module path everywhere. Is there a way to avoid this?
I have tried
module GameMan
class GameService
include Project::Errors
Blah blah
def validate(score)
raise ServiceException.new('blah')
end
end
end
This gives
uninitialized constant ServiceException error.
I have
config.autoload_paths +=
%W(#{config.root}/lib #{config.root}/app/services)
already set inapplication.rb``
What am I doing wrong?
It is all about constants lookup.
ServiceException is defined in the scope of Project::Errors. When you reference ServiceException without prefixing Project::Errors it looks for the class defined in the outer scope, and failing, because there is none.
You should be using the full path.
include Project::Errors
Replace above line to following line
include Project::Errors::ServiceException

Rails has trouble loading module chain

I am building a Rails Engine. I defined a controller like this,
module A::B::C::D::E
extend ActiveSupport::Concern
# module stuff ...
end
class ExamplesController < ApplicationController
include A::B::C::D::E
# controller stuff ...
end
When I start the Rails console, rails console, I get the following error,
uninitialized constant A::B (NameError)
Why do I get this error?
Because the module does not exist at the point you are using it.
Since A is not defined, Ruby doesn't know what it is.
Note that the :: is a scope resolution operator used for look up, not defining a namespace-like hierarchy.
It would work if you defined A first:
module A
end
Then B:
module A::B
end
Then C:
module A::B::C
end
And so on.
Of course you could also do this:
module A
module B
module C
module D
module E
end
end
end
end
end

Module scope not right

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

Resources