Skip to content

Commit

Permalink
Improve custom configuration
Browse files Browse the repository at this point in the history
1. Hashes can be assigned
2. We don't need a special level anymore

The method chain only works in the top level.

If users need a second level they need to assign a OrderedOptions to the
key:

    config.resque.server = ActiveSupport::OrderedOptions.new
    config.resque.server.url = "http://localhost"
    config.resque.server.port = 3000

[Rafael Mendonça França + Carlos Antonio da Silva]
  • Loading branch information
Rafael Mendonça França authored and rafaelfranca committed Aug 19, 2014
1 parent 0cb3bdb commit de48913
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 46 deletions.
18 changes: 10 additions & 8 deletions guides/source/4_2_release_notes.md
Expand Up @@ -75,22 +75,24 @@ Please refer to the [Changelog][railties] for detailed changes.
* Introduced an `after_bundle` callback for use in Rails templates. * Introduced an `after_bundle` callback for use in Rails templates.
([Pull Request](https://github.com/rails/rails/pull/16359)) ([Pull Request](https://github.com/rails/rails/pull/16359))


* Introduced the `x` namespace for defining custom configuration options: * Custom configuration options can be chained:


```ruby ```ruby
# config/environments/production.rb # config/environments/production.rb
config.x.payment_processing.schedule = :daily config.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3 config.payment_processing.retries = 3
config.x.super_debugger = true config.resque = { timeout: 60, inline_jobs: :always }
config.super_debugger = true
``` ```


These options are then available through the configuration object: These options are then available through the configuration object:


```ruby ```ruby
Rails.configuration.x.payment_processing.schedule # => :daily Rails.configuration.payment_processing.schedule # => :daily
Rails.configuration.x.payment_processing.retries # => 3 Rails.configuration.payment_processing.retries # => 3
Rails.configuration.x.super_debugger # => true Rails.configuration.resque.timeout # => 60
Rails.configuration.x.super_debugger.not_set # => nil Rails.configuration.resque.inline_jobs # => :always
Rails.configuration.super_debugger # => true
``` ```


([Commit](https://github.com/rails/rails/commit/611849772dd66c2e4d005dcfe153f7ce79a8a7db)) ([Commit](https://github.com/rails/rails/commit/611849772dd66c2e4d005dcfe153f7ce79a8a7db))
Expand Down
16 changes: 9 additions & 7 deletions guides/source/configuring.md
Expand Up @@ -1006,16 +1006,18 @@ Custom configuration
You can configure your own code through the Rails configuration object with custom configuration. It works like this: You can configure your own code through the Rails configuration object with custom configuration. It works like this:


```ruby ```ruby
config.x.payment_processing.schedule = :daily config.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3 config.payment_processing.retries = 3
config.x.super_debugger = true config.resque = { timeout: 60, inline_jobs: :always }
config.super_debugger = true
``` ```


These configuration points are then available through the configuration object: These configuration points are then available through the configuration object:


```ruby ```ruby
Rails.configuration.x.payment_processing.schedule # => :daily Rails.configuration.payment_processing.schedule # => :daily
Rails.configuration.x.payment_processing.retries # => 3 Rails.configuration.payment_processing.retries # => 3
Rails.configuration.x.super_debugger # => true Rails.configuration.resque.timeout # => 60
Rails.configuration.x.super_debugger.not_set # => nil Rails.configuration.resque.inline_jobs # => :always
Rails.configuration.super_debugger # => true
``` ```
16 changes: 9 additions & 7 deletions railties/CHANGELOG.md
Expand Up @@ -13,16 +13,18 @@
configure your own code through the Rails configuration object with custom configuration: configure your own code through the Rails configuration object with custom configuration:


# config/environments/production.rb # config/environments/production.rb
config.x.payment_processing.schedule = :daily config.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3 config.payment_processing.retries = 3
config.x.super_debugger = true config.resque = { timeout: 60, inline_jobs: :always }
config.super_debugger = true


These configuration points are then available through the configuration object: These configuration points are then available through the configuration object:


Rails.configuration.x.payment_processing.schedule # => :daily Rails.configuration.payment_processing.schedule # => :daily
Rails.configuration.x.payment_processing.retries # => 3 Rails.configuration.payment_processing.retries # => 3
Rails.configuration.x.super_debugger # => true Rails.configuration.resque.timeout # => 60
Rails.configuration.x.super_debugger.not_set # => nil Rails.configuration.resque.inline_jobs # => :always
Rails.configuration.super_debugger # => true


*DHH* *DHH*


Expand Down
14 changes: 1 addition & 13 deletions railties/lib/rails/application/configuration.rb
Expand Up @@ -13,7 +13,7 @@ class Configuration < ::Rails::Engine::Configuration
:railties_order, :relative_url_root, :secret_key_base, :secret_token, :railties_order, :relative_url_root, :secret_key_base, :secret_token,
:serve_static_assets, :ssl_options, :static_cache_control, :session_options, :serve_static_assets, :ssl_options, :static_cache_control, :session_options,
:time_zone, :reload_classes_only_on_change, :time_zone, :reload_classes_only_on_change,
:beginning_of_week, :filter_redirect, :x :beginning_of_week, :filter_redirect


attr_writer :log_level attr_writer :log_level
attr_reader :encoding attr_reader :encoding
Expand Down Expand Up @@ -48,7 +48,6 @@ def initialize(*)
@eager_load = nil @eager_load = nil
@secret_token = nil @secret_token = nil
@secret_key_base = nil @secret_key_base = nil
@x = Custom.new


@assets = ActiveSupport::OrderedOptions.new @assets = ActiveSupport::OrderedOptions.new
@assets.enabled = true @assets.enabled = true
Expand Down Expand Up @@ -155,17 +154,6 @@ def session_store(*args)
def annotations def annotations
SourceAnnotationExtractor::Annotation SourceAnnotationExtractor::Annotation
end end

private
class Custom
def initialize
@configurations = Hash.new
end

def method_missing(method, *args)
@configurations[method] ||= ActiveSupport::OrderedOptions.new
end
end
end end
end end
end end
42 changes: 40 additions & 2 deletions railties/lib/rails/railtie/configuration.rb
Expand Up @@ -88,11 +88,49 @@ def respond_to?(name, include_private = false)


def method_missing(name, *args, &blk) def method_missing(name, *args, &blk)
if name.to_s =~ /=$/ if name.to_s =~ /=$/
@@options[$`.to_sym] = args.first key = $`.to_sym
value = args.first

if value.is_a?(Hash)
@@options[key] = ChainedConfigurationOptions.new value
else
@@options[key] = value
end
elsif @@options.key?(name) elsif @@options.key?(name)
@@options[name] @@options[name]
else else
super @@options[name] = ActiveSupport::OrderedOptions.new
end
end

class ChainedConfigurationOptions < ActiveSupport::OrderedOptions # :nodoc:
def initialize(value = nil)
if value.is_a?(Hash)
value.each_pair { |k, v| set_value k, v }
else
super
end
end

def method_missing(meth, *args)
if meth =~ /=$/
key = $`.to_sym
value = args.first

set_value key, value
else
self.fetch(meth) { super }
end
end

private

def set_value(key, value)
if value.is_a?(Hash)
value = self.class.new(value)
end

self[key] = value
end end
end end
end end
Expand Down
6 changes: 4 additions & 2 deletions railties/test/application/configuration/base_test.rb
Expand Up @@ -5,6 +5,8 @@
module ApplicationTests module ApplicationTests
module ConfigurationTests module ConfigurationTests
class BaseTest < ActiveSupport::TestCase class BaseTest < ActiveSupport::TestCase
include ActiveSupport::Testing::Isolation

def setup def setup
build_app build_app
boot_rails boot_rails
Expand All @@ -30,8 +32,8 @@ def app
end end


def require_environment def require_environment
require "#{app_path}/config/environment" require "#{app_path}/config/environment"
end end
end end
end end
end end
81 changes: 75 additions & 6 deletions railties/test/application/configuration/custom_test.rb
@@ -1,15 +1,84 @@
require 'application/configuration/base_test' require 'application/configuration/base_test'


class ApplicationTests::ConfigurationTests::CustomTest < ApplicationTests::ConfigurationTests::BaseTest class ApplicationTests::ConfigurationTests::CustomTest < ApplicationTests::ConfigurationTests::BaseTest
test 'access custom configuration point' do test 'configuration top level can be chained' do
add_to_config <<-RUBY add_to_config <<-RUBY
config.x.resque.inline_jobs = :always config.resque.inline_jobs = :always
config.x.resque.timeout = 60 config.resque.timeout = 60
RUBY RUBY
require_environment require_environment


assert_equal :always, Rails.configuration.x.resque.inline_jobs assert_equal :always, Rails.configuration.resque.inline_jobs
assert_equal 60, Rails.configuration.x.resque.timeout assert_equal 60, Rails.configuration.resque.timeout
assert_nil Rails.configuration.x.resque.nothing assert_nil Rails.configuration.resque.nothing
end

test 'configuration top level accept normal values' do
add_to_config <<-RUBY
config.timeout = 60
config.something_nil = nil
config.something_false = false
config.something_true = true
RUBY
require_environment

assert_equal 60, Rails.configuration.timeout
assert_equal nil, Rails.configuration.something_nil
assert_equal false, Rails.configuration.something_false
assert_equal true, Rails.configuration.something_true
end

test 'configuration top level builds options from hashes' do
add_to_config <<-RUBY
config.resque = { timeout: 60, inline_jobs: :always }
RUBY
require_environment

assert_equal :always, Rails.configuration.resque.inline_jobs
assert_equal 60, Rails.configuration.resque.timeout
assert_nil Rails.configuration.resque.nothing
end

test 'configuration top level builds options from hashes with string keys' do
add_to_config <<-RUBY
config.resque = { 'timeout' => 60, 'inline_jobs' => :always }
RUBY
require_environment

assert_equal :always, Rails.configuration.resque.inline_jobs
assert_equal 60, Rails.configuration.resque.timeout
assert_nil Rails.configuration.resque.nothing
end

test 'configuration top level builds nested options from hashes with symbol keys' do
add_to_config <<-RUBY
config.resque = { timeout: 60, inline_jobs: :always, url: { host: 'localhost', port: 8080 } }
config.resque.url.protocol = 'https'
config.resque.queues = { production: ['low_priority'] }
RUBY
require_environment

assert_equal(:always, Rails.configuration.resque.inline_jobs)
assert_equal(60, Rails.configuration.resque.timeout)
assert_equal({ host: 'localhost', port: 8080, protocol: 'https' }, Rails.configuration.resque.url)
assert_equal('localhost', Rails.configuration.resque.url.host)
assert_equal(8080, Rails.configuration.resque.url.port)
assert_equal('https', Rails.configuration.resque.url.protocol)
assert_equal(['low_priority'], Rails.configuration.resque.queues.production)
assert_nil(Rails.configuration.resque.nothing)
end

test 'configuration top level builds nested options from hashes with string keys' do
add_to_config <<-RUBY
config.resque = { 'timeout' => 60, 'inline_jobs' => :always, 'url' => { 'host' => 'localhost', 'port' => 8080 } }
RUBY
require_environment

assert_equal(:always, Rails.configuration.resque.inline_jobs)
assert_equal(60, Rails.configuration.resque.timeout)
assert_equal({ host: 'localhost', port: 8080 }, Rails.configuration.resque.url)
assert_equal('localhost', Rails.configuration.resque.url.host)
assert_equal(8080, Rails.configuration.resque.url.port)
assert_nil(Rails.configuration.resque.nothing)
end end
end end
2 changes: 1 addition & 1 deletion railties/test/railties/engine_test.rb
Expand Up @@ -840,7 +840,7 @@ def index


Rails.application.load_seed Rails.application.load_seed
assert Rails.application.config.app_seeds_loaded assert Rails.application.config.app_seeds_loaded
assert_raise(NoMethodError) { Bukkits::Engine.config.bukkits_seeds_loaded } assert_empty Bukkits::Engine.config.bukkits_seeds_loaded


Bukkits::Engine.load_seed Bukkits::Engine.load_seed
assert Bukkits::Engine.config.bukkits_seeds_loaded assert Bukkits::Engine.config.bukkits_seeds_loaded
Expand Down

0 comments on commit de48913

Please sign in to comment.