Home > front end >  Ruby gem for Rails app: how to get `Rails.env` method without requiring Rails?
Ruby gem for Rails app: how to get `Rails.env` method without requiring Rails?

Time:07-22

I have RoR experience, but I'm working on my first gem.

The gem is specifically for use in Rails apps and I want to rely on Rails.env in several cases.

I know requiring Rails in the .gemspec is a bad idea (at least bad practice) because Rails is big and comes with lots of its own dependencies.

But Rails.env isn't exactly an extension I can just pull in.

Rails.env functionality comes from railties which itself relies on active_support, action_dispatch and a bunch of other things:

require "rails/ruby_version_check"

require "pathname"

require "active_support"
require "active_support/core_ext/kernel/reporting"
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/array/extract_options"
require "active_support/core_ext/object/blank"

require "rails/application"
require "rails/version"

require "active_support/railtie"
require "action_dispatch/railtie"

module Rails
  extend ActiveSupport::Autoload
  extend ActiveSupport::Benchmarkable

  autoload :Info
  autoload :InfoController
  autoload :MailersController
  autoload :WelcomeController

  class << self
    ...

    # Returns the current Rails environment.
    #
    #   Rails.env # => "development"
    #   Rails.env.development? # => true
    #   Rails.env.production? # => false
    def env
      @_env ||= ActiveSupport::EnvironmentInquirer.new(ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence || "development")
    end

ActiveSupport::EnvironmentInquirer just gives me the ability to do Rails.env.production? which I don't really care about.

I could also just mimic this behavior by checking for ENV["RAILS_ENV"] and ENV["RACK_ENV"] but if Rails.env is changed, that doesn't change the ENV variables:

3.0.2 :001 > Rails.env
 => "development" 
3.0.2 :005 > ENV["RAILS_ENV"]
 => "development" 
3.0.2 :006 > ENV["RACK_ENV"]
 => "development" 
3.0.2 :007 > Rails.env = 'test'
 => "test" 
3.0.2 :008 > Rails.env
 => "test" 
3.0.2 :009 > ENV["RAILS_ENV"]
 => "development" 
3.0.2 :010 > ENV["RACK_ENV"]
 => "development" 

Or I could just instantiate the class as a PORO, but this also seems like bad practice:

module Rails
  def self.env
    @_env ||=
      ENV['RAILS_ENV'] ||
      ENV['RACK_ENV'] ||
      'development'
  end
end

Right now I'm just rescuing when Rails throws a name error:

@environment =
  begin
    Rails.env
  rescue NameError
    'development'
  end

Is there a standard way to accomplish this or is my rescue the best way to proceed?

CodePudding user response:

You could use defined? to check whether a top-level constant Rails is defined:

def rails_env
  ::Rails.env if defined?(::Rails)
end

if you want to be extra safe:

def rails_env
  ::Rails.env if defined?(::Rails) && ::Rails.respond_to?(:env)
end

To enforce a plain string: (instead of a ActiveSupport::EnvironmentInquirer instance)

def rails_env
  ::Rails.env.to_s if defined?(::Rails) && ::Rails.respond_to?(:env)
end

With the above you could write:

@environment = rails_env || 'development'
  • Related