Ruby/Rails organizing and loading constants for an entire module

Viewed 112

This question may just be for Ruby, but it was from working on a Rails that spurred me asking this question.

Suppose I am creating a new module so that I can better organize correlated/coupled code. Let's call this module amazing_feature and all of it's classes/submodules are located in the app/services directory. So according to code loading principles, the entire module should be in the app/services/amazing_feature directory in order to be loaded properly.

Let's say that I have two classes for this module:

# app/services/amazing_feature/thing_one.rb
module AmazingFeature
  class ThingOne
    ...
  end
end

# app/services/amazing_feature/thing_two.rb
module AmazingFeature
  module ThingTwo
    ...
  end
end

There are some constants that I would like to be available for all of the classes/submodules within module AmazingFeature, as well as being available from the AmazingModule namespace for any external code (eg, other controllers and models, in the Rails point of view). For example, if I want to define MY_CONSTANT = 1, then it would be accessible as just MY_CONSTANT within the module and as AmazingFeature::MY_CONSTANT from outside the module.

So the question is, how can I actually accomplish this in Ruby or Rails? There are thoughts that I've had, approaches that think may work, or approaches that I have seen elsewhere, such as other SOF posts:

  • Make a file directly in app/services for the module that associate the constants directly to the module. I don't prefer this approach because it feels weird putting a file coupled to the module outside of its subdirectory.
# app/services/amazing_feature.rb
module AmazingFeature
  MY_CONSTANT = 1
end
  • Load the constants globally as a Rails initializer (ie, in config/initializers). I also have the same dislike for this approach as above.
  • Create a Constants module in the subdirectory so that the constants are colocated with all other code for the module. I just don't know how to properly associate these constants to the parent module, so there is a missing piece in this code example.
# app/services/amazing_feature/constants.rb
module AmazingFeature
  module Constants
    MY_CONSTANT = 1
  end
end

# Now what??? :(
  • Some other approach? I'm at a loss here.

Thank you.

2 Answers

You can do whatever you want of course, but take some inspiration from popular gems:

It is normal to rely on Ruby autoloaders to map the names of your constants (AmazingFeature) to file names that contain those constants. So AmazingFeature could map to load/path/amazing_feature.rb and AmazingFeature::Greatness could map to load/path/amazing_feature/greatness.rb.

I'd advise doing this and pretty soon it won't feel weird :)

I think this is a good idea:

# app/services/amazing_feature/constants.rb
module AmazingFeature
  module Constants
    MY_CONSTANT = 1
  end
  include AmazingFeature::Constants
end

module AmazingModule
 include AmazingFeature::Constants
end

# Then
AmazingFeature::MY_CONSTANT # => 1
AmazingModule::MY_CONSTANT # => 1
Related