Extending ActiveSupport::Concern - ruby-on-rails

I'm trying to extend an existing Concern in a different Rails project.
This module exists in a gem I'm requiring:
module Foo
extend ActiveSupport::Concern
included do
#some stuff
end
def method_a
end
end
And then in my project:
module Foo
extend ActiveSupport::Concern
included do
#some other stuff
end
def method_b
end
end
Result is, objects including Foo only have method_b, and only run #some other stuff on inclusion. Is there any way for all code under included to run, and all methods to be added?
EDIT: Both the gem and the project are mine, and I'm not dead set on using ActiveSupport::Concern if there's a more fitting solution.

You shouldn't override or extend directly the concern. With a simple module it would be maybe useful, but concerns are set up to be explicitly extended:
module MyFoo
extend ActiveSupport::Concern
extend Foo
included do
#some other stuff
end
def method_b
end
end

Related

How to override class_method in rails model concern

How does one override a class method defined in a model concern?
This is a bit tricky since you’re not really overriding a class method right? Because it’s using the concern api of definining class methods in the class_methods block.
so say I have a concern that looks like this:
module MyConcern
extend ActiveSupport::Concern
class_methods do
def do_something
#some code
end
end
end
In model.. how would I override that method so that I could call it like we do with super when using inheritance? So in my model I’d like to go:
def self.do_something
#call module do_something
end
?
If you've included MyConcern in the model that defines self.do_something, you should just be able to use super:
module MyConcern
extend ActiveSupport::Concern
class_methods do
def do_something
puts "I'm do_something in the concern"
end
end
end
class UsesMyConcern < ActiveRecord::Base
include MyConcern
def self.do_something
super
end
end
UsesMyConcern.do_something
# => "I'm do_something in the concern"
If you haven't or don't want to include MyConcern in the model and you want to invoke do_something on the module without creating any intermediary objects, you can change your model to:
class UsesMyConcern < ActiveRecord::Base
def self.do_something
MyConcern::ClassMethods.instance_method(:do_something).bind(self).call
end
end
UsesMyConcern.do_something
# => "I'm do_something in the concern"
ActiveSupport::Concern.class_methods defines a ClassMethods module in the concern if there isn't one already, and that's where we can find the do_something method.
Why not simply call the module's method: MyConcern.do_something?
I'm not sure if there's an easy of doing super for modules (though I can see why that may be useful).
The next best solution could be doing something like calling #included_modules and manually iterating with #responds_to?:
def self.do_something
self.super_module(__method__)
end
def self.super_module(method)
self.included_modules.find { |m| m.responds_to? method }.public_send(method)
end
The old way using alias_method_chain: https://ernie.io/2011/02/03/when-to-use-alias_method_chain/
The new way (requires > ruby 2.0.0) you really should use this, as there will be a DEPRECATION WARNING when using it in rails 5.0:
http://paweljaniak.co.za/2014/09/30/understanding-ruby-module-prepend-and-include/

How to scope a method call within a module

I want to define a method encapsulated module.
It has the same name as a method in a different module.
When calling the one I want it calls the other one that is mixed in first.
Code :
class User
include ModuleA
include ModuleB
end
module ModuleA
extend ActiveSupport::Concern
included do
def hi
end
end
end
module ModuleB
extend ActiveSupport::Concern
def hi(param)
end
def say_hi
hi(param)
end
end
Errors with
ArgumentError: wrong number of arguments (1 for 0)
# ./app/models/concerns/modulea.rb:16:in `hi'
Rails 4
The correct answer would be to remove included in both the Modules.
Instance methods will happily be included without the included block. as suggested by #Sergio Tulentsev
class User
include ModuleA
include ModuleB
end
module ModuleA
extend ActiveSupport::Concern
def hi
end
end
module ModuleB
extend ActiveSupport::Concern
def hi(param)
end
def say_hi
hi(param)
end
end

How to define custom class and use it in helper

I want to define a class and let many helpers use.
I can include MvaasPortal moude in fine,
Then I can new the object , but can not use any methods of the object,
It's so strange.
If I can not use the methods in the object, why I can new the object.
Ruby is so strange.
#portal = Portal.new
There is no methods in #portal object
mvaas_portal.rb
module MvaasPortal
module InstanceMethods
class Portal
def initialize(server_url)
~~~~
end
def query_server(body_to_send={},session_id=nil)
~~~
end
end
end
def self.included(receiver)
receiver.send :include, InstanceMethods
end
end
If you're using rails, you can use ActiveSupport::Concern : http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
If don't, take a look at the first example on the link.
Moreover, your namespace is a little bit weird and misses some context. Here is an example with a dummy method :
require 'active_support/concern'
module MvaasPortal
include ActiveSupport::Concern
def an_instance_method
puts "Here!"
end
end
class Portal
include MvaasPortal
end
Portal.new.an_instance_method
=> "Here!"

Why has the InstanceMethods module been deprecated?

I love ActiveSupport::Concern.
It makes it easy to add functionality to your classes, with a nice syntax.
Anyways, in Rails 3.2, the InstanceMethods module has been deprecated. If I understood correctly, we should just define our methods in the included block (actually it's just in the body of the module):
# edit: don't do this! The method definition should just be in the body of the module
included do
def my_method; end
end
I was just wondering if anyone knows why they decided to do that?
Let's look at the example you linked first.
module TagLib
extend ActiveSupport::Concern
module ClassMethods
def find_by_tags()
# ...
end
end
module InstanceMethods
def tags()
# ...
end
end
end
When you include TagLib into your class AS Concern automatically extends the class with ClassMethods module and includes InstanceMethods module.
class Foo
include TagLib
# is roughly the same as
include TagLib::InstanceMethods
extend TagLib::ClassMethods
end
But as you may noticed we are already including TagLib module itself so the methods defined within it are already available as instance methods on the class. Why would you want to have a separate InstanceMethods module then?
module TagLib
extend ActiveSupport::Concern
module ClassMethods
def find_by_tags()
# ...
end
end
def tags()
# ...
end
end
class Foo
include TagLib
# does only `extend TagLib::ClassMethods` for you
end

Rails 3- Plugin returns 'undefined local variable'

I have a custom plugin (I didn't write it) that is not working on rails 3, however it did work with rails 2. It is for a custom authentication scheme, here is what the main module looks like:
#lib/auth.rb
module ActionController
module Verification
module ClassMethods
def verify_identity(options = {})
class_eval(%(before_filter :validate_identity, :only => options[:only], :except => options[:except]))
end
end
end
class Base
#some configuration variables in here
def validate_identity
#does stuff to validate the identity
end
end
end
#init.rb
require 'auth'
require 'auth_helper'
ActionView::Base.send(:include, AuthHelper)
AuthHelper contains a simple helper method for authenticating, base on a group membership.
When I include 'verify_identity' on an actioncontroller:
class TestController < ApplicationController
verify_identity
....
end
I get a routing error: undefined local variable or method `verify_identity' for TestController:Class. Any ideas how I can fix this? Thanks!
It worked in 2.3 because there was an ActionController::Verification module back there. It's not working in 3.0 because this module doesn't exist. Rather than relying on Rails to have a module that you can hook into, define your own like this:
require 'active_support/concern'
module Your
module Mod
extend ActiveSupport::Concern
module ClassMethods
def verify_identity(options = {})
# code goes here
end
end
end
end
and use:
ActionController::Base.send(:include, Your::Mod)
To make its functions available. ActiveSupport::Concern supports you having a ClassMethods and InstanceMethods module inside your module and it takes care of loading the methods in these modules into the correct areas of whatever the module is included into.

Resources