Constant definition in Ruby/Rails - ruby-on-rails

I have initializers config in my rails application under config/initializers/my_config.rb.
What is the difference between:
A:
module MyModule
Config = "path/to/config.yml"
end
and:
B:
MyModule::Config = "path/to/config.yml"
Let's suppose we do some requests, change its implementation, and hit the application again. If I defined my constant the B way, I get an error:
uninitialized constant MyModule::Config
It will be resolved only when I restart my rails server. But when I do the A way, it still recognized the constant when I updated my code.
What is the importance of using the A syntax in this case?

Part of this seems to have to do with rails hot code reloading, which has a bunch of caveats. If you aren't using hot code reloading, A and B are more equivalent, as long as MyModule has been defined first.
However, when code is reloaded, (particularly the file that defines MyModule), it might end up overwriting the existing module, and not running the B line.
The main difference though, is that A doesn't rely on how the order of other code in the project is loaded/run, but B must be run after certain code.

The differences is that code A raises a syntax error, while code B is grammatical. Code B will raise a name error for MyModule unless it is previously defined, though.

Related

Rails `NameError: uninitialized constant` only in production (not console) - class is in /app subfolder - needs to add double colon `::`

In a Rails 5.2 app, I have a class Foos::SomethingSerializer, stored in app/serializers/foos/something_serializer.rb.
In Development, this controller code works just fine:
# api/v1/foos_controller.rb
render json: Foos::SomethingSerializer.new(foo).as_json
In Production only, this code raises an exception NameError: uninitialized constant Api::V1::Foos::SomethingSerializer
I've had this before so I just added :: in front of it, like:
# api/v1/foos_controller.rb
render json: ::Foos::SomethingSerializer.new(foo).as_json
I could call it a day, but I would really like to understand this, since these bugs that only bites in Production are the worst.
Also, Rails.configuration.eager_load_paths in the console shows that /app/serializers is on the autoload path, as it should be, as it's a subfolder of app/.
Also, in the production environment console, I can type Foos::SomethingSerializer.new without any exception whatsoever (so it finds the constant).
So why (1) in development this works and (2) in production it throws an exception trying to find that constant namespaced inside the controller and can't find it on the upper Foos namespace?
Ruby has relative constant lookup.
module A
class B
def self.c
C # note that it's not A::C
end
end
end
module A
class C
end
end
class C
end
C # C < Object
A::B.c # A::C < Object
If it can find a constant relatively to where you're looking from, it will return that. If it cannot find the constant in the current context, it will go up a level and look for it there until it reaches root level. If the constant is not found on root level, then an error is raised.
Prepending :: to the constant tells ruby to just start looking from the root level.
In your code, you have both ::Foos and Api::V1::Foos defined. When calling Foos::SomethingSerializer from inside Api::V1, ruby will find Api::V1::Foos instead of ::Foos, but there is no SomethingSerializer there, so it will fail. I know it's inconvenient and I don't personally like it either, but that's how it is. If you know you're looking for something from the root level, adding :: to the start is always safe.
It didn't fail on development, because you have autoloading on. When you called Foos::SomethingSerializer, Api::V1::Foos was not loaded yet and since ::Foos was already loaded, rails found it and didn't try to autoload Api::V1::Foos.
I suggest you run your specs with eager_load = true to attempt to catch errors like this and use root-level lookup (::) whenever you have constants with the same name in both root and nested contexts.

Class name conflict across modules

I'm running into an issue in a Rails 4 application related to class names and modules.
I have an Event class in my main application that inherits from ActiveRecord::Base. I also have a set of files in my /lib directory that have been grouped into a module I call LibModule. There's a class in that module that's also named Event. I noticed something interesting about referencing these classes. Here are some examples using the Rails console.
Example #1: When Event has never been referenced, the ActiveRecord version gets loaded:
> Event
=> Event(id: integer...)
Example #2: When LibModule::Event gets referenced first:
> LibModule::Event
=> LibModule::Event
> Event
=> LibModule::Event
As a result, when my server restarts (after updates, etc), I'll occasionally get the following error if a user engages in behavior that triggers server activity similar to Example #2:
superclass mismatch for class Event
I know there are a few ways to ensure that there's no conflict here. What's the best practice way of handling a situation like this?
I tried replicating the behavior from Example #2 with class names from gems and it seems like Rails completely segregates the classes in gems. Is there a way to do the same here? I think this would be my ideal situation.
Should I just change the name of LibModule::Event?
Should I ensure that the ActiveRecord class loads during initialization?
Some other Rails best practice I haven't thought of?
This has to do with the way qualified constants are resolved by the Rails autoloader. The documentation offers the following solution:
Naming conflicts of this kind are rare in practice, but if one occurs, require_dependency provides a solution by ensuring that the constant needed to trigger the heuristic is defined in the conflicting place.
The solution, in your case, is to add this just above the class definition for LibModule::Event:
require_dependency 'event'
This will inform the autoloader of the ::Event constant that references your ActiveRecord model, ensuring the appropriate constant naming for LibModule::Event.
Although there's nothing in Ruby that precludes you from having duplicated class names, the Rails auto-loader does get easily confused by them which can cause a lot of problems.
Typically I go out of my way to avoid duplication for this very reason. Sometimes they work, sometimes they don't, and the working/not-working aspect of the code can often depend on which entry point is taken, which things are loaded first, making it unpredictable.
You can try and circumvent this by force-loading your Event class using require_relative at the end of lib_module.rb.
to reference the main event class try ::Event. :: is a scope resolution operator, which specifies the global/main scope.

