Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Including a Concern with a constant within a Concern

I have concern in which I store constants:

module Group::Constants   extend ActiveSupport::Concern    MEMBERSHIP_STATUSES = %w(accepted invited requested     rejected_by_group rejected_group) end 

And another concern that I wish to use these constants:

module User::Groupable   extend ActiveSupport::Concern   include Group::Constants    MEMBERSHIP_STATUSES.each do |status_name|     define_method "#{status_name}_groups" do       groups.where(:user_memberships => {:status => status_name})     end   end end 

Unfortunately, this results in a routing error:

uninitialized constant User::Groupable::MEMBERSHIP_STATUSES 

It looks like the first concern isn't loading properly in the second concern. If that's the case, what can I do about it?

like image 625
nullnullnull Avatar asked Apr 11 '13 15:04

nullnullnull


People also ask

What are concerns Rails?

A Rails Concern is a module that extends the ActiveSupport::Concern module. Concerns allow us to include modules with methods (both instance and class) and constants into a class so that the including class can use them. A concern provides two blocks: included.

What is the reason for using concern in Rails?

According to the Rails docs, we want to encapsulate the code in a different module to reuse that code in different places when that same functionality is needed. We use concerns when we feel that one file is overwhelmed with the functionality, so we extract some part of the code and save it into another file or module.

What does ActiveSupport :: concern do?

Using extend ActiveSupport::Concern tells Rails that we are creating a concern. The code within the included block will be executed wherever the module is included. This is best for including third party functionality. In this case, we will get an error if the before_action is written outside of the included block.

What are Controller concerns in Rails?

If you setup a Rails 4 app, you'll notice the app/models/concerns and app/controllers/concerns directories. Concerns are modules that can be mixed into your models and controllers to share code between them. Some developers falsely classify mixins as composition when they are actually a form of inheritance.


2 Answers

It appears this behavior is by design, as explained nicely over here.

What you'll need to do in this case is not have Group::Constants extend from ActiveSupport::Concern since that will block its implementation from being shared with other ActiveSupport::Concern extending modules (although it will be ultimately shared in a class that includes the second module):

module A   TEST_A = 'foo' end  module B   extend ActiveSupport::Concern   TEST_B = 'bar' end  module C   extend ActiveSupport::Concern   include A   include B end  C::TEST_A  => 'foo' C::TEST_B  => uninitialized constant C::TEST_B  class D   include C end  D::TEST_A  => 'foo' D::TEST_B  => 'bar' 

In short, you'll need to make Group::Constants a standard module and then all will be well.

like image 112
PinnyM Avatar answered Sep 19 '22 21:09

PinnyM


If you want to keep everything in one file, and if you can stomach a bit of boilerplate, you could break up your module into a "concern" bit and a "non-concern" bit:

module A   FOO = [22]    def self.included base     base.include Concern   end    module Concern     extend ActiveSupport::Concern      class_methods do       def foo_from_the_perspective_of_a_class_method_in_A         {lexical: FOO, instance: self::FOO}       end     end   end end  module B   extend ActiveSupport::Concern    include A    FOO += [33]    def foo_from_the_perspective_of_an_instance_method_in_B     FOO   end end  class C   include B end   C.foo_from_the_perspective_of_a_class_method_in_A => {:lexical=>[22], :instance=>[22, 33]} C.new.foo_from_the_perspective_of_an_instance_method_in_B => [22, 33] C::FOO => [22, 33] 
like image 37
TRL Avatar answered Sep 16 '22 21:09

TRL