Clearly there is something off with how I understand it in ruby. I have 3 ruby files main, base, and derived. I have two classes Derived specializes from Base.
/bin/main.rb
require './lib/base'
/lib/base.rb
require './lib/derived'
class Base
end
/lib/derived.rb
require './lib/base'
class Derived < Base
end
running with rake ruby './bin/main.rb'
`<top (required)>': uninitialized constant Base (NameError)
What is causing the error?
Edit:
I just realized one point I was missing was that I forgot that require is a Kernel#method, that I do not need to keep on top of my code every time like I normally do with other languages.
The issue here is that the require './lib/derived' in the /lib/base.rb file is actually causing /lib/derived.rb to be parsed before Base is declared in /lib/base.rb. Try this, instead:
/bin/main.rb
require './lib/base'
require './lib/derived'
puts 'Success!'
/lib/base.rb
class Base
end
/lib/derived.rb
require './lib/base'
class Derived < Base
end
This allows the declarations to occur in the proper order.
As a side note, it isn't technically necessary to require './lib/base' in lib/main.rb, since it's actually successfully included in lib/derived.rb, but it is good form if Base is used directly in the body of lib/main.rb.
Related
While debugging an unrelated issue in rspec, I'm coming across issues with constant loading.
The setup is as follows:
# app/models/foo.rb
class Foo << ApplicationRecord
include Foo::Searchable
end
# app/models/foo/searchable.rb
module Foo::Searchable
extend ActiveSupport::Concern
included do
#yada yada
end
end
I received the following error while debugging. NameError: uninitialized constant #<Class:0x00007fadd32ea580>::Searchable
Changing the naming to Foos::Searchable with corresponding folder move does fix the issue but I would like to understand what is actually happening.
Rails 6.0.3.1 Ruby 2.6.6
As well as using << instead of < you have fallen victim to really common issue due to missusing the scope resolution operator ::. It should not be used when declaring nested classes or modules as it leads to the wrong module nesting and will lead to a missing constant error unless the module which you are nesting in is already loaded.
You should always explitly declare nested modules/classes:
class Foo < ApplicationRecord
module Searchable
extend ActiveSupport::Concern
included do
#yada yada
end
end
end
The reason this issue is popping up everywhere after the transition to Rails 6 is that the old classic autoloader overloaded Module#const_missing and glassed over these bugs as it would autoload Foo.
Zeitwork which replaces the classic autoloader instead uses Module#autoload which is a relatively new addition to Ruby and a lot less hacky.
This turns out to be caused by incompatibility with Byebug. Byebug stops the events Zeitwerk uses for autoloading to fire off in a debugging session. The issue is described here.
The << was a typo and would not result in that error.
Perhaps you should replace << with <. You inherit, not redirect
class Foo < ApplicationRecord
include Foo::Searchable
end
I am getting the "superclass mismatch for class" error when I run reload! in the Rails console. I have some super simple classes defined in ruby, something like this:
# base_class.rb
module A
module B
module C
class BaseClass
def close
#stub
end
end
end
end
end
And:
# more_specific.rb
module A
module B
module C
class MoreSpecific < BaseClass
def initialize
# ...
end
def close
end
end
end
end
end
I can see that in fact there's a problem because if I do this before I do reload!:
A::B::C::MoreSpecific.superclass.equal? A::B::C::BaseClass
I get true, and then if I do it after I get the error, I get a false. Additionally, the object_id of the BaseClass does in fact change.
Why might this happen? I've checked for additional references to the MoreSpecific class in the codebase because I thought that might lead to the BaseClass being established as a constant more than once, but did not see anything.
What could be causing the object_id of A:B:C::BaseClass to switch after the reload!?
Autoloading Modules Without a Require Statement
reload! is a Rails console method, not a standard Ruby method. While there could be other causes for the behavior you're seeing, it's worth noting that your C module in more_specific.rb doesn't require base_class at runtime, and may be losing its lookup; Rails may not autoload modules the way you're expecting without it.
Make sure that modules that depend on BaseClass contain a require base_class statement to be executed when the module reloads. If that doesn't resolve it, there may be other problems with your code as well that aren't shown in your current post.
I'm using ActiveRecord with Sinatra instead of Rails, and I want to use fixtures in my tests. The documentation for ActiveRecord's FixtureSet says that you have to use fixture_path to tell it where the fixture files are:
placed in the directory appointed by ActiveSupport::TestCase.fixture_path=(path)
How can I write to that setting? I tried #fixture_path and ##fixture_path, but both of them left the value nil when FixtureSet tried to read it.
Here's the only thing I could get to work, but it can't possibly be right:
# test_helper.rb
require_relative '../app'
require 'minitest/autorun'
require 'active_record'
ActiveRecord::Base.establish_connection(:test)
#Set up fixtures and such
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
include ActiveRecord::TestFixtures::ClassMethods
class << self
def fixtures(*fixture_set_names)
self.fixture_path = 'test/fixtures'
super *fixture_set_names
end
end
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
end
The full source code is posted as a small demo project for ActiveRecord and Sinatra.
I tried to leave this as a comment on your answer, but it got too long so I thought I might as put it in an answer.
The reason #fixture_path and ##fixture_path didn't work is that fixture_path is an ActiveSupport class attribute, which is like a Ruby attr_accessor except it's defined as a singleton method on the class. You can see where the fixture_path attribute is defined with class_attribute :fixture_path in the ActiveRecord::TestFixtures module source.
Class attributes are part of ActiveSupport and not native to Ruby. You can read more about them in the Active Support Core Extensions Rails Guide and in the API docs, and see how class_attribute works in the Rails source. As you can see,
the value is stored in the instance variable "##{name}" (e.g. #fixture_path), but that happens inside a singleton method, which means it's an instance variable on the singleton class and you can only access it from within the singleton class.
That's all a little bit moot, though, because the point of attributes (and feel free to disregard this if it's old news to you) is that they allow you to keep instance variables private and change your implementation without breaking code that subclasses or includes your code. When an attribute reader or writer exists, you should always use it instead of accessing the instance variable directly, because at some point the implementation might change and the attribute methods could be replaced by methods with more complex logic, and accessing the instance variable directly will no longer produce the same results as using the attribute reader and writer.
As you discovered, you need to use self.fixture_path = instead of fixture_path = because in the latter case Ruby assumes you want to assign to a local variable.
I can't believe I didn't see this, but I didn't. I had to use self, just like the settings for transactional fixtures and instantiated fixtures.
# test_helper.rb
require_relative '../app'
require 'minitest/autorun'
require 'active_record'
ActiveRecord::Base.establish_connection(:test)
#Set up fixtures and such
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
include ActiveRecord::TestFixtures::ClassMethods
self.fixture_path = 'test/fixtures'
self.use_transactional_fixtures = true
self.use_instantiated_fixtures = false
end
The trick is understanding the meaning of self in a class definition; it refers to the class, not an instance of the class. I guess when I'm monkey patching ActiveSupport::TestCase, that's the only way to set a class variable. For some reason #fixture_path and ##fixture_path don't work.
[SOLVED: See my comment below]
I've created a Ruby Gem to connect to my application's API: my_app_api. I'd like to use it like so: MyAppAPI::Foo.bar(). However, I get:
NameError: uninitialized constant MyAppAPI
I know the standard way to call/name this would be MyAppApi::Foo.bar(), but I'd prefer to keep with acronym class naming conventions. How do I specify/load the module?
For reference, the class looks like this:
module MyAppAPI
class Foo < ActiveResource::Base
extend MyAppAPI
self.site = 'http://localhost:3000/api/'
self.format = :json
class << self
def bar
return 'huzzah!'
end
end
end
end
And the my_app_api.rb file looks like this:
require "rubygems"
require 'active_resource'
require 'my_app_api/foo'
Have you tried loading the gem the normal way?
require 'my_app_api'
MyAppAPI::Foo.bar()
The constant name MyAppAPI is fine and is not the cause of the problem. There are tons of Ruby core classes/modules that have acronyms in their names:
http://www.ruby-doc.org/core-1.9.3/GC.html
http://www.ruby-doc.org/core-1.9.3/RubyVM.html
http://ruby-doc.org/stdlib-1.9.3/libdoc/csv/rdoc/CSV.html
Try declaring the empty module in my_app_api.rb after your require statements:
module MyAppAPI
end
This may help if you're relying on a dynamic class and module loading mechanism (like Rails uses).
I assume your app is explicitly calling require "my_app_api". What kind of app is this, and where are you doing the require?
In Ruby I can require all class files in a directory at once:
Dir["/path/to/directory/*.rb"].each {|file| require file }
But what if the classes have inheritance relationships? How to avoid requiring the subclass before superclass? (or it's impossible?)
Actually there is a dirty hack, suppose you've named all your files correspond to class names. But this approach works only if you have just inheritance relationships between different classes. Method overriding multiple times is not taken into account.
Let's say you have two rb files in the to be required directory: engineer.rb and human.rb.
-- engineer.rb
class Engineer < Human
end
-- human.rb
class Human
end
And in another file which is gonna require them, if you specify engineer first you'll get an error:
require 'engineer' #=> uninitialized constant Human (NameError)
require 'human'
So we can simply play with the powerful weapon autoload:
Dir['/path/to/directory/*.rb'].each do |f|
module_name = /\/([^\/\.]+?)\.rb$/.match(f)[1].capitalize.to_sym
autoload(module_name, f)
end
#=> autoload :Engineer, 'engineer'
#=> autoload :Human, 'human'
puts Engineer.new #=> #<Engineer:0x007f91728847e8>
Basically autoload will require file on demand, thus the sequence of requiring is no more a problem.
If you want to know more about autoload, please check here:
http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-autoload
http://www.rubyinside.com/ruby-techniques-revealed-autoload-1652.html
But please be aware that autoload will be dead soon (because it's not thread safe), please refer to here (by Matz):
http://www.ruby-forum.com/topic/3036681