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.
Related
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.
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.
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
I wanted to know how ActiveRecord::Base.connection.execute method is defined.
So I've checked source code but I couldn't understand what is going on.
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
end
undef_method :execute
https://github.com/rails/rails/blob/c13284131511fb7871a85659dd0b5e821d9ad29b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L55
Perhaps the method is defined another place dynamically.
How can I find place where the method is described?
The method you show is defined in the module DatabaseStatements which is included into the class AbstractAdapter (connection_adapters/abstract_adapter.rb).
AbstractAdapter simply serves as a base class for the various specialised database adapters for the different database servers Rails interoperates with; it's not intended to be instantiated on its own. For instance, the definition of execute for PostgreSQL databases is in postgresql/database_statements.rb, as part of class PostgreSQLAdapter < AbstractAdapter.
They are defined in respective adapters.
The adapters are in the following directory names as *_adapter.rb:
activerecord-x.x.x/lib/active_record/connection_adapters/
You can see the the definition of the execute method inside those files. like: mysql_adapter.rb, postgresql_adapter.rb etc.
To know how ActiveRecord::Base.connection.execute method is defined you should look in the connection adapter class you're using.
For instance, in case you're using mysql db (via mysql2 gem) you'll find the execute method definition you're using here:
activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#206
I'm slowly making my way through the rails source, to get a better grip on ruby and rails in general. In the following rails class test_case.rb
the line is
class TestCase < ::Test::Unit::TestCase
and I was wondering if there is any difference with doing the following
class TestCase < Test::Unit::TestCase
It may seem trivial, but these things matter picking up a new language. The tests still run for ActiveSupport if I remove the leading :: so what does it do... :P
::Test ensures that you get a toplevel module named Test.
The latter case (Test::Unit::TestCase) doesn't ensure that Test is a toplevel module, it could be a class, for example. It means that most of the time, it'll work, but you could accidentally break it.
Imagine you have this code
module UserTesting
class Test # Details about a simple test
end
class TestCases < Test::Unit::TestCase
end
end
# => NameError: uninitialized constant UserTesting::Test::Unit
This would error out since your Test constant available to the class you are creating has no Unit constant in it. If you address it by :: this is like the leading slash in a path.
There is also a special case for using these - you can be evaluating your code in something else than the default root namespace, and there you actually need the double colon to address classes like ::Object (usually to monkeypatch them).