Rails: trouble including modules via a plugin - ruby-on-rails

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

Related

Rails include behaviour?

On this project setup :
RoR : 6.0.3.4
Ruby: 2.7.1p83
I have this bit of code serving as a Nokogiri helper - within app > helpers > nepper > helpers > nokogiri.rb (a bit redundant fo'sure) :
# frozen_string_literal: true
module Nepper
module Helpers
# Dedicated to Nokogiri utilities
module Nokogiri
# HTML::Document extension
class ::Nokogiri::HTML::Document
def has?(css_selector)
# css_selector (symbole): const name
css(css_selectors.const_get(css_selector)).present?
end
def text_at(css_selector)
# css_selector (symbole): const name
elt = css(css_selectors.const_get(css_selector))
# raise TypeError 'No text on node' unless elt.text.present?
elt.text
end
def href_at(css_selector)
# css_selector (symbole): const name
elt = css(css_selectors.const_get(css_selector)).first
# raise TypeError 'Not a link' unless elt[:href].present?
elt[:href]
end
end
# css selectors constants shortcut
def css_selectors
Nepper::Constants::CssSelector
end
end
end
end
and within a model declaration, something looking like this
class Card
include Mongoid::Document
include Nepper::Helpers::Nokogiri
# [...]
end
My issue is that, within rails console, I get undefined method for Nokogiri::HTML::Document error for css_selectors whenever an instance of the class is using any of the previous added methods.
More troublesome : It worked just fine yesterday right before pushing the code.
I could put the method within the Nokogiri::HTML::Document method implementation, but I want to keep this bit of code accessible whenether a file includes it, without having to use a Nokogiri instance nor duplicating the code.
So ... What's going on with this weird load behaviour ?
I don't get why the Nokogiri::HTML::Document instances don't access the module method anymore ... both should exist once this code is included isn't it ?
Edit : Btw I had the same issue with TypeError being errored as undefined - for why I had to comment those raise error parts (also weird). This is as if some fo the Main object modules are not loaded prior to those methods calls.

Ruby - Check if controller defined

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

Rails::Application.initialize! method source location

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.

Include plugin in module in Rails

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

Making custom changes to AssetTagHelper into a plugin

I have written some custom code that make changes to some of the methods in ActionView::Helpers::AssetTagHelper
An example:
module ActionView
module Helpers
module AssetTagHelper
alias old_existing_method existing_method
def existing_method
puts "Does foobar"
return old_existing_method
end
end
end
end
Now normally i would keep this code code in RAILS_ROOT/config/initializers/asset_helper_overrides.rb
This works as expected.
Now i want to turn this into a plugin.
I copied this file to the plugin location and the i would require it in init.rb file.
However, this doesn't seem to work.
I'm not sure why this doesn't work.
I'm guessing maybe its because the file is required before ActionView::Helpers are required. Not sure.
Can someone help me out here. Thank you.
I'd suggest creating a module for this, and including your module into the helper. Here's an example:
module AssetTagExtensions
def self.included(base)
base.alias_method_chain :existing_method, :added_cleverness
end
def existing_method_with_added_cleverness
puts "Does foobar"
existing_method_without_added_cleverness
end
end
Then, in your init.rb file you should do something like:
ActionView::Helpers::AssetTagHelper.module_eval do
include AssetTagExtensions
end
It's important to call module_eval (or send) on the ActionView::Helpers::AssetTagHelper class rather than reopening it, to ensure you don't prevent it from loading correctly.

Resources