How to include a module in a class - ruby-on-rails

A module in my gem is included in a class in another gem, which is extended by a custom class in a Rails app:
My gem:
module MyGem
def my_method
end
end
AnotherGem.send :include, MyGem
Another gem:
class AnotherGem
end
Class in Rails app:
class ClassInRailsApp < AnotherGem
end
Running this leads to the following behavior:
$ rails c
Loading development environment (Rails 5.1.4)
irb(main):004:0> MyGem.method_defined? :my_method
=> true
irb(main):005:0> AnotherGem.method_defined? :my_method
=> true
irb(main):006:0> ClassInRailsApp.method_defined? :my_method
NoMethodError: undefined method `my_method' for ClassInRailsApp:Class
How can I make sure my module is included before the class is extended?
EDIT:
I tried to directly include MyGem in ClassInRailsApp and the specified instance method is still not available. Could the issue be related to that?

In your thinking, you're just calling a method, e.g. #object.my_method. In reality, you're calling Class level method, e.g. Object.my_method, but have it defined as an instance level method. The correct way to do what you're trying would be Object.new.my_method, however, don't do that.
To call a method like this you'd have to define it as a method on the class. See this page, for a better understanding. Specifically the section "A Common Idiom" on how to define Class level methods via a module.

Related

Rails: Reference an ActiveRecord model over a module with the same name

I have the following standard Rails ActiveRecord Foo defined:
# app/models/foo.rb
class Foo < ApplicationRecord
end
And I'm trying to call Foo.find(..) from within a hierarchy that contains a module also named Foo..
# lib/commands/bar.rb
module Commands
module Bar
module Create
class Command
def initialize(params)
...
Foo.find(params[:foo_id]
...
end
end
end
end
end
# lib/commands/foo.rb
module Commands
module Foo
module Create
class Command
...
end
end
end
end
Ruby/Rails is finding Commands::Foo instead of my Foo Model and throwing undefined method 'find' for Commands::Foo:Module.. how can I point at the correct ActiveModel implementation?
The obvious answer is to rename Commands::Foo.. to Commands::Foos.. but I'm curious to know if there's another way :o)
If you want to avoid the clash then you should rename the modules. The existing structure is unwieldy and will present similar problems to all future maintainers.
The best solution that I find in your code is to ensure you call the appropriate module and method via its full path:
2.3.3 :007 > ::Commands::Foo::Create::Command.new
"Commands::Foo::Command reached"
=> #<Commands::Foo::Create::Command:0x007ffa1b05e2f0>
2.3.3 :008 > ::Commands::Bar::Create::Command.new
"Commands::Bar::Command reached"
=> #<Commands::Bar::Create::Command:0x007ffa1b04f110>
You shouldn't try to override or modify internal Rails calls, because then you've modified the framework to fit code, which leads to unpredictable side effects.
You can try to call::Foo in Commands::Foo, it should go with your Foo model

Calling a module's methods on a Gem's object?

I've generated an object via a ruby gem (Koala) and I've written a few modules with helper methods. What do I need to do in order to be able to use the methods within the modules on the object?
If I, model_object = Model.new, model_object will have access to all the instance variables but object does not (see below).
Ruby 2.1, Rails 4.1
config/application.rb - Autoloading modules in folder
config.autoload_paths << Rails.root.join('lib/module_folder')
Model
class Model < ActiveRecord::Base
include Module
include Module::Module2
include Module::Module3
def self.create_account(token)
object = Module.module_class_method(token) #this works and generates the Koala object
ERROR: object.module2_instance_method # Error: NoMethodError Exception: undefined method
end
end
Module
module Module
extend ActiveSupport::Concern
end
Module2
module Module
module Module2
def module2_instance_method
end
end
end
SOLVED MYSELF
- the issue was the include statements being within the class, if I moved them outside it worked.
I believe if you include your modules somewhere under the app/ directory - they will be included automatically. Otherwise, you actually have to require them in your rails code explicitly with a require statement
Without seeing the actual code, I think the problem with Module2 in your code snippet is the self. method.
Because you are calling module2_instance_method on an instance of your object, the method in the module cannot have the self. because that designates a class method and, as such, would have to be called as Module::Module2.module2_instance_but_not_really_because_I_am_a_class_method.
I believe if you change def self.module2_instance_method ... end to def module2_instance_method ... end, you should no longer receive the NoMethodError exception.
Apologies if I've misread or misunderstand the OP.
Moved the include statements from inside to above the class declaration and all methods began to work. My assumption is that when they are within the statement they are only available to objects of that class.

undefined method in Module

I have a /lib/custom
inside I have custom.rb and custom_page.rb
custom.rb
require 'custom_page.rb'
module Custom
def self.name(params)
# logic
end
end
I've added in application.rb config.autoload_paths += %W(#{config.root}/lib)
I can't seem to call in my controllers to Custom.name(params)
NoMethodError: undefined method `name' for Custom:Module
I've tried with define the method as def Custom.name, using class << self and method_function :name yet nothing helps..
Am I missing something?
It's because of the Rails naming convention. In your rails console, try
irb(main):001:0> Custom::Custom
LoadError: Expected lib/custom/custom.rb to define Custom::Custom
Rais expects you do define module Custom::Custom (not module Custom) in lib/custom/custom.rb.
Rails sees a folder lib/custom and created an empty module Custom (doesn't respond to name method) based on convention, if you want to define module Custom, you have to write a file lib/custom.rb
The convention is
lib/custom.rb #define module Custom
lib/custom/deeper.rb #define module Custom::Deeper
lib/empty_folder/ # rails provides you an empty module EmptyFolder
BTW you don't have to require 'custom_page' in your custom.rb, if Rails sees CustomPage in your code, it will try to load the class definition file based on naming convention, given that your custom_page.rb file path follows the convention.
You can also use ActiveSupport::Concern to extend both class and instance methods. In the module just to this:
module Custom
extend ActiveSupport::Concern
included do
# everything but the class methods go here
end
module ClassMethods
# define class methods here
def name(params)
#logic
end
end
end

Create bare rails controller class

I'm trying to create clean controller based on ActionController::Base. That's what I try:
class MetalController
ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
include left
end
end
From Rails doc:
Shortcut helper that returns all the modules included in
ActionController::Base except the ones passed as arguments:
This gives better control over what you want to exclude and makes it
easier to create a bare controller class, instead of listing the modules
required manually.
My another controller inherits from MetalController :
class API::BaseController < MetalController
#.... my awesome api code
end
So this not work then i launch rails server:
block in <module:AssetPaths>': undefined methodconfig_accessor' for
MetalController:Class (NoMethodError)
Rails 4.1.0, Ruby 2.1.0
Update:
If i include ActiveSupport::Configurable
throws the errors:
_implied_layout_name': undefined local variable or method
controller_path' for MetalController:Class (NameError)
You need to inherit from ActionController::Metal:
class MetalController < ActionController::Metal
ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
include left
end
end

Auto-create the containing module of a class

In Rails you can create a model under app/foo/bar.rb, with bar.rb containing:
class Foo::Bar
def some_method
puts "I work fine"
end
end
If you try to do this in a pure ruby app you'd get a NameError: uninitialized constant Foo unless you've already initialized a module Foo.
What is Rails doing that allows it to create classes without first initializing their containing module? Is it possible to import this behavior through something like activesupport, or are we left to implement on our own?
Rails modifies the Class class to include a const_missing method which gets called when an undefined class is used. It then loads things to try and load the requested class.
The implementation of this in ActiveSupport is in lib/active_support/dependencies.rb.
actually model class created is extend to < ActiveRecord::Base

Resources