undefined method `configure' for MyGem:Module - ruby-on-rails

Context of the Problem
gem 'leanpirates-aarrr', '~> 0.1.1', :path => "/src/gem-aarrr" runs ok
rails g shows the generator lean_pirates:aarrr:install
the initializer is successfully created under config/initializers/leanpirates_aarrr.rb
but the initializer FAILS when trying to configure the gem
The initializer: leanpirates_aarrr.rb
LeanPirates::Aarrr.configure do |config|
config.api_server = "http://localhost:3000"
config.startup_key = Rails.configuration.leanpirates_aarrr.startup_key
config.startup_secret = Rails.application.secrets.leanpirates_startup_secret
end
The gem definition file: aarrr.rb
# (...)
module LeanPirates
module Aarrr
class << self
attr_writer :configuration
end
def self.configuration
#configuration ||= LeanPirates::Aarrr::Domain::Configuration.new
end
def self.reset
#configuration = LeanPirates::Aarrr::Domain::Configuration.new
end
def self.configure
yield(configuration)
end
end
end
The error
/src/app-ebusiness-v3/config/initializers/leanpirates_aarrr.rb:1:in <top (required)>: undefined method 'configure' for LeanPirates::Aarrr:Module (NoMethodError)
/src/app-ebusiness-v3/config/initializers/leanpirates_aarrr.rb:1:in `<top (required)>': undefined method `configure' for LeanPirates::Aarrr:Module (NoMethodError)
from /home/ubuntu/.rvm/gems/ruby-2.4.0/gems/activesupport-5.0.2/lib/active_support/dependencies.rb:287:in `load'
from /home/ubuntu/.rvm/gems/ruby-2.4.0/gems/activesupport-5.0.2/lib/active_support/dependencies.rb:287:in `block in load'
from /home/ubuntu/.rvm/gems/ruby-2.4.0/gems/activesupport-5.0.2/lib/active_support/dependencies.rb:259:in `load_dependency'
from /home/ubuntu/.rvm/gems/ruby-2.4.0/gems/activesupport-5.0.2/lib/active_support/dependencies.rb:287:in `load'
(...)
This is for the opensource LeanPirates AARRR from https://github.com/lean-pirates/gem-aarrr/tree/simplification (branch simplification)

The Autoloader feature in Rails is very handy, it takes care of a ton of things automagically, but it's also not very smart. If a namespace is already defined it will not load additional files.
It's your obligation as a gem to require any and all support modules or classes that are necessary for your library to work properly.
Here's the problem in lib/gem/aarrr/version.rb:
module LeanPirates
module Aarrr
VERSION = '0.1.1'
end
end
Now LeanPirates::Aarrr is already defined so no additional work by the autoloader is required. As such, your lib/gem/aarrr.rb is never loaded.
To fix this you need to avoid creating that namespace path. You should also ensure that require_paths is set properly in your .gemspec file so that lib/gem is referenced correctly. Right now you'd have to require 'gem/aarrr' which is really confusing and clunky.
I usually set up a plain-old VERSION file in the main gem directory that has nothing but the version string. The gemspec can read this in on-demand if required with File.read, or you can bake it into the .gemspec using a rake task. I prefer the latter approach as making a .gemspec with tons of arbitrary code in it is always over-complicating things.

