ruby ::Module or just Module - ruby-on-rails

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).

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.

ruby require relative quesiton

Hello I am trying to learn how to make a ruby on rails app. I am stuck on the require_relative syntax. Currently I have a game_runner.rb file, that is not in the app directory, that is supposed to get a method (who_is_hider) from role.rb that is in models. Am I typing it correctly? When I run the current code I get an "uninitialized constant ApplicationRecord" error. This makes no sense since I have a application_record.rb file that has (self.abstract_class = true) inside. Thank you for your time.
game_runner.rb
require_relative './app/models/Role'
who_is_hider
role.rb
class Role < ApplicationRecord
belongs_to :round
belongs_to :player
enum label: {seeker: 1, hider: 2, decoy: 3}
validates :player_id, uniqueness: {scope: :round_id}
def who_is_hider
p "made it here"
end
end
application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
I am stuck on the require_relative syntax. […] Am I typing it correctly? When I run the current code I get an "uninitialized constant ApplicationRecord" error.
Yes, your syntax is correct. If your syntax were incorrect, your code wouldn't even run in the first place, and you would get a SyntaxError. The fact that your code does run and produce a NameError exception shows without a doubt that your syntax is correct.
As an easy way to check whether your syntax is correct, you can
visually inspect the syntax coloring in your editor,
visually inspect the syntax coloring in the Stack Overflow question, if you post one,
run ruby -c to check the syntax
The last one is the preferred option if you have it available, of course. But sometimes, you are writing code in an environment where you don't have a Ruby execution engine available, and in that case, you can at least use your editor or even the (admittedly not very good and easily confused) highlighting that Stack Overflow uses. While especially the latter one is not very good, it still does tend to highlight easy mistakes caused by typos, e.g. unterminated string literals.
Hmmm, you are using a single ruby file and expecting to load all rails environment? your Role model knows about ApplicationRecord because Rails loads a LOT of things on the background. If Rails is not in the middle then you have to explicitlly tell what thinks to load. You can require ApplicationRecord on the game_runner file or inside role.rb, but then you'll have to require ActiveRecord too.
I don't think it's a good idea to do what you are trying to do. If you wan't to run some script that depends on your environment, I'd recommend you to use a rake task, it will handle the loading of the environment. If you still want to make your game_runner.rb run as is, then you'll have to import everything the interpreter complains about until it works.

ActiveJob uninitialized constant

I have a weird problem with ActiveJob.
From a controller I'm executing the following sentence:
ExportJob.set(wait: 5.seconds).perform([A series of parameters, basically strings and integers])
ExportJob.rb
require_relative 'blablabla/resource_manager'
class ExportJob < ActiveJob::Base
def perform
ResourceManager.export_process([A series of parameters, basically strings and integers])
end
end
When the controller/action is executed for the first time the process goes fine, but the second time an error is thrown:
uninitialized constant ExportJob::ResourceManager
The weird thing is that this is not the only job I have in my project, the other ones are being executed without any problem.
I'm Attaching some information of my project:
development/production.rb
config.active_job.queue_adapter = :delayed_job
Gemfile:
gem 'delayed_job'
gem 'delayed_job_active_record'
Any clue would be a help for me.
Thanks in advance!
Constants don't have global scope in Ruby. Constants can be visible from any scope, but you must specify where the constant is to be found.
Without :: Ruby looks for the ResourceManager constant in lexical scope of the currently executing code (which is ExportJob class, so it looks for ExportJob::ResourceManager).
The following should work (assuming that ResourceManager is defined as a top level constant (eg not nested under any module/class):
class ExportJob < ActiveJob::Base
def perform
::ResourceManager.export_process(*args)
end
end

How to successfully inherit ActiveRecord::Base?

How to successfully inherit ActiveRecord::Base?
Environment: Ruby 2.0.0, Rails 4.0.3, Windows 8.1, PostreSQL 9.3.3, Devise 3.2.4
I have an operational app and would like to add a comprehensive logging class to it. This will be a complex class that not only logs messages but also creates an SQL database that logs transactions by object. I need this class available throughout all of the classes in the application.
To do this, I wanted to inherit ActiveRecord::Base into the class and then have all other classes inherit it, though I don't plan to use STI. That seemed to be a lot simpler in concept than in practice, even though I thought such inheritance was a common best practice. Am I missing something?
One of the initial tables was this:
class Device < ActiveRecord::Base
...
end
I set it up like this:
class XLog < ActiveRecord::Base
self.abstract_class = true
def initialize
end
end
class Device < XLog
...
end
Prior to this change, the app was working fine. After this change, when I login I receive:
ArgumentError at /devices/sign_in
wrong number of arguments (1 for 0)
The error occurs in:
bin/rails, line 4
bin/rails is:
#!/usr/bin/env ruby.exe
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'
Device is the Devise "User" class in this application and the error occurs when I try to login. If I change Device to inherit ActiveRecord::Base, it lets me login and run.
But, then I get another error whenever I call "new" on the other classes:
undefined method `[]' for nil:NilClass
I am definitely missing something when it comes to this inheritance. Advice appreciated.
The initialize method was throwing the error. It was triggering every time a subclass was initialized and was configured to accept 0 parameters. When I removed it, the whole thing started working. If I need it, I'll have to configure it to accept a variable number of parameters and pass them as expected, I guess.

Rails isolated tests running under rails env

I'm currently working on a project and we started migrating our tests to isolated test (no rails dependency, and using stubs and mocks).
The thing is that until all the current tests are being isolated, we have to run the tests together with the isolated tests, which will start the rails environment.
The problem comes when, in the isolated tests, there is a fake class (class Foo; end;), it will override the original class for the rest of the tests.
Example:
In the foo_spec.rb we have this line
class Bar; end;
This would override the Bar class for the next nonisolated tests, and would cause a lot of fails.
There are 2 approaches I could figure in order to get rid of this:
- either comment out the fake classes when the tests are run with rails env
- put the isolated tests in another folder and run them separated from the rest (this would make more sense)
Can you think of a nicer way to deal with this?
we are using rspec (which should not change anything) and have our rails spec located in spec with their own spec_helper.rb file that is loading the env and all the ugly stuff.
in spec_fast folder we have all the spec that can run without rails, with their own spec-helper that only loads our independent lib folder.
for our ci-server we let both spec folder run in a different task:
if Rails.env.test?
require 'rspec/core/rake_task'
require 'ci/reporter/rake/rspec'
RSpec::Core::RakeTask.new(:all_fast) do |t|
t.pattern = 'spec_fast/**/*_spec.rb'
end
RSpec::Core::RakeTask.new(:all_slow) do |t|
t.pattern = 'spec/**/*_spec.rb'
end
task :all => ["ci:setup:rspec", :all_fast, :all_slow]
end
it should also be possible to just put them into separate subfolders like spec/rails and spec/fast but i did not try it out because it means to do a lot of path-changing in spec-files.
I don't know if it's right, but I end up not actually assigning such contextual dummy manually created dummy classes to constants.
Instead of:
#no
class Foo
#something
end
Instead:
foo = Class.new do
#stuff
end
And you can foo.new or foo.class_method to your heart's content. Could be in #foo too. But you aren't assigning it to the constant Foo like ordinary class definition does, you're creating an 'anonymous' class and assigning it to an ordinary variable, scoped to just within the area you need it.
Note: I'm not saying this is "right" way to do things with rspec, I never feel like I know the right thing to do, the right thing to do might be to somehow not create classes like this at all, or use some weird factorygirl thing which I don't understand or something. But when I need to create 'dummy' type classes just to the scope of a particular test or block, that's what I do.

Resources