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?
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.
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.
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.
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.
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.
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With