Problem solved
#tadman guided me in the right direction with his explanation (thanks very much for that), although the problem was bigger than I could initially assess.
The Rails Autoloader was having trouble to load my files. Why? Because I tried to organise them.
The problem was
Namespacing! Long ago, when the gem was started, it felt reasonable to move everything into a /lib/gem folder. However, it proved to be the source of the problem.
With the namespacing not matching the folder structure properly, the Autoloader was having trouble loading the files, and they were not getting recognised along with the classes of my gem, etc.
How was the Namespacing sorted?
When you are doing your gems, make sure that you understand well namespacing. In my case, generating the plugin with the following command line sorted out the problem:
rails plugin new lean_pirates-aarrr
This line above created things in the way they should be for the auto-loader to pick up. It's not something we can complain about: Rails follows a lot the "convention over convention" paradigm.
My end structure was the following:
(https://github.com/lean-pirates/gem-aarrr/tree/simplification)
- lib/
-> generators/
-> lean_pirates/
-> aarrr/
-> install/
-> routines/*.rb
-> templates/*.rb
-> *.rb
-> lean_pirates/
-> aarrr/
-> domain/*.rb
-> helpers/*.rb
-> *.rb
I'd be happier to make /generators and /gem at the root, and I'm sure it would be possible, but I don't really wanna fight the convention, so I'm pretty happy to leave it as is, at least for now.
If you are facing the same problem, fixing the folder structure might help the Autoloader to find your files. Change it, or configure it accordingly.

Related

Ruby on Rails: Using Modules

I am relatively new to Ruby on Rails, but I have almost completed the online course on Lynda. I am stuck on 14-5 "Using the positionMove module". The tutorial is in Rails 3 while my version is Rails 4. I have the solution code, however it does not work for me. I have put my file position_mover.rb in the lib directory. Then in my subject model I require and include the module like this:
require 'lib/position_mover'
class Subject < ActiveRecord::Base
include PositionMover
...
end
I used the methods in this model in the Subject controller just like the instructor. However, when I go to run my app on the Subjects index page I get the error:
cannot load such file -- lib/position_mover
app/models/page.rb:1:in `<top (required)>'
app/views/subjects/list.html.erb:23:in `block in_app_views_subjects_list_html_erb__420960863_60005508'
app/views/subjects/list.html.erb:18:in `_app_views_subjects_list_html_erb__420960863_60005508'
app/controllers/subjects_controller.rb:9:in `index'
I have tried many different approaches that I found online such as adding it to the class definition (Ruby on Rails 4.0 - Loading and using a module) or moving it to a different directory and specifying the path. How do I achieve the same result in Rails 4? What am I doing wrong? Any help is greatly appreciated.
require "#{Rails.root}/lib/position_mover"
or
require_relative 'lib/position_mover'
You also can auto-loading lib files.
in config/application.rb:
config.autoload_paths << Rails.root.join('lib')

Spree Commerce extending helpers

I am attempting to extend one of the helper modules which Spree Commerce offers. I came across the following file: spree/frontend/app/helpers/frontend_helper.rb
Ultimately what I am trying to do is add a simple helper method to this module. I plan to call this helper method for use with the single product page. My implementation seems to be working fine with a couple exceptions, which has led me to believe that I have screwed up somewhere. Here is what I have done:
I have the file app/helpers/spree/frontend_helper_decorator.rb. The contents of this file:
Spree::FrontendHelper.module_eval do
# Create Variant Matrix
def create_variant_matrix( variants )
#valid_variants = {}
...
#valid_variants
end
end
The next thing I have done is extended the Products Controller for the purpose of overwriting one of the methods. I created app/controllers/spree/products_controller_decorator.rb. The content of this file:
Spree::ProductsController.class_eval do
include Spree::FrontendHelper
# Overwrite Show to include variant matrix
def show
#variants = #product.variants_including_master.active(current_currency).includes([:option_values, :images])
#product_properties = #product.product_properties.includes(:property)
#taxon = Spree::Taxon.find(params[:taxon_id]) if params[:taxon_id]
#vMatrix = create_variant_matrix( #variants )
end
end
As you can see here, I have overwritten the show method, copied the code from the default spree method and added my custom call. I have also included the FrontendHelper module in the controller.
This works without any errors with few exceptions. When I run bundle for the purpose of installing a new gem for instance, I typically get an error which requires me to remove my custom FrontendHelper method completly before I can successfully install the gem. Once the gem is installed, I add the code back in and it works fine. The error I receive is:
/fake/path/app/controllers/spree/product_controller_decorator.rb:2:in `block in <top (required)>': uninitialized constant Spree::FrontendHelper (NameError)
I have tried searching for this error with no luck. I am fairly new to Ruby (background is in PHP), but based on the tutorials I followed, I feel that it is correct. It also seems to be working properly in most scenarios.
Any help is appreciated.

Rails unable to autoload constant from file despite being defined in that file

This is a tricky one to explain. I have a module in another module namespace like so:
# app/models/points/calculator.rb
module Points
module Calculator
def self.included(base)
base.send(:include, CommonMethods)
base.send(:include, "Points::Calculator::#{base}Methods".constantize)
end
end
end
So then in other classes all I need to do is:
class User
include Points::Calculator
end
I've specified this directory in application.rb to be autoloadable...(even though i think rails recurses through models...)
config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
In development env, everything works fine. When running tests(and production env), I get the following error:
Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
I actually followed the advice here to fix the problem: Stop Rails from unloading a module in development mode by explicitly requiring calculator.rb in application.rb.
However, why is this happening??
I stuck some debug output in ActiveSupport's dependencies.rb file and noticed that this file is being required twice. The first time its required I can see that the constant is indeed loaded.
But the 2nd time its required the constant has been unloaded as far as Rails can tell, but when the actual require is called, ruby returns false because ruby knows its already required it. Then Rails throws the "unable to autoload constant" error because the constant still isn't present and ruby didn't "re-require" the file.
Can anyone shed light on why this might be happening?
Rails augments the constant lookup mechanism of ruby.
Constant lookup in Ruby:
Similar to method missing, a Module#constant-missing is invoked when a reference to a constant fails to be resolved. When we refer to a constant in a given lexical scope, that constant is searched for in:
Each entry in Module.nesting
Each entry in Module.nesting.first.ancestors
Each entry in Object.ancestors if Module.nesting.first is nil or a module.
When we refer to a constant, Ruby first attempts to find it according to this built-in lookup rules.
When ruby fails to find... rails kicks in, and using its own lookup convention and its knowledge about which constants have already been loaded (by ruby), Rails overrides Module#const_missing to load missing constants without the need for explicit require calls by the programmer.
Its own lookup convention?
Contrasting Ruby’s autoload (which requires the location of each autoloaded constant to be specified in advance) rails following a convention that maps constants to file names.
Points::Calculator # =>points/calculator.rb
Now for the constant Points::Calculator, rails searches this file path (ie 'points/calculator.rb') within the autoload paths, defined by the autoload_paths configuration.
In this case, rails searched for file path points/calculator in its autoloaded paths, but fails to find file and hence this error/warning is shown.
This answer is an abstract from this Urbanautomation blog.
Edit:
I wrote a blog about Zeitwerk, the new code reloader in Rails. Check it out at -> https://blog.bigbinary.com/2019/10/08/rails-6-introduces-new-code-loader-called-zeitwerk.html
If someone is having this issue in rails 6 which has zeitwerk autoloader,
Change ruby constant lookup back to classic in your application.rb
# config/application.rb
#...
config.autoloader = :classic
#...
Read more details here Rails Official Guides
Calculator should be a class to be autoloaded correctly
module Points
class Calculator
...
end
end

Rails: You cannot have more than one Rails::Application (RuntimeError)

I installed a pesapal gem with an initializer file that looks like this;
# Load Pesapal config file when applicatin is loaded ... the config can then be
# accessed from PesapalRails::Application.config.yaml
module PesapalRails
class Application < Rails::Application
config.yaml = YAML::load(IO.read("#{Rails.root}/config/pesapal.yml"))
end
end
When i use it i get the error like this;
/usr/local/lib/ruby/gems/1.9.1/gems/railties-3.2.13/lib/rails/application.rb:63:in ``inherited'``: You cannot have more than one Rails::Application (RuntimeError)
With a partial trace looking like this;
from /var/www/html/webapp/config/initializers/pesapal.rb:4:in `<module:PesapalRails>'
from /var/www/html/webapp/config/initializers/pesapal.rb:3:in `<top (required)>'
Fix
Upgrade the gem, v1.2.1 should fix the problem (changelog)
Short Explanation
Simple version ... my approach was wrong - sincere apologies. New to Ruby. New to Rails.
Long Explanation
The initializer was meant to create a global variable that you could access in your rails app. In my case it was PesapalRails::Application.config.yaml. I'd assume in your case, it was different.
The gem assumed that that global variable was set, correctly, which would bring an error since in your app PesapalRails doesn't exist (as you would know, the initializer is ran only when the app is started so this was kinda a safe assumption). This went undetected in my case since my demo app was actually using the PesapalRails namespace.
Request
Wish you would have filed an issue on Github - here next time (it's sheer luck I stumbled on this) ... this way other devs might actually contribute to the discussion/solution. I'm willing to review and push up updates asap.

Converting Rails 2 plugin to Rails 3 gem

So there's this great plugin I've gotten used to using in my Rails 2 projects called Bootstrapper. It essentially duplicates the functionality of the seeds.rb file, but I like it because it lets you break up your bootstrap process into concise chunks.
Anyway, I've gone so far as to fork the project and attempt to turn it into a Rails 3 gem. I've been able to get the gem to initialize and register the rake tasks and generators OK. However, I'm running into a problem with the Bootstrapper class itself. It won't load in the Rails project unless it's in a module.
That is, if I place the Bootstrapper class in a file by itself and require that file in my Railtie, then in my Rails app, it can't find the Bootstrapper class. If I put the class in a module and call Bootstrapper::Bootstrapper everything is peachy.
The code that actually requires the Bootstrapper class is this:
ActiveSupport.on_load :active_record do
require 'bootstrapper/bootstrapper'
end
The source is available here:
http://github.com/jrmehle/bootstrapper/tree/make_gem
Autoload paths actually has an annoying feature of following filesystem paths. For example in your lib or extras (depending on what you autoload) you might have the following file structure:
lib/bootstrapper/bootstrapper.rb
# in this case, Bootstrapper::Bootstrapper.class = Class in rails c
# ie: you don't get a NameError exception
More specifically,
lib/bootstrappers/bootstrapper.rb
# Bootstrapper::Bootstrapper => NameError
# Bootstrappers::Bootstrapper => works
If you really want the other way, you can move everything into your lib/bootstrapper.rb source file but meh, I don't like doing that, that's not how gems are organized. In rails3, you'll find the autoloading pretty nice once you use modules everywhere (which can be painful).
Rails3 uses /extras instead of /lib but it's not required, it's just the default (commented out) from rails new. To switch, you just autoload extras instead of lib.

Resources