Rails vs Ruby namespacing rules.

Reading this really good article on Rails namespacing and module lookup. Here
I don't understand what this means:
If constants are loaded only when they’re first encountered at
runtime, then by necessity their load order depends on the individual
execution path.
What is the individual execution path?
I think that non-understand leads me to not understand this:
As soon as an already-loaded constant Baz is encountered, Rails knows
this cannot be the Baz it is looking for, and the algorithm raises a
NameError.
or more importantly this:
The first time, as before, is down to the loss of nesting information.
Rails can’t know that Foo::Qux isn’t what we’re after, so once it
realises that Foo::Bar::Qux does not exist, it happily loads it.
The second time, however, Foo::Qux is already loaded. So our reference can’t have been to that constant, otherwise Ruby would have
resolved it, and autoloading would never have been invoked. So the
lookup terminates with a NameError, even though our reference could
(and should) have resolved to the as-yet-unloaded ::Qux.
Why doesn't rails use the constant that is encountered that is already loaded? Also why does running:
Foo::Bar.print_qux
twice lead to two different outcomes?
By "execution path" they mean the way your code is running. If there's a reference to a class X::Y inside an if block that isn't executed, that means your execution path bypasses it so it's not loaded.
This is different than force-loading all classes referenced in your code at parse time. They're simply loaded as they're exercised if and only if that given line of code is executed.
The autoloader has a strategy for trying to load modules starting with the most specific and then looking for increasingly global names. Qux is tested against the current module context, then the root of that and so on. This is how symbols are resolved.
In that example the auto-loaded version actually pushes the Foo::Qux definition ahead of ::Qux in terms of priority. That's the major change there.

Strange behaviour in a single line ruby sentence having a block and an if clause

I don't know why these two pieces of code behave different in Ruby 1.8.7 since one seems to be the single line version of the other.
The first piece of code (it works as it should):
if #type.present?
type = #type
orders = Order.where{type.eq(type)}
end
The single line version (it doesn't work at all, no error but seems no execution too):
orders = Order.where{type.eq(type)} if (type = #type).present?
NOTE: I'm using the squeel gem, that is the reason a block follows the where method. Also the variable type has to capture the instance variable #type since the execution context changes inside the block and the instance variables are not shared between the main context and the block context.
NOTE 2: I have to use Ruby 1.8.7 for legacy reasons.
Any idea? Thank you!
There is a problem with the order of parsing of your code. Variables need to be defined before they are used.
Even though variables defined inside if statement clauses "leak" out into the current scope, they do not leak "backwards" in Ruby code.
Ruby is a little bit curious in that way that variables need to be defined before the parser parses the code. The parsing is done from top to bottom and left to right.
Hence since the variable type is defined after your block code where you use it, it will not be available in the block.
Example:
>> 3.times { puts x } if (x = 123)
NameError: undefined local variable or method `x' for main:Object
The reason you don't get any error message is that in Ruby 1.8 type is a method that is a synonym for Object#class.
So what your code is really doing is (probably):
orders = Order.where{type.eq(this.class)} if (type = #type).present?
To fix it you have to define type before you use it. Therefore you can't really turn that into a one-liner unless you simply do this instead:
orders = Order.where{type.eq(#type)} if #type.present?
All in all it's not a good idea in Ruby 1.8 to use type as a variable in Rails models, because of the Object#class issue it will most likely bring you headaches in the long run.
similar problem that i got was that 'type' is a keyword in database, it allows for our model to have the field as 'type' but it works strangely in different conditions. if changing the name is an option for you then check after changing it, worked for me...
gem Squeel uses instance_eval method when calls block which passed to where. So, there is no any #type in squeel instance. If you want to use methods or variables from another context, try to wrap it into method my with block
orders = Order.where { type.eq my { #type } } if #type.present?
PS sorry for my English

Calling a Rails constant in a module

I declared a module MigrationProcedures in app/concerns, which I would like to use to execute some unsupported ActiveRecord SQL statements.
Inside this module, I need to call the constant
[RAILS_ENV]['database']
but as it stands right now, rails tells me that there is an uninitialized constant RAILS_ENV in the method.
I should not that this is being used under
def self.included(base)
...
end
I tried to use AppName::RAILS_ENV but that tells me that MigrationProcedures::AppName dosen't exist. How can I call this constant?
You can get at the Rails environment a couple of ways:
c = ::Rails.application.config
my_env = ::ENV
In the first case c.database_configuration[Rails.env]["database"] gives you what you want.
The ::SOME_MODULE syntax gets you out of your current namespace and into a new one. Without a module name, it takes you to the top of your Ruby environment.
This Stack Overflow discussion is very illuminating.

Resources