I want to find the source for following line in my blog application's environment.rb
Blog::Application.initialize!
If found that the initialize! method is defined only in one place in the source code.
railties/lib/rails/application.rb
def initialize!(group=:default) #:nodoc:
raise "Application has been already initialized." if #initialized
However it is an instance method.
I did some experiments in the rails console:
Blog::Application.initialize! # => Application has been already initialized
Blog::Application.method(:initialize!) # => undefined method for Class
Blog::Application.instance_methods.include?(:initialize!) # => true
Its strange that we get an undefined method for initialize! when it is clearly callable on the Application class. Please help in understanding this code.
This is due to some Ruby Magic aka "Method Missing":
Blog::Application.ancestors
=> [... Rails::Railtie::Configurable ...]
Have a look at configurable.rb:
module Rails
class Railtie
module Configurable
extend ActiveSupport::Concern
module ClassMethods
[...]
def method_missing(*args, &block)
instance.send(*args, &block)
end
end
end
end
end
it delegtes all the calls to the underlying application instance.
Related
I’m using Ruby on Rails 6.1.4.4. I want to override a method in a gem, whose signature is this
module Juixe
module Acts
module Commentable
module ClassMethods
def acts_as_commentable(*args)
…
end
end
end
end
end
So I tried creating a file, lib/ext/acts_as_commentable_extensions.rb, and including this code
require 'acts_as_commentable'
module GemExtensions
module Commentable
def hello
print "hello\n"
end
def acts_as_commentable(*args)
abcdef
end
end
end
module Juixe
module Acts
module Commentable
module ClassMethods
include GemExtensions::Commentable
end
end
end
end
Juixe::Acts::Commentable::ClassMethods.instance_method(:hello).source.display
Juixe::Acts::Commentable::ClassMethods.instance_method(:acts_as_commentable).source.display
Although the first statement prints out the correct source code from my new method “hello,” the second prints out the old source code from the original gem as opposed to my new code. How do I override this method with my own code?
Try prepend GemExtensions::Commentable instead of include, it will make Ruby to search for the method first in prepended module. More explanation here https://medium.com/#leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073
I am using Solidus with Ruby on Rails to create a webshop and I have multiple modules for that webshop.
So, I defined a me controller into an module called 'solidus_jwt_auth' with the followin code:
module Spree
module Api
class MeController < Spree::Api::BaseController
def index
...
end
def orders
...
end
def addresses
...
end
end
end
end
I want to extend this in another module called 'solidus_prescriptions' so I created a decorator for this with the following code me_decorator:
if defined? Spree::Api::MeController.class
Spree::Api::MeController.class_eval do
def prescriptions
...
end
def create_prescription
...
end
private
def prescription_params
params.require(:prescription).permit(
*Spree::CustomerPrescription.permitted_attributes
)
end
end
end
And for this I wrote unit tests in solidus_prescription module and integration tests in webshop. The unit tests are working fine, but the integration tests are giving the following error:
Error:
MeEndpointsTest#test_me/prescriptions_post_endpoint_throws_an_error_when_wrong_params:
AbstractController::ActionNotFound: The action 'create_prescription' could not be found for Spree::Api::MeController
test/integration/me_endpoints_test.rb:68:in `block in '
Which means that he can not find the MeController defined in another module. How can I make the check if the MeController is defined since the code bellow does not help me with anything:
if defined? Spree::Api::MeController.class
end
This worked in the end:
def class_defined?(klass)
Object.const_get(klass)
rescue
false
end
if class_defined? 'Spree::Api::MeController'
....
end
if defined? should do exactly what you want it to do in theory. The problem is you're checking if defined? Spree::Api::MeController.class. The #class of your class is Class. So what you're really getting is if defined? Class which will always be true!
This issue is most likely not that the conditional is failing but that it's never getting read. Rails lazy loads most of the code you write, meaning the file is not read until it's called somewhere in execution.
The decorator module should just contain the methods you want to add, without the conditionals or the use of class_eval. Then in the original class you can include it.
module Spree
module Api
class MeController < Spree::Api::BaseController
include MeDecorator
end
end
end
If for any reason you're not certain MeDecorator will be defined, don't use defined?, because defined? MeDecorator will not actually go looking for it if it's not defined and load the necessary file. It will return nil if the constant has no value. Just rescue a NameError
module Spree
module Api
class MeController < Spree::Api::BaseController
begin
include MeDecorator
rescue NameError => e
logger.error e
end
end
end
end
I am extending ActiveRecord::Base. In my lib folder, I have a file mongoid_bridge.rb:
module MongoidBridge
extend ActiveSupport::Concern
module ClassMethods
...
end
module InstanceMethods
...
def create_mongo(klass, fields)
...
end
end
end
ActiveRecord::Base.send(:include, MongoidBridge)
In config/initializers, I have two files, in order to be read in the correct order, each is prefixed with 01, 02, etc. In 01_mongo_mixer.rb, I have the following:
require "active_record_bridge"
require "mongoid_bridge"
Then in 02_model_initializer.rb, I have the following:
MyActiveRecordModel.all.each do |model|
model.create_mongo(some_klass, some_fields)
end
model is an instance of an ActiveRecord subclass, so it should find create_mongo instance method in the lookup chain. However, it does not find it, as I get the following error:
Uncaught exception: undefined method `create_mongo' for #<MyActiveRecordModel:0x007fff1f5e5e18>
Why can't it find the instance method?
UPDATE:
It seems that methods under ClassMethods are included, but not the methods under InstanceMethods:
singleton_respond = MyActiveRecordModel.respond_to? :has_many_documents
# => true
instance_respond = MyActiveRecordModel.new.respond_to? :create_mongo
# => false
You don't need an InstanceMethods module - your module should look like
module MongoidBridge
extend ActiveSupport::Concern
module ClassMethods
...
end
def create_mongo
end
end
Earlier versions of rails made use of an instance methods module but in the end it was decided that this was redundant since you could just define the methods in the enclosing module. Using InstanceMethods was deprecated a while back (maybe rails 3.2 - my memory is fuzzy) and subsequently removed
I'm trying to create a rails plugin and the problem I'm facing is that the app won't include my modules when migrating the plugin.
Here's what I have so far:
1. A file lib/patch/settings_helper_patch.rb with extension code
2. An init.rb file with require_dependency 'patch/settings_helper_patch'
3. Some code in settings_helper_patch.rb which is as follows:
module ValidateIssuePatch
module Patch
module SettingsHelperPatch
def self.included(base)
base.send(:include, InstanceMethods)
end
module InstanceMethods
def issue_options
#some code here
end
end
end
end
end
unless SettingsHelper.included_modules.include?(ValidateIssuePatch::Patch::SettingsHelperPatch)
SettingsHelper.send(:include, ValidateIssuePatch::Patch::SettingsHelperPatch)
end
After I migrate the plugin, I wish to use the issue_options method, but I get undefined local variable or method error.
If I run SettingsHelper.included_modules.include?(ValidateIssuePatch::Patch::SettingsHelperPatch) from the console, I get uninitialized constant Patch::SettingsHelperPatch.
However, if I call ValidateIssuePatch from the console, I get => ValidateIssuePatch in response.
Can anyone tell me what is the magic I'm missing here?
Firstly, if your module is only going to have instance methods, I would recommend using the following easy-to-follow syntax:
module ValidateIssuePatch
module Patch
module SettingsHelperPatch
def issue_options
# code
end
end
end
end
SettingsHelper.include(ValidateIssuePatch::Patch::SettingsHelperPatch)
Secondly, the reason why ValidateIssuePatch might be defined is that some other file has it which is being required properly. This file isn't being executed in any way. I would raise an error somewhere that, when raised, will verify that the code is / isn't being executed. Something like the following:
module ValidateIssuePatch
module Patch
module SettingsHelperPatch
raise "All good" # remove this afterwards
def issue_options
# code
end
end
end
end
SettingsHelper.include(ValidateIssuePatch::Patch::SettingsHelperPatch)
Chances are that the error won't be raised and it'll confirm that your file isn't being required - either not at all or not in the right order.
To further verify this, simply open up your console and do the following with your existing code:
ValidateIssuePatch::Patch::SettingsHelperPatch #=> error
require path_of_file
ValidateIssuePatch::Patch::SettingsHelperPatch #=> no more error
Finally, why do you check for the module already being included in SettingsHelper? (referring to the unless condition) Your code should be including the module only once, not "maybe only once".
This is a definite newb question:
I have a module defined in my lib/ directory that I call from a sweeper and as a rake task. Inside the module I want to reference my spawn plugin. Just including methods from that plugin doesn't work (undefined method error), nor does any version of require or include that I've tried. What do I need to do to include it?
Thank you!
EDIT:
Here is my latest effort:
require 'vendor/plugins/spawn/init.rb'
module MyModule
include Spawn
def self.my_method
spawn(:method => :thread, :nice => 9) do # also tried Spawn::spawn
...
end
end
end
The error I get is:
undefined method 'spawn' for MyModule:Module #spawn or undefined method 'spawn' for Spawn:Module # Spawn::spawn
The including has to be done from within the plugin. When you "reopen" a module (which is in the lib/ dir), remember to require the original file before making changes to it (the module).
EDIT:
Considering that the plugin is third party, you could try:
Spawn.module_eval do
module_function :spawn
public :spawn
end
module MyModule
def self.my_method
Spawn.spawn(...)
end
end