diff options
19 files changed, 108 insertions, 281 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index bdb14b69e5..050ec5e649 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,11 @@ +* Allow `config.action_dispatch.trusted_proxies` to accept an IPAddr object. + + Example: + # config/environments/production.rb + config.action_dispatch.trusted_proxies = IPAddr.new('4.8.15.0/16') + + *Sam Aarons* + * Avoid duplicating routes for HEAD requests. Instead of duplicating the routes, we will first match the HEAD request to diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 6a79b4e859..b022fea001 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -1,3 +1,5 @@ +require 'ipaddr' + module ActionDispatch # This middleware calculates the IP address of the remote client that is # making the request. It does this by checking various headers that could @@ -28,14 +30,14 @@ module ActionDispatch # guaranteed by the IP specification to be private addresses. Those will # not be the ultimate client IP in production, and so are discarded. See # http://en.wikipedia.org/wiki/Private_network for details. - TRUSTED_PROXIES = %r{ - ^127\.0\.0\.1$ | # localhost IPv4 - ^::1$ | # localhost IPv6 - ^[fF][cCdD] | # private IPv6 range fc00::/7 - ^10\. | # private IPv4 range 10.x.x.x - ^172\.(1[6-9]|2[0-9]|3[0-1])\.| # private IPv4 range 172.16.0.0 .. 172.31.255.255 - ^192\.168\. # private IPv4 range 192.168.x.x - }x + TRUSTED_PROXIES = [ + "127.0.0.1", # localhost IPv4 + "::1", # localhost IPv6 + "fc00::/7", # private IPv6 range fc00::/7 + "10.0.0.0/8", # private IPv4 range 10.x.x.x + "172.16.0.0/12", # private IPv4 range 172.16.0.0 .. 172.31.255.255 + "192.168.0.0/16", # private IPv4 range 192.168.x.x + ].map { |proxy| IPAddr.new(proxy) } attr_reader :check_ip, :proxies @@ -47,24 +49,24 @@ module ActionDispatch # clients (like WAP devices), or behind proxies that set headers in an # incorrect or confusing way (like AWS ELB). # - # The +custom_proxies+ argument can take a regex, which will be used - # instead of +TRUSTED_PROXIES+, or a string, which will be used in addition - # to +TRUSTED_PROXIES+. Any proxy setup will put the value you want in the - # middle (or at the beginning) of the X-Forwarded-For list, with your proxy - # servers after it. If your proxies aren't removed, pass them in via the - # +custom_proxies+ parameter. That way, the middleware will ignore those - # IP addresses, and return the one that you want. + # The +custom_proxies+ argument can take an Array of string, IPAddr, or + # Regexp objects which will be used instead of +TRUSTED_PROXIES+. If a + # single string, IPAddr, or Regexp object is provided, it will be used in + # addition to +TRUSTED_PROXIES+. Any proxy setup will put the value you + # want in the middle (or at the beginning) of the X-Forwarded-For list, + # with your proxy servers after it. If your proxies aren't removed, pass + # them in via the +custom_proxies+ parameter. That way, the middleware will + # ignore those IP addresses, and return the one that you want. def initialize(app, check_ip_spoofing = true, custom_proxies = nil) @app = app @check_ip = check_ip_spoofing - @proxies = case custom_proxies - when Regexp - custom_proxies - when nil - TRUSTED_PROXIES - else - Regexp.union(TRUSTED_PROXIES, custom_proxies) - end + @proxies = if custom_proxies.blank? + TRUSTED_PROXIES + elsif custom_proxies.respond_to?(:any?) + custom_proxies + else + Array(custom_proxies) + TRUSTED_PROXIES + end end # Since the IP address may not be needed, we store the object here @@ -80,32 +82,6 @@ module ActionDispatch # into an actual IP address. If the ActionDispatch::Request#remote_ip method # is called, this class will calculate the value and then memoize it. class GetIp - - # This constant contains a regular expression that validates every known - # form of IP v4 and v6 address, with or without abbreviations, adapted - # from {this gist}[https://gist.github.com/gazay/1289635]. - VALID_IP = %r{ - (^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4 - (^( - (([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated - (([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end - (([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6 - (([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with - (([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon - (([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle - (([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - (([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - (([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - (([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - (([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - (([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - (::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4 - ([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4 - (::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the beginning - (([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending - )$) - }x - def initialize(env, middleware) @env = env @check_ip = middleware.check_ip @@ -173,12 +149,22 @@ module ActionDispatch def ips_from(header) # Split the comma-separated list into an array of strings ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : [] - # Only return IPs that are valid according to the regex - ips.select{ |ip| ip =~ VALID_IP } + ips.select do |ip| + begin + # Only return IPs that are valid according to the IPAddr#new method + range = IPAddr.new(ip).to_range + # we want to make sure nobody is sneaking a netmask in + range.begin == range.end + rescue ArgumentError, IPAddr::InvalidAddressError + nil + end + end end def filter_proxies(ips) - ips.reject { |ip| ip =~ @proxies } + ips.reject do |ip| + @proxies.any? { |proxy| proxy === ip } + end end end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index 552a902349..3fc2ab178c 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,9 +1,3 @@ -* Add I18n support for input/textarea placeholder text. - - Placeholder I18n follows the same convention as `label` I18n. - - *Alex Robbin* - * Fix that render layout: 'messages/layout' should also be added to the dependency tracker tree. *DHH* diff --git a/actionview/lib/action_view/helpers/tags/placeholderable.rb b/actionview/lib/action_view/helpers/tags/placeholderable.rb deleted file mode 100644 index 313aa725c9..0000000000 --- a/actionview/lib/action_view/helpers/tags/placeholderable.rb +++ /dev/null @@ -1,32 +0,0 @@ -module ActionView - module Helpers - module Tags # :nodoc: - module Placeholderable # :nodoc: - def initialize(*) - super - - if tag_value = @options[:placeholder] - object_name = @object_name.gsub(/\[(.*)_attributes\]\[\d+\]/, '.\1') - method_and_value = tag_value.is_a?(TrueClass) ? @method_name : "#{@method_name}.#{tag_value}" - - if object.respond_to?(:to_model) - key = object.class.model_name.i18n_key - i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] - end - - i18n_default ||= "" - placeholder = I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.placeholder").presence - - placeholder ||= if object && object.class.respond_to?(:human_attribute_name) - object.class.human_attribute_name(method_and_value) - end - - placeholder ||= @method_name.humanize - - @options[:placeholder] = placeholder - end - end - end - end - end -end diff --git a/actionview/lib/action_view/helpers/tags/text_area.rb b/actionview/lib/action_view/helpers/tags/text_area.rb index 69038c1498..9ee83ee7c2 100644 --- a/actionview/lib/action_view/helpers/tags/text_area.rb +++ b/actionview/lib/action_view/helpers/tags/text_area.rb @@ -1,11 +1,7 @@ -require 'action_view/helpers/tags/placeholderable' - module ActionView module Helpers module Tags # :nodoc: class TextArea < Base # :nodoc: - include Placeholderable - def render options = @options.stringify_keys add_default_name_and_id(options) diff --git a/actionview/lib/action_view/helpers/tags/text_field.rb b/actionview/lib/action_view/helpers/tags/text_field.rb index 5c576a20ca..e0b80d81c2 100644 --- a/actionview/lib/action_view/helpers/tags/text_field.rb +++ b/actionview/lib/action_view/helpers/tags/text_field.rb @@ -1,11 +1,7 @@ -require 'action_view/helpers/tags/placeholderable' - module ActionView module Helpers module Tags # :nodoc: class TextField < Base # :nodoc: - include Placeholderable - def render options = @options.stringify_keys options["size"] = options["maxlength"] unless options.key?("size") diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index d944214961..a9f137aec6 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -59,35 +59,6 @@ class FormHelperTest < ActionView::TestCase } } - I18n.backend.store_translations 'placeholder', { - activemodel: { - attributes: { - post: { - cost: "Total cost" - }, - :"post/cost" => { - uk: "Pounds" - } - } - }, - helpers: { - placeholder: { - post: { - title: "What is this about?", - written_on: { - spanish: "Escrito en" - }, - comments: { - body: "Write body here" - } - }, - tag: { - value: "Tag" - } - } - } - } - @post = Post.new @comment = Comment.new def @post.errors() @@ -326,68 +297,6 @@ class FormHelperTest < ActionView::TestCase ) end - def test_text_field_placeholder_without_locales - with_locale :placeholder do - assert_dom_equal('<input id="post_body" name="post[body]" placeholder="Body" type="text" value="Back to the hill and over it again!" />', text_field(:post, :body, placeholder: true)) - end - end - - def test_text_field_placeholder_with_locales - with_locale :placeholder do - assert_dom_equal('<input id="post_title" name="post[title]" placeholder="What is this about?" type="text" value="Hello World" />', text_field(:post, :title, placeholder: true)) - end - end - - def test_text_field_placeholder_with_human_attribute_name - with_locale :placeholder do - assert_dom_equal('<input id="post_cost" name="post[cost]" placeholder="Total cost" type="text" />', text_field(:post, :cost, placeholder: true)) - end - end - - def test_text_field_placeholder_with_human_attribute_name_and_value - with_locale :placeholder do - assert_dom_equal('<input id="post_cost" name="post[cost]" placeholder="Pounds" type="text" />', text_field(:post, :cost, placeholder: "uk")) - end - end - - def test_text_field_placeholder_with_locales_and_value - with_locale :placeholder do - assert_dom_equal('<input id="post_written_on" name="post[written_on]" placeholder="Escrito en" type="text" value="2004-06-15" />', text_field(:post, :written_on, placeholder: "spanish")) - end - end - - def test_text_field_placeholder_with_locales_and_nested_attributes - with_locale :placeholder do - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:comments) do |cf| - concat cf.text_field(:body, placeholder: true) - end - end - - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - '<input id="post_comments_attributes_0_body" name="post[comments_attributes][0][body]" placeholder="Write body here" type="text" />' - end - - assert_dom_equal expected, output_buffer - end - end - - def test_text_field_placeholder_with_locales_fallback_and_nested_attributes - with_locale :placeholder do - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:tags) do |cf| - concat cf.text_field(:value, placeholder: true) - end - end - - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" placeholder="Tag" type="text" value="new tag" />' - end - - assert_dom_equal expected, output_buffer - end - end - def test_text_field assert_dom_equal( '<input id="post_title" name="post[title]" type="text" value="Hello World" />', @@ -756,83 +665,6 @@ class FormHelperTest < ActionView::TestCase ) end - def test_text_area_placeholder_without_locales - with_locale :placeholder do - assert_dom_equal( - %{<textarea id="post_body" name="post[body]" placeholder="Body">\nBack to the hill and over it again!</textarea>}, - text_area(:post, :body, placeholder: true) - ) - end - end - - def test_text_area_placeholder_with_locales - with_locale :placeholder do - assert_dom_equal( - %{<textarea id="post_title" name="post[title]" placeholder="What is this about?">\nHello World</textarea>}, - text_area(:post, :title, placeholder: true) - ) - end - end - - def test_text_area_placeholder_with_human_attribute_name - with_locale :placeholder do - assert_dom_equal( - %{<textarea id="post_cost" name="post[cost]" placeholder="Total cost">\n</textarea>}, - text_area(:post, :cost, placeholder: true) - ) - end - end - - def test_text_area_placeholder_with_human_attribute_name_and_value - with_locale :placeholder do - assert_dom_equal( - %{<textarea id="post_cost" name="post[cost]" placeholder="Pounds">\n</textarea>}, - text_area(:post, :cost, placeholder: "uk") - ) - end - end - - def test_text_area_placeholder_with_locales_and_value - with_locale :placeholder do - assert_dom_equal( - %{<textarea id="post_written_on" name="post[written_on]" placeholder="Escrito en">\n2004-06-15</textarea>}, - text_area(:post, :written_on, placeholder: "spanish") - ) - end - end - - def test_text_area_placeholder_with_locales_and_nested_attributes - with_locale :placeholder do - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:comments) do |cf| - concat cf.text_area(:body, placeholder: true) - end - end - - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - %{<textarea id="post_comments_attributes_0_body" name="post[comments_attributes][0][body]" placeholder="Write body here">\n</textarea>} - end - - assert_dom_equal expected, output_buffer - end - end - - def test_text_area_placeholder_with_locales_fallback_and_nested_attributes - with_locale :placeholder do - form_for(@post, html: { id: 'create-post' }) do |f| - f.fields_for(:tags) do |cf| - concat cf.text_area(:value, placeholder: true) - end - end - - expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do - %{<textarea id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" placeholder="Tag">\nnew tag</textarea>} - end - - assert_dom_equal expected, output_buffer - end - end - def test_text_area assert_dom_equal( %{<textarea id="post_body" name="post[body]">\nBack to the hill and over it again!</textarea>}, diff --git a/activejob/lib/active_job/logging.rb b/activejob/lib/active_job/logging.rb index d9e544acf5..ae098a80f3 100644 --- a/activejob/lib/active_job/logging.rb +++ b/activejob/lib/active_job/logging.rb @@ -52,19 +52,19 @@ module ActiveJob class LogSubscriber < ActiveSupport::LogSubscriber def enqueue(event) - info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)}" + args_info(event) + info { "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)}" + args_info(event) } end def enqueue_at(event) - info "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) + info { "Enqueued #{event.payload[:job].name} (Job ID: #{event.payload[:job_id]}) to #{queue_name(event)} at #{enqueued_at(event)}" + args_info(event) } end def perform_start(event) - info "Performing #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) + info { "Performing #{event.payload[:job].name} from #{queue_name(event)}" + args_info(event) } end def perform(event) - info "Performed #{event.payload[:job].name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms" + info { "Performed #{event.payload[:job].name} from #{queue_name(event)} in #{event.duration.round(2).to_s}ms" } end private diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc index ca1f2fd665..569685bd45 100644 --- a/activerecord/RUNNING_UNIT_TESTS.rdoc +++ b/activerecord/RUNNING_UNIT_TESTS.rdoc @@ -32,8 +32,8 @@ defined in +Rakefile+) == Config File -If +test/config.yml+ is present, it's parameters are obeyed. Otherwise, the -parameters in +test/config.example.yml+ are obeyed. +If +test/config.yml+ is present, then its parameters are obeyed; otherwise, the +parameters in +test/config.example.yml+ are. You can override the +connections:+ parameter in either file using the +ARCONN+ (Active Record CONNection) environment variable: diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index fb8cf1cecc..4ec1c8d545 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1578,6 +1578,18 @@ module ActiveRecord # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>) # The declaration may include an +options+ hash to specialize the behavior of the association. # + # === Scopes + # + # You can pass a second argument +scope+ as a callable (i.e. proc or + # lambda) to retrieve a specific set of records or customize the generated + # query when you access the associated collection. + # + # Scope examples: + # has_and_belongs_to_many :projects, -> { includes :milestones, :manager } + # has_and_belongs_to_many :categories, ->(category) { + # where("default_category = ?", category.name) + # } + # # === Options # # [:class_name] diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 96bce53999..88d6e09d4b 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,6 +1,11 @@ +* Add the `Duration#instance_of?` method that was previously delegated to the + internal `value` attribute. + + *Robin Dupret* + * Fix rounding errors with #travel_to by resetting the usec on any passed time to zero, so we only travel with per-second precision, not anything deeper than that. - + *DHH* * Fix ActiveSupport::TestCase not to order users' test cases by default. diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 0ae641d05b..084d13a9e3 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -35,10 +35,14 @@ module ActiveSupport end def is_a?(klass) #:nodoc: - Duration == klass || value.is_a?(klass) + instance_of?(klass) || value.is_a?(klass) end alias :kind_of? :is_a? + def instance_of?(klass) # :nodoc: + Duration == klass + end + # Returns +true+ if +other+ is also a Duration instance with the # same +value+, or if <tt>other == value</tt>. def ==(other) diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 31af3c4521..330d995b7c 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -41,6 +41,11 @@ class DurationTest < ActiveSupport::TestCase assert !1.eql?(1.second) end + def test_instance_of + assert !1.minute.instance_of?(Fixnum) + assert !2.days.instance_of?(Fixnum) + end + def test_inspect assert_equal '0 seconds', 0.seconds.inspect assert_equal '1 month', 1.month.inspect diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 53b8566d83..88c6210296 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -211,7 +211,7 @@ logger.tagged("BCX") { logger.tagged("Jason") { logger.info "Stuff" } } # Logs " ### Impact of Logs on Performance Logging will always have a small impact on performance of your rails app, - particularly when logging to disk.However, there are a few subtleties: + particularly when logging to disk. However, there are a few subtleties: Using the `:debug` level will have a greater performance penalty than `:fatal`, as a far greater number of strings are being evaluated and written to the diff --git a/guides/source/generators.md b/guides/source/generators.md index 2b39ea66d8..f5d2c67cb4 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -341,13 +341,17 @@ end If you generate another resource, you can see that we get exactly the same result! This is useful if you want to customize your scaffold templates and/or layout by just creating `edit.html.erb`, `index.html.erb` and so on inside `lib/templates/erb/scaffold`. -Many scaffold templates in Rails are written in ERB tags which need to be escaped, so that the output is valid ERB code. For example, +Scaffold templates in Rails frequently use ERB tags; these tags need to be +escaped so that the generated output is valid ERB code. + +For example, the following escaped ERB tag would be needed in the template +(note the extra `%`)... ```ruby <%%= stylesheet_include_tag :application %> ``` -when passed through the generator, would generate the following output. +...to generate the following output: ```ruby <%= stylesheet_include_tag :application %> diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md index 4f0634d955..f0ee34cfb1 100644 --- a/guides/source/nested_model_forms.md +++ b/guides/source/nested_model_forms.md @@ -1,4 +1,4 @@ -Rails nested model forms +Rails Nested Model Forms ======================== Creating a form for a model _and_ its associations can become quite tedious. Therefore Rails provides helpers to assist in dealing with the complexities of generating these forms _and_ the required CRUD operations to create, update, and destroy associations. @@ -54,6 +54,9 @@ class Person < ActiveRecord::Base end ``` +NOTE: For greater detail on associations see [Active Record Associations](association_basics.html). +For a complete reference on associations please visit the API documentation for [ActiveRecord::Associations::ClassMethods](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html). + ### Custom model As you might have inflected from this explanation, you _don't_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behavior: diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index c926620b33..96ced3c2f9 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -14,7 +14,7 @@ module Rails OptionParser.new do |opt| opt.banner = "Usage: rails console [environment] [options]" - opt.on('-s', '--sandbox', 'Rollbacks database modifications on exit.') { |v| options[:sandbox] = v } + opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v } opt.on("-e", "--environment=name", String, "Specifies the environment to run this console under (test/development/production).", "Default: development") { |v| options[:environment] = v.strip } diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index ba7e7396ba..c479e92ae0 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -20,7 +20,7 @@ module Rails def option_parser(options) OptionParser.new do |opts| - opts.banner = "Usage: rails server [Mongrel, Thin etc] [options]" + opts.banner = "Usage: rails server [mongrel, thin etc] [options]" opts.on("-p", "--port=port", Integer, "Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v } opts.on("-b", "--binding=IP", String, diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb index 946b82eeb3..97d5b5c698 100644 --- a/railties/test/application/middleware/remote_ip_test.rb +++ b/railties/test/application/middleware/remote_ip_test.rb @@ -1,3 +1,4 @@ +require 'ipaddr' require 'isolation/abstract_unit' require 'active_support/key_generator' @@ -53,12 +54,25 @@ module ApplicationTests end end + test "remote_ip works with HTTP_X_FORWARDED_FOR" do + make_basic_app + assert_equal "4.2.42.42", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "4.2.42.42") + end + test "the user can set trusted proxies" do make_basic_app do |app| app.config.action_dispatch.trusted_proxies = /^4\.2\.42\.42$/ end - assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "4.2.42.42,1.1.1.1") + assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "4.2.42.42") + end + + test "the user can set trusted proxies with an IPAddr argument" do + make_basic_app do |app| + app.config.action_dispatch.trusted_proxies = IPAddr.new('4.2.42.0/24') + end + + assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1", "HTTP_X_FORWARDED_FOR" => "10.0.0.0,4.2.42.42") end end end |