Created
November 18, 2013 12:42
-
-
Save matiaskorhonen/7527168 to your computer and use it in GitHub Desktop.
Changes between Rails 3.2.13 and 3.2.15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/Gemfile b/Gemfile | |
| index c45be18..8c5cbb2 100644 | |
| --- a/Gemfile | |
| +++ b/Gemfile | |
| @@ -14,7 +14,7 @@ gem 'jquery-rails' | |
| if ENV['JOURNEY'] | |
| gem 'journey', :path => ENV['JOURNEY'] | |
| else | |
| - gem 'journey', :git => 'git://github.com/rails/journey.git', :branch => '1-0-stable' | |
| + gem 'journey' | |
| end | |
| # This needs to be with require false to avoid | |
| @@ -53,7 +53,7 @@ end | |
| platforms :ruby do | |
| gem 'yajl-ruby' | |
| - gem 'nokogiri', '>= 1.4.5' | |
| + gem 'nokogiri', '>= 1.4.5', '< 1.6' | |
| # AR | |
| gem 'sqlite3', '~> 1.3.5' | |
| @@ -67,7 +67,7 @@ end | |
| platforms :jruby do | |
| gem 'json' | |
| - gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.0' | |
| + gem 'activerecord-jdbcsqlite3-adapter', '>= 1.2.7' | |
| # This is needed by now to let tests work on JRuby | |
| # TODO: When the JRuby guys merge jruby-openssl in | |
| @@ -75,8 +75,8 @@ platforms :jruby do | |
| gem 'jruby-openssl' | |
| group :db do | |
| - gem 'activerecord-jdbcmysql-adapter', '>= 1.2.0' | |
| - gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.0' | |
| + gem 'activerecord-jdbcmysql-adapter', '>= 1.2.7' | |
| + gem 'activerecord-jdbcpostgresql-adapter', '>= 1.2.7' | |
| end | |
| end | |
| diff --git a/RAILS_VERSION b/RAILS_VERSION | |
| index d883a10..5365c9e 100644 | |
| --- a/RAILS_VERSION | |
| +++ b/RAILS_VERSION | |
| @@ -1 +1 @@ | |
| -3.2.13 | |
| +3.2.15 | |
| diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md | |
| index 3bc15fd..b8c3753 100644 | |
| --- a/actionmailer/CHANGELOG.md | |
| +++ b/actionmailer/CHANGELOG.md | |
| @@ -1,9 +1,13 @@ | |
| -## unreleased ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| * No changes. | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| +* No changes. | |
| + | |
| + | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| * No changes. | |
| diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec | |
| index c2c8e9b..20a2d0d 100644 | |
| --- a/actionmailer/actionmailer.gemspec | |
| +++ b/actionmailer/actionmailer.gemspec | |
| @@ -6,6 +6,9 @@ Gem::Specification.new do |s| | |
| s.version = version | |
| s.summary = 'Email composition, delivery, and receiving framework (part of Rails).' | |
| s.description = 'Email on Rails. Compose, deliver, receive, and test emails using the familiar controller/view pattern. First-class support for multipart email and attachments.' | |
| + | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| @@ -17,5 +20,5 @@ Gem::Specification.new do |s| | |
| s.requirements << 'none' | |
| s.add_dependency('actionpack', version) | |
| - s.add_dependency('mail', '~> 2.5.3') | |
| + s.add_dependency('mail', '~> 2.5.4') | |
| end | |
| diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb | |
| index 7ba57b1..4f4e21e 100644 | |
| --- a/actionmailer/lib/action_mailer/log_subscriber.rb | |
| +++ b/actionmailer/lib/action_mailer/log_subscriber.rb | |
| @@ -4,12 +4,12 @@ module ActionMailer | |
| class LogSubscriber < ActiveSupport::LogSubscriber | |
| def deliver(event) | |
| recipients = Array.wrap(event.payload[:to]).join(', ') | |
| - info("\nSent mail to #{recipients} (%1.fms)" % event.duration) | |
| + info("\nSent mail to #{recipients} (#{format_duration(event.duration)})") | |
| debug(event.payload[:mail]) | |
| end | |
| def receive(event) | |
| - info("\nReceived mail (%.1fms)" % event.duration) | |
| + info("\nReceived mail (#{format_duration(event.duration)})") | |
| debug(event.payload[:mail]) | |
| end | |
| @@ -19,4 +19,4 @@ def logger | |
| end | |
| end | |
| -ActionMailer::LogSubscriber.attach_to :action_mailer | |
| \ No newline at end of file | |
| +ActionMailer::LogSubscriber.attach_to :action_mailer | |
| diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb | |
| index 00f0516..80473e8 100644 | |
| --- a/actionmailer/lib/action_mailer/version.rb | |
| +++ b/actionmailer/lib/action_mailer/version.rb | |
| @@ -2,7 +2,7 @@ module ActionMailer | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb | |
| index 50438ea..bd3f193 100644 | |
| --- a/actionmailer/test/mailers/base_mailer.rb | |
| +++ b/actionmailer/test/mailers/base_mailer.rb | |
| @@ -38,7 +38,7 @@ def attachment_with_content(hash = {}) | |
| end | |
| def attachment_with_hash | |
| - attachments['invoice.jpg'] = { :data => "\312\213\254\232)b", | |
| + attachments['invoice.jpg'] = { :data => ::Base64.encode64("\312\213\254\232)b"), | |
| :mime_type => "image/x-jpg", | |
| :transfer_encoding => "base64" } | |
| diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md | |
| index f1eb9e8..ca2a145 100644 | |
| --- a/actionpack/CHANGELOG.md | |
| +++ b/actionpack/CHANGELOG.md | |
| @@ -1,9 +1,129 @@ | |
| -## unreleased ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| -* No changes. | |
| +* Fix `ActionDispatch::RemoteIp::GetIp#calculate_ip` to only check for spoofing | |
| + attacks if both `HTTP_CLIENT_IP` and `HTTP_X_FORWARDED_FOR` are set. | |
| + | |
| + Fixes #12410 | |
| + Backports #10844 | |
| + | |
| + *Tamir Duberstein* | |
| + | |
| +* Fix the assert_recognizes test method so that it works when there are | |
| + constraints on the querystring. | |
| + | |
| + Issue/Pull Request #9368 | |
| + Backport #5219 | |
| + | |
| + *Brian Hahn* | |
| + | |
| +* Fix to render partial by context(#11605). | |
| + | |
| + *Kassio Borges* | |
| + | |
| +* Fix `ActionDispatch::Assertions::ResponseAssertions#assert_redirected_to` | |
| + does not show user-supplied message. | |
| + | |
| + Issue: when `assert_redirected_to` fails due to the response redirect not | |
| + matching the expected redirect the user-supplied message (second parameter) | |
| + is not shown. This message is only shown if the response is not a redirect. | |
| + | |
| + *Alexey Chernenkov* | |
| + | |
| + | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| + | |
| +* Merge `:action` from routing scope and assign endpoint if both `:controller` | |
| + and `:action` are present. The endpoint assignment only occurs if there is | |
| + no `:to` present in the options hash so should only affect routes using the | |
| + shorthand syntax (i.e. endpoint is inferred from the the path). | |
| + | |
| + Fixes #9856 | |
| + | |
| + *Yves Senn*, *Andrew White* | |
| + | |
| +* Always escape the result of `link_to_unless` method. | |
| + | |
| + Before: | |
| + | |
| + link_to_unless(true, '<b>Showing</b>', 'github.com') | |
| + # => "<b>Showing</b>" | |
| + | |
| + After: | |
| + | |
| + link_to_unless(true, '<b>Showing</b>', 'github.com') | |
| + # => "<b>Showing</b>" | |
| + | |
| + *dtaniwaki* | |
| + | |
| +* Use a case insensitive URI Regexp for #asset_path. | |
| + | |
| + This fix a problem where the same asset path using different case are generating | |
| + different URIs. | |
| + | |
| + Before: | |
| + | |
| + image_tag("HTTP://google.com") | |
| + # => "<img alt=\"Google\" src=\"/assets/HTTP://google.com\" />" | |
| + image_tag("http://google.com") | |
| + # => "<img alt=\"Google\" src=\"http://google.com\" />" | |
| + | |
| + After: | |
| + | |
| + image_tag("HTTP://google.com") | |
| + # => "<img alt=\"Google\" src=\"HTTP://google.com\" />" | |
| + image_tag("http://google.com") | |
| + # => "<img alt=\"Google\" src=\"http://google.com\" />" | |
| + | |
| + *David Celis + Rafael Mendonça França* | |
| + | |
| +* Fix explicit names on multiple file fields. If a file field tag has | |
| + the multiple option, it is turned into an array field (appending `[]`), | |
| + but if an explicit name is passed to `file_field` the `[]` is not | |
| + appended. | |
| + Fixes #9830. | |
| + | |
| + *Ryan McGeary* | |
| + | |
| +* Fix assets loading performance in 3.2.13. | |
| + | |
| + Issue #8756 uses Sprockets for resolving files that already exist on disk, | |
| + for those files their extensions don't need to be rewritten. | |
| + | |
| + Fixes #9803. | |
| + | |
| + *Fred Wu* | |
| + | |
| +* Fix `ActionController#action_missing` not being called. | |
| + Fixes #9799. | |
| + | |
| + *Janko Luin* | |
| + | |
| +* `ActionView::Helpers::NumberHelper#number_to_human` returns the number unaltered when | |
| + the units hash does not contain the needed key, e.g. when the number provided is less | |
| + than the largest key provided. | |
| + | |
| + Examples: | |
| + | |
| + number_to_human(123, units: {}) # => 123 | |
| + number_to_human(123, units: { thousand: 'k' }) # => 123 | |
| + | |
| + Fixes #9269. | |
| + Backport #9347. | |
| + | |
| + *Michael Hoffman* | |
| + | |
| +* Include I18n locale fallbacks in view lookup. | |
| + Fixes GH#3512. | |
| + | |
| + *Juan Barreneche* | |
| + | |
| +* Fix `ActionDispatch::Request#formats` when the Accept request-header is an | |
| + empty string. Fix #7774 [Backport #8977, #9541] | |
| + | |
| + *Soylent + Maxime Réty* | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| * Fix incorrectly appended square brackets to a multiple select box | |
| if an explicit name has been given and it already ends with "[]". | |
| @@ -43,7 +163,7 @@ | |
| *Sergey Nartimov* | |
| -* Eagerly populate the http method loookup cache so local project inflections do | |
| +* Eagerly populate the http method lookup cache so local project inflections do | |
| not interfere with use of underscore method ( and we don't need locks ) | |
| *Aditya Sanghi* | |
| diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec | |
| index f018425..cd906c0 100644 | |
| --- a/actionpack/actionpack.gemspec | |
| +++ b/actionpack/actionpack.gemspec | |
| @@ -6,6 +6,9 @@ Gem::Specification.new do |s| | |
| s.version = version | |
| s.summary = 'Web-flow and rendering framework putting the VC in MVC (part of Rails).' | |
| s.description = 'Web apps on Rails. Simple, battle-tested conventions for building and testing MVC web applications. Works with any Rack-compatible server.' | |
| + | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb | |
| index 194f26a..f2545ef 100644 | |
| --- a/actionpack/lib/action_controller/log_subscriber.rb | |
| +++ b/actionpack/lib/action_controller/log_subscriber.rb | |
| @@ -23,7 +23,7 @@ def process_action(event) | |
| exception_class_name = payload[:exception].first | |
| status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name) | |
| end | |
| - message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration | |
| + message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{format_duration(event.duration)}" | |
| message << " (#{additions.join(" | ")})" unless additions.blank? | |
| info(message) | |
| @@ -34,9 +34,7 @@ def halted_callback(event) | |
| end | |
| def send_file(event) | |
| - message = "Sent file %s" | |
| - message << " (%.1fms)" | |
| - info(message % [event.payload[:path], event.duration]) | |
| + info("Sent file #{event.payload[:path]} (#{format_duration(event.duration)})") | |
| end | |
| def redirect_to(event) | |
| @@ -44,7 +42,7 @@ def redirect_to(event) | |
| end | |
| def send_data(event) | |
| - info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration]) | |
| + info("Sent data #{event.payload[:filename]} (#{format_duration(event.duration)})") | |
| end | |
| %w(write_fragment read_fragment exist_fragment? | |
| @@ -53,7 +51,8 @@ def send_data(event) | |
| def #{method}(event) | |
| key_or_path = event.payload[:key] || event.payload[:path] | |
| human_name = #{method.to_s.humanize.inspect} | |
| - info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}") | |
| + duration = format_duration(event.duration) | |
| + info("\#{human_name} \#{key_or_path} \#{duration}") | |
| end | |
| METHOD | |
| end | |
| diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb | |
| index 109484d..1ded166 100644 | |
| --- a/actionpack/lib/action_controller/metal/hide_actions.rb | |
| +++ b/actionpack/lib/action_controller/metal/hide_actions.rb | |
| @@ -28,7 +28,7 @@ def hide_action(*args) | |
| end | |
| def visible_action?(action_name) | |
| - action_methods.include?(action_name) | |
| + not hidden_actions.include?(action_name) | |
| end | |
| # Overrides AbstractController::Base#action_methods to remove any methods | |
| diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb | |
| index bc6828a..e089fee 100644 | |
| --- a/actionpack/lib/action_controller/test_case.rb | |
| +++ b/actionpack/lib/action_controller/test_case.rb | |
| @@ -20,7 +20,12 @@ def setup_subscriptions | |
| ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload| | |
| path = payload[:layout] | |
| - @layouts[path] += 1 | |
| + if path | |
| + @layouts[path] += 1 | |
| + if path =~ /^layouts\/(.*)/ | |
| + @layouts[$1] += 1 | |
| + end | |
| + end | |
| end | |
| ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload| | |
| @@ -56,6 +61,15 @@ def process(*args) | |
| # # assert that the "new" view template was rendered | |
| # assert_template "new" | |
| # | |
| + # # assert that the layout 'admin' was rendered | |
| + # assert_template :layout => 'admin' | |
| + # assert_template :layout => 'layouts/admin' | |
| + # assert_template :layout => :admin | |
| + # | |
| + # # assert that no layout was rendered | |
| + # assert_template :layout => nil | |
| + # assert_template :layout => false | |
| + # | |
| # # assert that the "_customer" partial was rendered twice | |
| # assert_template :partial => '_customer', :count => 2 | |
| # | |
| @@ -88,17 +102,18 @@ def assert_template(options = {}, message = nil) | |
| end | |
| end | |
| when Hash | |
| - if expected_layout = options[:layout] | |
| + if options.key?(:layout) | |
| + expected_layout = options[:layout] | |
| msg = build_message(message, | |
| "expecting layout <?> but action rendered <?>", | |
| expected_layout, @layouts.keys) | |
| case expected_layout | |
| - when String | |
| - assert(@layouts.keys.include?(expected_layout), msg) | |
| + when String, Symbol | |
| + assert(@layouts.keys.include?(expected_layout.to_s), msg) | |
| when Regexp | |
| assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg) | |
| - when nil | |
| + when nil, false | |
| assert(@layouts.empty?, msg) | |
| end | |
| end | |
| @@ -125,7 +140,7 @@ def assert_template(options = {}, message = nil) | |
| options[:partial], @partials.keys) | |
| assert(@partials.include?(expected_partial), msg) | |
| end | |
| - else | |
| + elsif options.key?(:partial) | |
| assert @partials.empty?, | |
| "Expected no partials to be rendered" | |
| end | |
| @@ -460,7 +475,7 @@ def process(action, parameters = nil, session = nil, flash = nil, http_method = | |
| parameters ||= {} | |
| controller_class_name = @controller.class.anonymous? ? | |
| "anonymous_controller" : | |
| - @controller.class.name.underscore.sub(/_controller$/, '') | |
| + @controller.class.controller_path | |
| @request.assign_parameters(@routes, controller_class_name, action.to_s, parameters) | |
| diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb | |
| index 5c48a60..42f14bc 100644 | |
| --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb | |
| +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb | |
| @@ -98,8 +98,8 @@ def negotiate_mime(order) | |
| BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/ | |
| def valid_accept_header | |
| - (xhr? && (accept || content_mime_type)) || | |
| - (accept && accept !~ BROWSER_LIKE_ACCEPTS) | |
| + (xhr? && (accept.present? || content_mime_type)) || | |
| + (accept.present? && accept !~ BROWSER_LIKE_ACCEPTS) | |
| end | |
| def use_accept_header | |
| diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb | |
| index 66ece60..f4545bd 100644 | |
| --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb | |
| +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb | |
| @@ -49,7 +49,7 @@ def calculate_ip | |
| forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR') | |
| remote_addrs = ips_from('REMOTE_ADDR') | |
| - check_ip = client_ip && @middleware.check_ip | |
| + check_ip = client_ip && forwarded_ips.present? && @middleware.check_ip | |
| if check_ip && !forwarded_ips.include?(client_ip) | |
| # We don't know which came from the proxy, and which from the user | |
| raise IpSpoofAttackError, "IP spoofing attack?!" \ | |
| diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb | |
| index 6b27270..8e28e0d 100644 | |
| --- a/actionpack/lib/action_dispatch/routing/mapper.rb | |
| +++ b/actionpack/lib/action_dispatch/routing/mapper.rb | |
| @@ -1,6 +1,7 @@ | |
| require 'active_support/core_ext/hash/except' | |
| require 'active_support/core_ext/object/blank' | |
| require 'active_support/core_ext/object/inclusion' | |
| +require 'active_support/core_ext/enumerable' | |
| require 'active_support/inflector' | |
| require 'action_dispatch/routing/redirection' | |
| @@ -67,8 +68,6 @@ def to_route | |
| private | |
| def normalize_options! | |
| - path_without_format = @path.sub(/\(\.:format\)$/, '') | |
| - | |
| @options.merge!(default_controller_and_action) | |
| requirements.each do |name, requirement| | |
| @@ -781,6 +780,10 @@ def merge_controller_scope(parent, child) #:nodoc: | |
| child | |
| end | |
| + def merge_action_scope(parent, child) #:nodoc: | |
| + child | |
| + end | |
| + | |
| def merge_path_names_scope(parent, child) #:nodoc: | |
| merge_options_scope(parent, child) | |
| end | |
| @@ -1255,6 +1258,10 @@ def match(path, *rest) | |
| paths = [path] + rest | |
| end | |
| + if @scope[:controller] && @scope[:action] | |
| + options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}" | |
| + end | |
| + | |
| path_without_format = path.to_s.sub(/\(\.:format\)$/, '') | |
| if using_match_shorthand?(path_without_format, options) | |
| options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1') | |
| diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb | |
| index a993699..d9671a4 100644 | |
| --- a/actionpack/lib/action_dispatch/routing/route_set.rb | |
| +++ b/actionpack/lib/action_dispatch/routing/route_set.rb | |
| @@ -97,9 +97,7 @@ def initialize | |
| @routes = {} | |
| @helpers = [] | |
| - @module = Module.new do | |
| - instance_methods.each { |selector| remove_method(selector) } | |
| - end | |
| + @module = Module.new | |
| end | |
| def helper_names | |
| @@ -108,13 +106,11 @@ def helper_names | |
| def clear! | |
| @helpers.each do |helper| | |
| - @module.module_eval do | |
| - remove_possible_method helper | |
| - end | |
| + @module.remove_possible_method helper | |
| end | |
| - @routes = {} | |
| - @helpers = [] | |
| + @routes.clear | |
| + @helpers.clear | |
| end | |
| def add(name, route) | |
| @@ -615,9 +611,10 @@ def call(env) | |
| def recognize_path(path, environment = {}) | |
| method = (environment[:method] || "GET").to_s.upcase | |
| path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://} | |
| + extras = environment[:extras] || {} | |
| begin | |
| - env = Rack::MockRequest.env_for(path, {:method => method}) | |
| + env = Rack::MockRequest.env_for(path, {:method => method, :params => extras}) | |
| rescue URI::InvalidURIError => e | |
| raise ActionController::RoutingError, e.message | |
| end | |
| diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb | |
| index 6886ff2..cd3329c 100644 | |
| --- a/actionpack/lib/action_dispatch/testing/assertions/response.rb | |
| +++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb | |
| @@ -62,7 +62,7 @@ def assert_redirected_to(options = {}, message=nil) | |
| redirect_expected = normalize_argument_to_redirection(options) | |
| if redirect_is != redirect_expected | |
| - flunk "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>" | |
| + flunk(build_message(message, "Expected response to be a redirect to <?> but was a redirect to <?>", redirect_expected, redirect_is)) | |
| end | |
| end | |
| diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb | |
| index b10aab9..2a25d3f 100644 | |
| --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb | |
| +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb | |
| @@ -39,7 +39,7 @@ module RoutingAssertions | |
| # # Test a custom route | |
| # assert_recognizes({:controller => 'items', :action => 'show', :id => '1'}, 'view/item1') | |
| def assert_recognizes(expected_options, path, extras={}, message=nil) | |
| - request = recognized_request_for(path) | |
| + request = recognized_request_for(path, extras) | |
| expected_options = expected_options.clone | |
| extras.each_key { |key| expected_options.delete key } unless extras.nil? | |
| @@ -179,7 +179,7 @@ def method_missing(selector, *args, &block) | |
| private | |
| # Recognizes the route for a given path. | |
| - def recognized_request_for(path) | |
| + def recognized_request_for(path, extras = {}) | |
| if path.is_a?(Hash) | |
| method = path[:method] | |
| path = path[:path] | |
| @@ -207,7 +207,7 @@ def recognized_request_for(path) | |
| request.request_method = method if method | |
| - params = @routes.recognize_path(path, { :method => method }) | |
| + params = @routes.recognize_path(path, { :method => method, :extras => extras }) | |
| request.path_parameters = params.with_indifferent_access | |
| request | |
| diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb | |
| index f319266..dda9fd6 100644 | |
| --- a/actionpack/lib/action_pack/version.rb | |
| +++ b/actionpack/lib/action_pack/version.rb | |
| @@ -2,7 +2,7 @@ module ActionPack | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb | |
| index c192d37..636a37b 100644 | |
| --- a/actionpack/lib/action_view/asset_paths.rb | |
| +++ b/actionpack/lib/action_view/asset_paths.rb | |
| @@ -43,7 +43,7 @@ def compute_source_path(source, dir, ext) | |
| end | |
| def is_uri?(path) | |
| - path =~ %r{^[-a-z]+://|^(?:cid|data):|^//} | |
| + path =~ %r{^[-a-z]+://|^(?:cid|data):|^//}i | |
| end | |
| private | |
| diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb | |
| index 99aa144..39b9a8d 100644 | |
| --- a/actionpack/lib/action_view/helpers/date_helper.rb | |
| +++ b/actionpack/lib/action_view/helpers/date_helper.rb | |
| @@ -112,7 +112,7 @@ def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, o | |
| # english it would read better as about 80 years. | |
| minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year | |
| remainder = (minutes_with_offset % 525600) | |
| - distance_in_years = (minutes_with_offset / 525600) | |
| + distance_in_years = (minutes_with_offset.div 525600) | |
| if remainder < 131400 | |
| locale.t(:about_x_years, :count => distance_in_years) | |
| elsif remainder < 394200 | |
| diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb | |
| index 920dc3f..0c07925 100644 | |
| --- a/actionpack/lib/action_view/helpers/form_helper.rb | |
| +++ b/actionpack/lib/action_view/helpers/form_helper.rb | |
| @@ -1196,27 +1196,26 @@ def add_default_name_and_id_for_value(tag_value, options) | |
| def add_default_name_and_id(options) | |
| if options.has_key?("index") | |
| - options["name"] ||= tag_name_with_index(options["index"]) | |
| + options["name"] ||= tag_name_with_index(options["index"], options["multiple"]) | |
| options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) } | |
| options.delete("index") | |
| elsif defined?(@auto_index) | |
| - options["name"] ||= tag_name_with_index(@auto_index) | |
| + options["name"] ||= tag_name_with_index(@auto_index, options["multiple"]) | |
| options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) } | |
| else | |
| - options["name"] ||= tag_name | |
| + options["name"] ||= tag_name(options["multiple"]) | |
| options["id"] = options.fetch("id"){ tag_id } | |
| end | |
| - options["name"] += "[]" if options["multiple"] && !options["name"].ends_with?("[]") | |
| options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence | |
| end | |
| - def tag_name | |
| - "#{@object_name}[#{sanitized_method_name}]" | |
| + def tag_name(multiple = false) | |
| + "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}" | |
| end | |
| - def tag_name_with_index(index) | |
| - "#{@object_name}[#{index}][#{sanitized_method_name}]" | |
| + def tag_name_with_index(index, multiple = false) | |
| + "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}" | |
| end | |
| def tag_id | |
| diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb | |
| index b627c77..ad86d13 100644 | |
| --- a/actionpack/lib/action_view/helpers/number_helper.rb | |
| +++ b/actionpack/lib/action_view/helpers/number_helper.rb | |
| @@ -360,7 +360,7 @@ def number_with_precision(number, options = {}) | |
| end | |
| - STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb].freeze | |
| + STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb] | |
| # Formats the bytes in +number+ into a more understandable | |
| # representation (e.g., giving it 1500 yields 1.5 KB). This | |
| @@ -452,7 +452,7 @@ def number_to_human_size(number, options = {}) | |
| end | |
| DECIMAL_UNITS = {0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion, | |
| - -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto}.freeze | |
| + -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto} | |
| # Pretty prints (formats and approximates) a number in a way it | |
| # is more readable by humans (eg.: 1200000000 becomes "1.2 | |
| @@ -593,7 +593,7 @@ def number_to_human(number, options = {}) | |
| unit = case units | |
| when Hash | |
| - units[DECIMAL_UNITS[display_exponent]] | |
| + units[DECIMAL_UNITS[display_exponent]] || '' | |
| when String, Symbol | |
| I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i) | |
| else | |
| diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb | |
| index 812bb4d..51c3100 100644 | |
| --- a/actionpack/lib/action_view/helpers/url_helper.rb | |
| +++ b/actionpack/lib/action_view/helpers/url_helper.rb | |
| @@ -422,7 +422,7 @@ def link_to_unless(condition, name, options = {}, html_options = {}, &block) | |
| if block_given? | |
| block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block) | |
| else | |
| - name | |
| + ERB::Util.html_escape(name) | |
| end | |
| else | |
| link_to(name, options, html_options) | |
| diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb | |
| index 33b508e..9f617a9 100644 | |
| --- a/actionpack/lib/action_view/lookup_context.rb | |
| +++ b/actionpack/lib/action_view/lookup_context.rb | |
| @@ -44,7 +44,13 @@ def initialize_details(details) | |
| module Accessors #:nodoc: | |
| end | |
| - register_detail(:locale) { [I18n.locale, I18n.default_locale].uniq } | |
| + register_detail(:locale) do | |
| + locales = [I18n.locale] | |
| + locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks | |
| + locales << I18n.default_locale | |
| + locales.uniq! | |
| + locales | |
| + end | |
| register_detail(:formats) { Mime::SET.symbols } | |
| register_detail(:handlers){ Template::Handlers.extensions } | |
| diff --git a/actionpack/lib/sprockets/helpers/rails_helper.rb b/actionpack/lib/sprockets/helpers/rails_helper.rb | |
| index 51f0cbb..243c2e5 100644 | |
| --- a/actionpack/lib/sprockets/helpers/rails_helper.rb | |
| +++ b/actionpack/lib/sprockets/helpers/rails_helper.rb | |
| @@ -163,7 +163,7 @@ def rewrite_extension(source, dir, ext) | |
| source | |
| elsif source_ext.blank? | |
| "#{source}.#{ext}" | |
| - elsif exact_match_present?(source) | |
| + elsif File.exists?(source) || exact_match_present?(source) | |
| source | |
| else | |
| "#{source}.#{ext}" | |
| diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb | |
| index 5252e43..3b5a515 100644 | |
| --- a/actionpack/test/controller/action_pack_assertions_test.rb | |
| +++ b/actionpack/test/controller/action_pack_assertions_test.rb | |
| @@ -76,6 +76,11 @@ def render_with_layout | |
| render "test/hello_world", :layout => "layouts/standard" | |
| end | |
| + def render_with_layout_and_partial | |
| + @variable_for_layout = nil | |
| + render "test/hello_world_with_partial", :layout => "layouts/standard" | |
| + end | |
| + | |
| def session_stuffing | |
| session['xmas'] = 'turkey' | |
| render :text => "ho ho ho" | |
| @@ -483,11 +488,43 @@ def test_fails_with_wrong_layout | |
| end | |
| end | |
| + def test_fails_expecting_no_layout | |
| + get :render_with_layout | |
| + assert_raise(ActiveSupport::TestCase::Assertion) do | |
| + assert_template :layout => nil | |
| + end | |
| + end | |
| + | |
| def test_passes_with_correct_layout | |
| get :render_with_layout | |
| assert_template :layout => "layouts/standard" | |
| end | |
| + def test_passes_with_layout_and_partial | |
| + get :render_with_layout_and_partial | |
| + assert_template :layout => "layouts/standard" | |
| + end | |
| + | |
| + def test_passed_with_no_layout | |
| + get :hello_world | |
| + assert_template :layout => nil | |
| + end | |
| + | |
| + def test_passed_with_no_layout_false | |
| + get :hello_world | |
| + assert_template :layout => false | |
| + end | |
| + | |
| + def test_passes_with_correct_layout_without_layouts_prefix | |
| + get :render_with_layout | |
| + assert_template :layout => "standard" | |
| + end | |
| + | |
| + def test_passes_with_correct_layout_symbol | |
| + get :render_with_layout | |
| + assert_template :layout => :standard | |
| + end | |
| + | |
| def test_assert_template_reset_between_requests | |
| get :hello_world | |
| assert_template 'test/hello_world' | |
| diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb | |
| index affa9a6..a652d8f 100644 | |
| --- a/actionpack/test/controller/base_test.rb | |
| +++ b/actionpack/test/controller/base_test.rb | |
| @@ -86,6 +86,12 @@ def url_options | |
| class RecordIdentifierController < ActionController::Base | |
| end | |
| +class ActionMissingController < ActionController::Base | |
| + def action_missing(action) | |
| + render :text => "Response for #{action}" | |
| + end | |
| +end | |
| + | |
| class ControllerClassTests < ActiveSupport::TestCase | |
| def test_controller_path | |
| @@ -196,6 +202,12 @@ def test_get_on_hidden_should_fail | |
| assert_raise(AbstractController::ActionNotFound) { get :hidden_action } | |
| assert_raise(AbstractController::ActionNotFound) { get :another_hidden_action } | |
| end | |
| + | |
| + def test_action_missing_should_work | |
| + use_controller ActionMissingController | |
| + get :arbitrary_action | |
| + assert_equal "Response for arbitrary_action", @response.body | |
| + end | |
| end | |
| class UrlOptionsTest < ActionController::TestCase | |
| diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb | |
| index 41ff2f3..a5fc3f6 100644 | |
| --- a/actionpack/test/controller/localized_templates_test.rb | |
| +++ b/actionpack/test/controller/localized_templates_test.rb | |
| @@ -19,4 +19,13 @@ def test_default_locale_template_is_used_when_locale_is_missing | |
| get :hello_world | |
| assert_equal "Hello World", @response.body | |
| end | |
| + | |
| + def test_use_fallback_locales | |
| + I18n.locale = :"de-AT" | |
| + I18n.backend.class.send(:include, I18n::Backend::Fallbacks) | |
| + I18n.fallbacks[:"de-AT"] = [:de] | |
| + | |
| + get :hello_world | |
| + assert_equal "Gutten Tag", @response.body | |
| + end | |
| end | |
| \ No newline at end of file | |
| diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb | |
| index dfd3ddb..222cdfd 100644 | |
| --- a/actionpack/test/dispatch/request_test.rb | |
| +++ b/actionpack/test/dispatch/request_test.rb | |
| @@ -481,6 +481,15 @@ def url_for(options = {}) | |
| request.expects(:parameters).at_least_once.returns({}) | |
| assert_equal [ Mime::HTML ], request.formats | |
| + request = stub_request 'HTTP_ACCEPT' => '' | |
| + request.expects(:parameters).at_least_once.returns({}) | |
| + assert_equal [Mime::HTML], request.formats | |
| + | |
| + request = stub_request 'HTTP_ACCEPT' => '', | |
| + 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" | |
| + request.expects(:parameters).at_least_once.returns({}) | |
| + assert_equal [Mime::JS], request.formats | |
| + | |
| request = stub_request 'CONTENT_TYPE' => 'application/xml; charset=UTF-8', | |
| 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest" | |
| request.expects(:parameters).at_least_once.returns({}) | |
| diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb | |
| index 9f95d82..1ca0895 100644 | |
| --- a/actionpack/test/dispatch/routing_assertions_test.rb | |
| +++ b/actionpack/test/dispatch/routing_assertions_test.rb | |
| @@ -3,6 +3,7 @@ | |
| class SecureArticlesController < ArticlesController; end | |
| class BlockArticlesController < ArticlesController; end | |
| +class QueryArticlesController < ArticlesController; end | |
| class RoutingAssertionsTest < ActionController::TestCase | |
| @@ -18,6 +19,10 @@ def setup | |
| scope 'block', :constraints => lambda { |r| r.ssl? } do | |
| resources :articles, :controller => 'block_articles' | |
| end | |
| + | |
| + scope 'query', :constraints => lambda { |r| r.params[:use_query] == 'true' } do | |
| + resources :articles, :controller => 'query_articles' | |
| + end | |
| end | |
| end | |
| @@ -42,7 +47,7 @@ def test_assert_recognizes | |
| def test_assert_recognizes_with_extras | |
| assert_recognizes({ :controller => 'articles', :action => 'index', :page => '1' }, '/articles', { :page => '1' }) | |
| end | |
| - | |
| + | |
| def test_assert_recognizes_with_method | |
| assert_recognizes({ :controller => 'articles', :action => 'create' }, { :path => '/articles', :method => :post }) | |
| assert_recognizes({ :controller => 'articles', :action => 'update', :id => '1' }, { :path => '/articles/1', :method => :put }) | |
| @@ -62,6 +67,13 @@ def test_assert_recognizes_with_block_constraint | |
| assert_recognizes({ :controller => 'block_articles', :action => 'index' }, 'https://test.host/block/articles') | |
| end | |
| + def test_assert_recognizes_with_query_constraint | |
| + assert_raise(ActionController::RoutingError) do | |
| + assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'false' }, '/query/articles', { :use_query => 'false' }) | |
| + end | |
| + assert_recognizes({ :controller => 'query_articles', :action => 'index', :use_query => 'true' }, '/query/articles', { :use_query => 'true' }) | |
| + end | |
| + | |
| def test_assert_routing | |
| assert_routing('/articles', :controller => 'articles', :action => 'index') | |
| end | |
| diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb | |
| index 88a5c37..659c6c7 100644 | |
| --- a/actionpack/test/dispatch/routing_test.rb | |
| +++ b/actionpack/test/dispatch/routing_test.rb | |
| @@ -529,6 +529,12 @@ def self.call(params, request) | |
| end | |
| end | |
| end | |
| + | |
| + scope '/job', :controller => 'job' do | |
| + scope ':id', :action => 'manage_applicant' do | |
| + get "/active" | |
| + end | |
| + end | |
| end | |
| end | |
| @@ -1444,6 +1450,13 @@ def test_match_shorthand_inside_nested_namespaces_and_scopes_with_controller | |
| end | |
| end | |
| + def test_controller_option_with_nesting_and_leading_slash | |
| + with_test_routes do | |
| + get '/job/5/active' | |
| + assert_equal 'job#manage_applicant', @response.body | |
| + end | |
| + end | |
| + | |
| def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper | |
| with_test_routes do | |
| assert_equal '/replies', replies_path | |
| diff --git a/actionpack/test/fixtures/test/change_priority.html.erb b/actionpack/test/fixtures/test/change_priority.html.erb | |
| new file mode 100644 | |
| index 0000000..71ecef1 | |
| --- /dev/null | |
| +++ b/actionpack/test/fixtures/test/change_priority.html.erb | |
| @@ -0,0 +1,2 @@ | |
| +<%= render :partial => "test/json_change_priority", :formats => :json %> | |
| +HTML Template, but <%= render :partial => "test/changing_priority" %> partial | |
| \ No newline at end of file | |
| diff --git a/actionpack/test/fixtures/test/hello_world_with_partial.html.erb b/actionpack/test/fixtures/test/hello_world_with_partial.html.erb | |
| new file mode 100644 | |
| index 0000000..ec31545 | |
| --- /dev/null | |
| +++ b/actionpack/test/fixtures/test/hello_world_with_partial.html.erb | |
| @@ -0,0 +1,2 @@ | |
| +Hello world! | |
| +<%= render '/test/partial' %> | |
| diff --git a/actionpack/test/metal/caching_test.rb b/actionpack/test/metal/caching_test.rb | |
| deleted file mode 100644 | |
| index a2b6763..0000000 | |
| --- a/actionpack/test/metal/caching_test.rb | |
| +++ /dev/null | |
| @@ -1,32 +0,0 @@ | |
| -require 'abstract_unit' | |
| - | |
| -CACHE_DIR = 'test_cache' | |
| -# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed | |
| -FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR) | |
| - | |
| -class CachingController < ActionController::Metal | |
| - abstract! | |
| - | |
| - include ActionController::Caching | |
| - | |
| - self.page_cache_directory = FILE_STORE_PATH | |
| - self.cache_store = :file_store, FILE_STORE_PATH | |
| -end | |
| - | |
| -class PageCachingTestController < CachingController | |
| - caches_page :ok | |
| - | |
| - def ok | |
| - self.response_body = "ok" | |
| - end | |
| -end | |
| - | |
| -class PageCachingTest < ActionController::TestCase | |
| - tests PageCachingTestController | |
| - | |
| - def test_should_cache_get_with_ok_status | |
| - get :ok | |
| - assert_response :ok | |
| - assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/ok.html"), "get with ok status should have been cached" | |
| - end | |
| -end | |
| diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb | |
| index b1a01b5..6b1bc01 100644 | |
| --- a/actionpack/test/template/asset_tag_helper_test.rb | |
| +++ b/actionpack/test/template/asset_tag_helper_test.rb | |
| @@ -79,13 +79,17 @@ def teardown | |
| JavascriptPathToTag = { | |
| %(javascript_path("xmlhr")) => %(/javascripts/xmlhr.js), | |
| %(javascript_path("super/xmlhr")) => %(/javascripts/super/xmlhr.js), | |
| - %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js) | |
| + %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js), | |
| + %(javascript_path("http://www.outside.com/foo.js")) => %(http://www.outside.com/foo.js), | |
| + %(javascript_path("HTTP://www.outside.com/foo.js")) => %(HTTP://www.outside.com/foo.js) | |
| } | |
| PathToJavascriptToTag = { | |
| %(path_to_javascript("xmlhr")) => %(/javascripts/xmlhr.js), | |
| %(path_to_javascript("super/xmlhr")) => %(/javascripts/super/xmlhr.js), | |
| - %(path_to_javascript("/super/xmlhr.js")) => %(/super/xmlhr.js) | |
| + %(path_to_javascript("/super/xmlhr.js")) => %(/super/xmlhr.js), | |
| + %(path_to_javascript("http://www.outside.com/foo.js")) => %(http://www.outside.com/foo.js), | |
| + %(path_to_javascript("HTTP://www.outside.com/foo.js")) => %(HTTP://www.outside.com/foo.js) | |
| } | |
| JavascriptIncludeToTag = { | |
| @@ -109,14 +113,18 @@ def teardown | |
| %(stylesheet_path("bank")) => %(/stylesheets/bank.css), | |
| %(stylesheet_path("bank.css")) => %(/stylesheets/bank.css), | |
| %(stylesheet_path('subdir/subdir')) => %(/stylesheets/subdir/subdir.css), | |
| - %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css) | |
| + %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css), | |
| + %(stylesheet_path("http://www.outside.com/foo.css")) => %(http://www.outside.com/foo.css), | |
| + %(stylesheet_path("HTTP://www.outside.com/foo.css")) => %(HTTP://www.outside.com/foo.css) | |
| } | |
| PathToStyleToTag = { | |
| %(path_to_stylesheet("style")) => %(/stylesheets/style.css), | |
| %(path_to_stylesheet("style.css")) => %(/stylesheets/style.css), | |
| %(path_to_stylesheet('dir/file')) => %(/stylesheets/dir/file.css), | |
| - %(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss) | |
| + %(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss), | |
| + %(path_to_stylesheet("http://www.outside.com/foo.css")) => %(http://www.outside.com/foo.css), | |
| + %(path_to_stylesheet("HTTP://www.outside.com/foo.css")) => %(HTTP://www.outside.com/foo.css) | |
| } | |
| StyleLinkToTag = { | |
| @@ -139,14 +147,18 @@ def teardown | |
| %(image_path("xml")) => %(/images/xml), | |
| %(image_path("xml.png")) => %(/images/xml.png), | |
| %(image_path("dir/xml.png")) => %(/images/dir/xml.png), | |
| - %(image_path("/dir/xml.png")) => %(/dir/xml.png) | |
| + %(image_path("/dir/xml.png")) => %(/dir/xml.png), | |
| + %(image_path("http://www.outside.com/foo.png")) => %(http://www.outside.com/foo.png), | |
| + %(image_path("HTTP://www.outside.com/foo.png")) => %(HTTP://www.outside.com/foo.png) | |
| } | |
| PathToImageToTag = { | |
| %(path_to_image("xml")) => %(/images/xml), | |
| %(path_to_image("xml.png")) => %(/images/xml.png), | |
| %(path_to_image("dir/xml.png")) => %(/images/dir/xml.png), | |
| - %(path_to_image("/dir/xml.png")) => %(/dir/xml.png) | |
| + %(path_to_image("/dir/xml.png")) => %(/dir/xml.png), | |
| + %(path_to_image("http://www.outside.com/foo.png")) => %(http://www.outside.com/foo.png), | |
| + %(path_to_image("HTTP://www.outside.com/foo.png")) => %(HTTP://www.outside.com/foo.png) | |
| } | |
| ImageLinkToTag = { | |
| @@ -181,14 +193,18 @@ def teardown | |
| %(video_path("xml")) => %(/videos/xml), | |
| %(video_path("xml.ogg")) => %(/videos/xml.ogg), | |
| %(video_path("dir/xml.ogg")) => %(/videos/dir/xml.ogg), | |
| - %(video_path("/dir/xml.ogg")) => %(/dir/xml.ogg) | |
| + %(video_path("/dir/xml.ogg")) => %(/dir/xml.ogg), | |
| + %(video_path("http://www.outside.com/foo.ogg")) => %(http://www.outside.com/foo.ogg), | |
| + %(video_path("HTTP://www.outside.com/foo.ogg")) => %(HTTP://www.outside.com/foo.ogg) | |
| } | |
| PathToVideoToTag = { | |
| %(path_to_video("xml")) => %(/videos/xml), | |
| %(path_to_video("xml.ogg")) => %(/videos/xml.ogg), | |
| %(path_to_video("dir/xml.ogg")) => %(/videos/dir/xml.ogg), | |
| - %(path_to_video("/dir/xml.ogg")) => %(/dir/xml.ogg) | |
| + %(path_to_video("/dir/xml.ogg")) => %(/dir/xml.ogg), | |
| + %(path_to_video("http://www.outside.com/foo.ogg")) => %(http://www.outside.com/foo.ogg), | |
| + %(path_to_video("HTTP://www.outside.com/foo.ogg")) => %(HTTP://www.outside.com/foo.ogg) | |
| } | |
| VideoLinkToTag = { | |
| @@ -211,14 +227,18 @@ def teardown | |
| %(audio_path("xml")) => %(/audios/xml), | |
| %(audio_path("xml.wav")) => %(/audios/xml.wav), | |
| %(audio_path("dir/xml.wav")) => %(/audios/dir/xml.wav), | |
| - %(audio_path("/dir/xml.wav")) => %(/dir/xml.wav) | |
| + %(audio_path("/dir/xml.wav")) => %(/dir/xml.wav), | |
| + %(audio_path("http://www.outside.com/foo.wav")) => %(http://www.outside.com/foo.wav), | |
| + %(audio_path("HTTP://www.outside.com/foo.wav")) => %(HTTP://www.outside.com/foo.wav) | |
| } | |
| PathToAudioToTag = { | |
| %(path_to_audio("xml")) => %(/audios/xml), | |
| %(path_to_audio("xml.wav")) => %(/audios/xml.wav), | |
| %(path_to_audio("dir/xml.wav")) => %(/audios/dir/xml.wav), | |
| - %(path_to_audio("/dir/xml.wav")) => %(/dir/xml.wav) | |
| + %(path_to_audio("/dir/xml.wav")) => %(/dir/xml.wav), | |
| + %(path_to_audio("http://www.outside.com/foo.wav")) => %(http://www.outside.com/foo.wav), | |
| + %(path_to_audio("HTTP://www.outside.com/foo.wav")) => %(HTTP://www.outside.com/foo.wav) | |
| } | |
| AudioLinkToTag = { | |
| diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb | |
| index e4f84f8..ad46ff4 100644 | |
| --- a/actionpack/test/template/date_helper_test.rb | |
| +++ b/actionpack/test/template/date_helper_test.rb | |
| @@ -19,6 +19,8 @@ def to_param | |
| end | |
| def assert_distance_of_time_in_words(from, to=nil) | |
| + Fixnum.send(:private, :/) if RUBY_VERSION >= '1.9.3' # test we avoid Integer#/ (redefined by mathn) | |
| + | |
| to ||= from | |
| # 0..1 with include_seconds | |
| @@ -96,6 +98,8 @@ def assert_distance_of_time_in_words(from, to=nil) | |
| # test to < from | |
| assert_equal "about 4 hours", distance_of_time_in_words(from + 4.hours, to) | |
| assert_equal "less than 20 seconds", distance_of_time_in_words(from + 19.seconds, to, true) | |
| + ensure | |
| + Fixnum.send(:public, :/) if RUBY_VERSION >= '1.9.3' | |
| end | |
| def test_distance_in_words | |
| diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb | |
| index 7b35424..22af39a 100644 | |
| --- a/actionpack/test/template/form_helper_test.rb | |
| +++ b/actionpack/test/template/form_helper_test.rb | |
| @@ -301,6 +301,16 @@ def test_file_field_has_no_size | |
| assert_dom_equal expected, file_field("user", "avatar") | |
| end | |
| + def test_file_field_with_multiple_behavior | |
| + expected = '<input id="import_file" multiple="multiple" name="import[file][]" type="file" />' | |
| + assert_dom_equal expected, file_field("import", "file", :multiple => true) | |
| + end | |
| + | |
| + def test_file_field_with_multiple_behavior_and_explicit_name | |
| + expected = '<input id="import_file" multiple="multiple" name="custom" type="file" />' | |
| + assert_dom_equal expected, file_field("import", "file", :multiple => true, :name => "custom") | |
| + end | |
| + | |
| def test_hidden_field | |
| assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />', | |
| hidden_field("post", "title") | |
| diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb | |
| index 8d679aa..37ce3cf 100644 | |
| --- a/actionpack/test/template/number_helper_test.rb | |
| +++ b/actionpack/test/template/number_helper_test.rb | |
| @@ -251,6 +251,11 @@ def test_number_to_human_with_custom_units | |
| assert_equal '4.5 tens', number_to_human(45, :units => {:unit => "", :ten => ' tens '}) | |
| end | |
| + def test_number_to_human_with_custom_units_that_are_missing_the_needed_key | |
| + assert_equal '123', number_to_human(123, :units => {:thousand => 'k'}) | |
| + assert_equal '123', number_to_human(123, :units => {}) | |
| + end | |
| + | |
| def test_number_to_human_with_custom_format | |
| assert_equal '123 times Thousand', number_to_human(123456, :format => "%n times %u") | |
| volume = {:unit => "ml", :thousand => "lt", :million => "m3"} | |
| diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb | |
| index b907e32..03f3a34 100644 | |
| --- a/actionpack/test/template/render_test.rb | |
| +++ b/actionpack/test/template/render_test.rb | |
| @@ -61,6 +61,13 @@ def test_render_template_with_a_missing_partial_of_another_format | |
| end | |
| end | |
| + def test_render_using_context_format_as_default | |
| + @view.lookup_context.formats = [:html] | |
| + assert_equal "partial html", @view.render('test/partial') | |
| + assert_equal "partial js", @view.render(:partial => 'test/partial', :formats => :js) | |
| + assert_equal "partial html", @view.render('test/partial') | |
| + end | |
| + | |
| def test_render_file_with_locale | |
| assert_equal "<h1>Kein Kommentar</h1>", @view.render(:file => "comments/empty", :locale => [:de]) | |
| assert_equal "<h1>Kein Kommentar</h1>", @view.render(:file => "comments/empty", :locale => :de) | |
| diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb | |
| index 93832c7..1ad9bca 100644 | |
| --- a/actionpack/test/template/sprockets_helper_test.rb | |
| +++ b/actionpack/test/template/sprockets_helper_test.rb | |
| @@ -277,6 +277,15 @@ def compute_host(source, request, options = {}) | |
| assert_nothing_raised { javascript_include_tag('foo.min') } | |
| end | |
| + test "assets that exist on filesystem don't need to go through Sprockets" do | |
| + @config.assets.digest = false | |
| + @config.assets.debug = true | |
| + | |
| + Rails.application.assets.expects(:resolve).never | |
| + | |
| + asset_paths.asset_for(FIXTURES.join("sprockets/app/javascripts/foo.min.js"), 'min') | |
| + end | |
| + | |
| test "stylesheet path through asset_path" do | |
| assert_match %r{/assets/application-[0-9a-f]+.css}, asset_path(:application, :ext => "css") | |
| diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb | |
| index 38f7720..a00d71f 100644 | |
| --- a/actionpack/test/template/url_helper_test.rb | |
| +++ b/actionpack/test/template/url_helper_test.rb | |
| @@ -306,6 +306,11 @@ def test_link_to_unless | |
| link_to_unless(true, "Showing", url_hash) { | |
| "test" | |
| } | |
| + | |
| + assert_equal %{<b>Showing</b>}, link_to_unless(true, "<b>Showing</b>", url_hash) | |
| + assert_equal %{<a href="/"><b>Showing</b></a>}, link_to_unless(false, "<b>Showing</b>", url_hash) | |
| + assert_equal %{<b>Showing</b>}, link_to_unless(true, "<b>Showing</b>".html_safe, url_hash) | |
| + assert_equal %{<a href="/"><b>Showing</b></a>}, link_to_unless(false, "<b>Showing</b>".html_safe, url_hash) | |
| end | |
| def test_link_to_if | |
| diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md | |
| index 1464f52..7db0854 100644 | |
| --- a/activemodel/CHANGELOG.md | |
| +++ b/activemodel/CHANGELOG.md | |
| @@ -1,9 +1,13 @@ | |
| -## unreleased ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| * No changes. | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| +* No changes. | |
| + | |
| + | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| * Specify type of singular association during serialization *Steve Klabnik* | |
| diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec | |
| index 283c3ee..a4f0836 100644 | |
| --- a/activemodel/activemodel.gemspec | |
| +++ b/activemodel/activemodel.gemspec | |
| @@ -7,6 +7,8 @@ Gem::Specification.new do |s| | |
| s.summary = 'A toolkit for building modeling frameworks (part of Rails).' | |
| s.description = 'A toolkit for building modeling frameworks like Active Record and Active Resource. Rich support for attributes, callbacks, validations, observers, serialization, internationalization, and testing.' | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb | |
| index ec0efc7..16fe93d 100644 | |
| --- a/activemodel/lib/active_model/version.rb | |
| +++ b/activemodel/lib/active_model/version.rb | |
| @@ -2,7 +2,7 @@ module ActiveModel | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md | |
| index ec017f8..878e5ee 100644 | |
| --- a/activerecord/CHANGELOG.md | |
| +++ b/activerecord/CHANGELOG.md | |
| @@ -1,11 +1,188 @@ | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| + | |
| +* When calling the method .find_or_initialize_by_* from a collection_proxy | |
| + it should set the inverse_of relation even when the entry was found on the db. | |
| + | |
| + *arthurnn* | |
| + | |
| +* Callbacks on has_many should access the in memory parent if a inverse_of is set. | |
| + | |
| + *arthurnn* | |
| + | |
| +* Fix `FinderMethods#last` unscoped primary key. | |
| + | |
| + Fixes #11917. | |
| + | |
| + *Eugene Kalenkovich* | |
| + | |
| +* Load fixtures from linked folders. | |
| + | |
| + *Kassio Borges* | |
| + | |
| +* When using optimistic locking, `update` was not passing the column to `quote_value` | |
| + to allow the connection adapter to properly determine how to quote the value. This was | |
| + affecting certain databases that use specific colmn types. | |
| + | |
| + Fixes: #6763 | |
| + | |
| + *Alfred Wong* | |
| + | |
| + | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| + | |
| +* Fix merge error when Equality LHS is non-attribute. | |
| + Backport of #7380. | |
| + | |
| + *Karmes Alexander* | |
| + | |
| +* Do not re-create destroyed association when saving the parent object. | |
| + | |
| + Fixes #11450. | |
| + | |
| + *Paul Nikitochkin* | |
| + | |
| +* Do not shallow the original exception in `exec_cache` on PostgreSQL adapter. | |
| + | |
| + Fixes #11260. | |
| + | |
| + *Rafael Mendonça França* | |
| + | |
| +* Fix `ActiveRecord::Store` incorrectly tracking changes of its attributes. | |
| + Fixes #10373. | |
| + | |
| + *Janko Marohnić* | |
| + | |
| +* Fix a bug that prevented the use of the default STI inheritance column | |
| + (ActiveRecord::Base.inheritance_column = 'some_column'.) | |
| + | |
| + *chapmajs + Takehiro Adachi* | |
| + | |
| +* Fix mysql2 adapter raises the correct exception when executing a query on a | |
| + closed connection. | |
| + | |
| + *Yves Senn* | |
| + | |
| +* Fixes bug where `Company.new.contract_ids` would incorrectly load | |
| + all non-associated contracts. | |
| + | |
| + Example: | |
| + | |
| + company = Company.new # Company has many :contracts | |
| + | |
| + # before | |
| + company.contract_ids # => SELECT ... WHERE `contracts`.`company_id` IS NULL | |
| + | |
| + # after | |
| + company.contract_ids # => [] | |
| + | |
| + *Jared Armstrong* | |
| + | |
| +* Fix the `:primary_key` option for `has_many` associations. | |
| + Fixes #10693. | |
| + | |
| + *Yves Senn* | |
| + | |
| +* fixes bug introduced by #3329. Now, when autosaving associations, | |
| + deletions happen before inserts and saves. This prevents a 'duplicate | |
| + unique value' database error that would occur if a record being created had | |
| + the same value on a unique indexed field as that of a record being destroyed. | |
| + | |
| + Backport of #10417 | |
| + | |
| + *Johnny Holton* | |
| + | |
| +* Fix that under some conditions, Active Record could produce invalid SQL of the sort: | |
| + "SELECT DISTINCT DISTINCT". | |
| + | |
| + Backport of #6792. | |
| + | |
| + *Ben Woosley* | |
| + | |
| +* Require `ActiveRecord::Base` in railtie hooks for rake_tasks, console and runner to | |
| + avoid circular constant loading issues. | |
| + | |
| + Backport #7695. | |
| + | |
| + Fixes #7683 and #882 | |
| + | |
| + *Ben Holley* | |
| + | |
| +* Maintain context for joins within ActiveRecord::Relation merges. | |
| + Backport #10164. | |
| + | |
| + *Neeraj Singh + Andrew Horner* | |
| + | |
| +* Make sure the `EXPLAIN` command is never triggered by a `select_db` call. | |
| + | |
| + *Daniel Schierbeck* | |
| + | |
| +* Revert changes on `pluck` that was ignoring the select clause when the relation already | |
| + has one. This caused a regression since it changed the behavior in a stable release. | |
| + | |
| + Fixes #9777. | |
| + | |
| + *Rafael Mendonça França* | |
| + | |
| +* Confirm a record has not already been destroyed before decrementing counter cache. | |
| + | |
| + *Ben Tucker* | |
| + | |
| +* Default values for PostgreSQL bigint types now get parsed and dumped to the | |
| + schema correctly. | |
| + Backport #10098. | |
| + | |
| + *Erik Peterson* | |
| + | |
| +* Removed warning when `auto_explain_threshold_in_seconds` is set and the | |
| + connection adapter doesn't support explain. | |
| + This is causing a regression since the Active Record Railtie is trying to | |
| + connect to the development database in the application boot. | |
| + | |
| + *Rafael Mendonça França* | |
| + | |
| +* Do not reset `inheritance_column` when it's set explicitly. | |
| + Backport of #5327. | |
| + | |
| + *kennyj + Fred Wu* | |
| + | |
| +* Fix a problem wrong exception is occured | |
| + when raising no translatable exception in PostgreSQL. | |
| + | |
| + *kennyj* | |
| + | |
| +* Resets the postgres search path in the structure.sql after the structure | |
| + is dumped in order to find schema_migrations table when multiples schemas | |
| + are used. | |
| + Fixes #9796. | |
| + | |
| + *Juan M. Cuello + Dembskiy Alexander* | |
| + | |
| +* Reload the association target if it's stale. `@stale_state` should be nil | |
| + when a model isn't saved. | |
| + Fixes #7526. | |
| + | |
| + *Larry Lv* | |
| + | |
| +* Don't read CSV files during execution of `db:fixtures:load`. CSV support for | |
| + fixtures was removed some time ago but the task was still loading them, even | |
| + though later the code was looking for the related yaml file instead. | |
| + | |
| + *kennyj* | |
| + | |
| + | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| + | |
| +* Chaining multiple preloaded scopes will correctly preload all the scopes | |
| + at the same time. | |
| + | |
| + *Chris Geihsler* | |
| * Reverted 921a296a3390192a71abeec6d9a035cc6d1865c8, 'Quote numeric values | |
| compared to string columns.' This caused several regressions. | |
| *Steve Klabnik* | |
| -* Fix overriding of attributes by default_scope on `ActiveRecord::Base#dup`. | |
| +* Fix overriding of attributes by `default_scope` on `ActiveRecord::Base#dup`. | |
| *Hiroshige UMINO* | |
| @@ -223,6 +400,16 @@ | |
| *Victor Costan* | |
| +* `#pluck` can be used on a relation with `select` clause. | |
| + Fixes #7551. | |
| + Backport of #8176. | |
| + | |
| + Example: | |
| + | |
| + Topic.select([:approved, :id]).order(:id).pluck(:id) | |
| + | |
| + *Yves Senn* | |
| + | |
| * Use `nil?` instead of `blank?` to check whether dynamic finder with a bang | |
| should raise RecordNotFound. | |
| Fixes #7238. | |
| diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec | |
| index 384dcd8..7e93d8a 100644 | |
| --- a/activerecord/activerecord.gemspec | |
| +++ b/activerecord/activerecord.gemspec | |
| @@ -7,6 +7,8 @@ Gem::Specification.new do |s| | |
| s.summary = 'Object-relational mapper framework (part of Rails).' | |
| s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.' | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb | |
| index ab0d888..99f3079 100644 | |
| --- a/activerecord/lib/active_record/associations/association.rb | |
| +++ b/activerecord/lib/active_record/associations/association.rb | |
| @@ -46,6 +46,7 @@ def reset | |
| @loaded = false | |
| IdentityMap.remove(target) if IdentityMap.enabled? && target | |
| @target = nil | |
| + @stale_state = nil | |
| end | |
| # Reloads the \target and returns +self+ on success. | |
| @@ -128,16 +129,21 @@ def target_scope | |
| # This method is abstract in the sense that it relies on +find_target+, | |
| # which is expected to be provided by descendants. | |
| # | |
| - # If the \target is already \loaded it is just returned. Thus, you can call | |
| - # +load_target+ unconditionally to get the \target. | |
| + # If the \target is stale(the target no longer points to the record(s) that the | |
| + # relevant foreign_key(s) refers to.), force reload the \target. | |
| + # | |
| + # Otherwise if the \target is already \loaded it is just returned. Thus, you can | |
| + # call +load_target+ unconditionally to get the \target. | |
| # | |
| # ActiveRecord::RecordNotFound is rescued within the method, and it is | |
| # not reraised. The proxy is \reset and +nil+ is the return value. | |
| def load_target | |
| - if find_target? | |
| + if (@stale_state && stale_target?) || find_target? | |
| begin | |
| if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class) | |
| @target = IdentityMap.get(association_class, owner[reflection.foreign_key]) | |
| + elsif @stale_state && stale_target? | |
| + @target = find_target | |
| end | |
| rescue NameError | |
| nil | |
| diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb | |
| index 97f531d..52c67df 100644 | |
| --- a/activerecord/lib/active_record/associations/belongs_to_association.rb | |
| +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb | |
| @@ -72,7 +72,7 @@ def target_id | |
| end | |
| def stale_state | |
| - owner[reflection.foreign_key].to_s | |
| + owner[reflection.foreign_key] && owner[reflection.foreign_key].to_s | |
| end | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb | |
| index 2ee5dbb..88ce03a 100644 | |
| --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb | |
| +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb | |
| @@ -27,7 +27,8 @@ def raise_on_type_mismatch(record) | |
| end | |
| def stale_state | |
| - [super, owner[reflection.foreign_type].to_s] | |
| + foreign_key = super | |
| + foreign_key && [foreign_key.to_s, owner[reflection.foreign_type].to_s] | |
| end | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb | |
| index 1759a41..d787177 100644 | |
| --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb | |
| +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb | |
| @@ -34,7 +34,10 @@ def add_counter_cache_callbacks(reflection) | |
| method_name = "belongs_to_counter_cache_before_destroy_for_#{name}" | |
| mixin.redefine_method(method_name) do | |
| record = send(name) | |
| - record.class.decrement_counter(cache_column, record.id) unless record.nil? | |
| + | |
| + if record && !self.destroyed? | |
| + record.class.decrement_counter(cache_column, record.id) | |
| + end | |
| end | |
| model.before_destroy(method_name) | |
| diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb | |
| index 65e8828..baddb98 100644 | |
| --- a/activerecord/lib/active_record/associations/collection_association.rb | |
| +++ b/activerecord/lib/active_record/associations/collection_association.rb | |
| @@ -43,7 +43,7 @@ def writer(records) | |
| # Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items | |
| def ids_reader | |
| - if loaded? || options[:finder_sql] | |
| + if owner.new_record? || loaded? || options[:finder_sql] | |
| load_target.map do |record| | |
| record.send(reflection.association_primary_key) | |
| end | |
| diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb | |
| index 416a582..62cdb1e 100644 | |
| --- a/activerecord/lib/active_record/associations/collection_proxy.rb | |
| +++ b/activerecord/lib/active_record/associations/collection_proxy.rb | |
| @@ -77,10 +77,12 @@ def respond_to?(name, include_private = false) | |
| def method_missing(method, *args, &block) | |
| match = DynamicFinderMatch.match(method) | |
| if match && match.instantiator? | |
| - send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |r| | |
| - proxy_association.send :set_owner_attributes, r | |
| - proxy_association.send :add_to_target, r | |
| - yield(r) if block_given? | |
| + send(:find_or_instantiator_by_attributes, match, match.attribute_names, *args) do |record| | |
| + proxy_association.send :set_owner_attributes, record | |
| + proxy_association.send :add_to_target, record | |
| + yield(record) if block_given? | |
| + end.tap do |record| | |
| + proxy_association.send :set_inverse_instance, record | |
| end | |
| elsif target.respond_to?(method) || (!proxy_association.klass.respond_to?(method) && Class.respond_to?(method)) | |
| diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb | |
| index 059e6c7..290f814 100644 | |
| --- a/activerecord/lib/active_record/associations/has_many_association.rb | |
| +++ b/activerecord/lib/active_record/associations/has_many_association.rb | |
| @@ -9,6 +9,7 @@ class HasManyAssociation < CollectionAssociation #:nodoc: | |
| def insert_record(record, validate = true, raise = false) | |
| set_owner_attributes(record) | |
| + set_inverse_instance(record) | |
| if raise | |
| record.save!(:validate => validate) | |
| @@ -89,8 +90,7 @@ def delete_records(records, method) | |
| records.each { |r| r.destroy } | |
| update_counter(-records.length) unless inverse_updates_counter_cache? | |
| else | |
| - keys = records.map { |r| r[reflection.association_primary_key] } | |
| - scope = scoped.where(reflection.association_primary_key => keys) | |
| + scope = self.scoped.where(reflection.klass.primary_key => records) | |
| if method == :delete_all | |
| update_counter(-scope.delete_all) | |
| diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb | |
| index cd366ac..e3d8356 100644 | |
| --- a/activerecord/lib/active_record/associations/join_dependency.rb | |
| +++ b/activerecord/lib/active_record/associations/join_dependency.rb | |
| @@ -109,7 +109,7 @@ def build(associations, parent = nil, join_type = Arel::InnerJoin) | |
| case associations | |
| when Symbol, String | |
| reflection = parent.reflections[associations.to_s.intern] or | |
| - raise ConfigurationError, "Association named '#{ associations }' was not found; perhaps you misspelled it?" | |
| + raise ConfigurationError, "Association named '#{ associations }' was not found on #{parent.active_record.name}; perhaps you misspelled it?" | |
| unless join_association = find_join_association(reflection, parent) | |
| @reflections << reflection | |
| join_association = build_join_association(reflection, parent) | |
| diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb | |
| index 03963ab..becf1a3 100644 | |
| --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb | |
| +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb | |
| @@ -55,7 +55,12 @@ def ==(other) | |
| def find_parent_in(other_join_dependency) | |
| other_join_dependency.join_parts.detect do |join_part| | |
| - parent == join_part | |
| + case parent | |
| + when JoinBase | |
| + parent.active_record == join_part.active_record | |
| + else | |
| + parent == join_part | |
| + end | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb | |
| index 4cb7b56..e052b00 100644 | |
| --- a/activerecord/lib/active_record/associations/preloader/through_association.rb | |
| +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb | |
| @@ -56,8 +56,7 @@ def through_options | |
| through_options[:include] = options[:include] || options[:source] | |
| through_options[:conditions] = options[:conditions] | |
| end | |
| - | |
| - through_options[:order] = options[:order] | |
| + through_options[:order] = options[:order] if options.has_key?(:order) | |
| end | |
| through_options | |
| diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb | |
| index fd0e90a..be890e5 100644 | |
| --- a/activerecord/lib/active_record/associations/through_association.rb | |
| +++ b/activerecord/lib/active_record/associations/through_association.rb | |
| @@ -62,7 +62,7 @@ def construct_join_attributes(*records) | |
| # properly support stale-checking for nested associations. | |
| def stale_state | |
| if through_reflection.macro == :belongs_to | |
| - owner[through_reflection.foreign_key].to_s | |
| + owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb | |
| index e1499fc..4da3929 100644 | |
| --- a/activerecord/lib/active_record/autosave_association.rb | |
| +++ b/activerecord/lib/active_record/autosave_association.rb | |
| @@ -332,16 +332,18 @@ def save_collection_association(reflection) | |
| if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave) | |
| begin | |
| - records_to_destroy = [] | |
| + if autosave | |
| + records_to_destroy = records.select(&:marked_for_destruction?) | |
| + records_to_destroy.each { |record| association.proxy.destroy(record) } | |
| + records -= records_to_destroy | |
| + end | |
| records.each do |record| | |
| next if record.destroyed? | |
| saved = true | |
| - if autosave && record.marked_for_destruction? | |
| - records_to_destroy << record | |
| - elsif autosave != false && (@new_record_before_save || record.new_record?) | |
| + if autosave != false && (@new_record_before_save || record.new_record?) | |
| if autosave | |
| saved = association.insert_record(record, false) | |
| else | |
| @@ -353,19 +355,14 @@ def save_collection_association(reflection) | |
| raise ActiveRecord::Rollback unless saved | |
| end | |
| - | |
| - records_to_destroy.each do |record| | |
| - association.proxy.destroy(record) | |
| - end | |
| rescue | |
| records.each {|x| IdentityMap.remove(x) } if IdentityMap.enabled? | |
| raise | |
| end | |
| - | |
| end | |
| # reconstruct the scope now that we know the owner's id | |
| - association.send(:reset_scope) if association.respond_to?(:reset_scope) | |
| + association.reset_scope if association.respond_to?(:reset_scope) | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb | |
| index ddb6896..6a5cff6 100644 | |
| --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb | |
| +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb | |
| @@ -42,8 +42,8 @@ def add_column_options!(sql, options) | |
| # Represents the schema of an SQL table in an abstract way. This class | |
| # provides methods for manipulating the schema representation. | |
| # | |
| - # Inside migration files, the +t+ object in +create_table+ and | |
| - # +change_table+ is actually of this type: | |
| + # Inside migration files, the +t+ object in +create_table+ | |
| + # is actually of this type: | |
| # | |
| # class SomeMigration < ActiveRecord::Migration | |
| # def up | |
| diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb | |
| index 524a7d3..c690b98 100644 | |
| --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb | |
| +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb | |
| @@ -204,9 +204,11 @@ def select_rows(sql, name = nil) | |
| # Executes the SQL statement in the context of this connection. | |
| def execute(sql, name = nil) | |
| - # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been | |
| - # made since we established the connection | |
| - @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone | |
| + if @connection | |
| + # make sure we carry over any changes to ActiveRecord::Base.default_timezone that have been | |
| + # made since we established the connection | |
| + @connection.query_options[:database_timezone] = ActiveRecord::Base.default_timezone | |
| + end | |
| super | |
| end | |
| diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb | |
| index 8806693..e98337e 100644 | |
| --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb | |
| +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb | |
| @@ -142,7 +142,7 @@ def self.extract_value_from_default(default) | |
| when NilClass | |
| nil | |
| # Numeric types | |
| - when /\A\(?(-?\d+(\.\d*)?\)?)\z/ | |
| + when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/ | |
| $1 | |
| # Character types | |
| when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m | |
| @@ -1144,7 +1144,9 @@ def postgresql_version | |
| UNIQUE_VIOLATION = "23505" | |
| def translate_exception(exception, message) | |
| - case exception.result.error_field(PGresult::PG_DIAG_SQLSTATE) | |
| + return exception unless exception.respond_to?(:result) | |
| + | |
| + case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE) | |
| when UNIQUE_VIOLATION | |
| RecordNotUnique.new(message, exception) | |
| when FOREIGN_KEY_VIOLATION | |
| @@ -1177,7 +1179,11 @@ def exec_cache(sql, binds) | |
| # prepared statements whose return value may have changed is | |
| # FEATURE_NOT_SUPPORTED. Check here for more details: | |
| # http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573 | |
| - code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE) | |
| + begin | |
| + code = e.result.result_error_field(PGresult::PG_DIAG_SQLSTATE) | |
| + rescue | |
| + raise e | |
| + end | |
| if FEATURE_NOT_SUPPORTED == code | |
| @statements.delete sql_key(sql) | |
| retry | |
| diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb | |
| index bc8d24a..9fc218d 100644 | |
| --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb | |
| +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb | |
| @@ -9,7 +9,7 @@ def initialize(conn) | |
| @tables = {} | |
| @columns = Hash.new do |h, table_name| | |
| - h[table_name] = conn.columns(table_name, "#{table_name} Columns") | |
| + h[table_name] = connection.columns(table_name, "#{table_name} Columns") | |
| end | |
| @columns_hash = Hash.new do |h, table_name| | |
| @@ -19,7 +19,7 @@ def initialize(conn) | |
| end | |
| @primary_keys = Hash.new do |h, table_name| | |
| - h[table_name] = table_exists?(table_name) ? conn.primary_key(table_name) : nil | |
| + h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb | |
| index 859c8ed..1d861a5 100644 | |
| --- a/activerecord/lib/active_record/explain_subscriber.rb | |
| +++ b/activerecord/lib/active_record/explain_subscriber.rb | |
| @@ -15,7 +15,7 @@ def call(*args) | |
| # On the other hand, we want to monitor the performance of our real database | |
| # queries, not the performance of the access to the query cache. | |
| IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE) | |
| - EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)/i | |
| + EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i | |
| def ignore_payload?(payload) | |
| payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS | |
| end | |
| diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb | |
| index 7a5634f..1e6b5c5 100644 | |
| --- a/activerecord/lib/active_record/fixtures.rb | |
| +++ b/activerecord/lib/active_record/fixtures.rb | |
| @@ -661,7 +661,7 @@ def column_names | |
| end | |
| def read_fixture_files | |
| - yaml_files = Dir["#{@fixture_path}/**/*.yml"].select { |f| | |
| + yaml_files = Dir["#{@fixture_path}/{**,*}/*.yml"].select { |f| | |
| ::File.file?(f) | |
| } + [yaml_file_path] | |
| @@ -752,7 +752,7 @@ def set_fixture_class(class_names = {}) | |
| def fixtures(*fixture_names) | |
| if fixture_names.first == :all | |
| - fixture_names = Dir["#{fixture_path}/**/*.{yml}"] | |
| + fixture_names = Dir["#{fixture_path}/{**,*}/*.{yml}"] | |
| fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] } | |
| else | |
| fixture_names = fixture_names.flatten.map { |n| n.to_s } | |
| diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb | |
| index b288199..6265be8 100644 | |
| --- a/activerecord/lib/active_record/locking/optimistic.rb | |
| +++ b/activerecord/lib/active_record/locking/optimistic.rb | |
| @@ -80,7 +80,7 @@ def update(attribute_names = @attributes.keys) #:nodoc: | |
| stmt = relation.where( | |
| relation.table[self.class.primary_key].eq(id).and( | |
| - relation.table[lock_col].eq(quote_value(previous_lock_value)) | |
| + relation.table[lock_col].eq(quote_value(previous_lock_value, self.class.columns_hash[lock_col])) | |
| ) | |
| ).arel.compile_update(arel_attributes_values(false, false, attribute_names)) | |
| diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb | |
| index 1517e5e..40cc8dc 100644 | |
| --- a/activerecord/lib/active_record/model_schema.rb | |
| +++ b/activerecord/lib/active_record/model_schema.rb | |
| @@ -159,7 +159,7 @@ def full_table_name_prefix #:nodoc: | |
| # The name of the column containing the object's class when Single Table Inheritance is used | |
| def inheritance_column | |
| if self == Base | |
| - 'type' | |
| + (@inheritance_column ||= nil) || 'type' | |
| else | |
| (@inheritance_column ||= nil) || superclass.inheritance_column | |
| end | |
| @@ -173,6 +173,7 @@ def original_inheritance_column #:nodoc: | |
| def inheritance_column=(value) | |
| @original_inheritance_column = inheritance_column | |
| @inheritance_column = value.to_s | |
| + @explicit_inheritance_column = true | |
| end | |
| def set_inheritance_column(value = nil, &block) #:nodoc: | |
| @@ -300,7 +301,8 @@ def reset_column_information | |
| connection.schema_cache.clear_table_cache!(table_name) if table_exists? | |
| @column_names = @content_columns = @column_defaults = @columns = @columns_hash = nil | |
| - @dynamic_methods_hash = @inheritance_column = nil | |
| + @dynamic_methods_hash = nil | |
| + @inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column | |
| @arel_engine = @relation = nil | |
| end | |
| diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb | |
| index 0509165..41b62f6 100644 | |
| --- a/activerecord/lib/active_record/nested_attributes.rb | |
| +++ b/activerecord/lib/active_record/nested_attributes.rb | |
| @@ -92,8 +92,9 @@ class TooManyRecords < ActiveRecordError | |
| # accepts_nested_attributes_for :posts | |
| # end | |
| # | |
| - # You can now set or update attributes on an associated post model through | |
| - # the attribute hash. | |
| + # You can now set or update attributes on the associated posts through | |
| + # an attribute hash for a member: include the key +:posts_attributes+ | |
| + # with an array of hashes of post attributes as a value. | |
| # | |
| # For each hash that does _not_ have an <tt>id</tt> key a new record will | |
| # be instantiated, unless the hash also contains a <tt>_destroy</tt> key | |
| @@ -116,10 +117,10 @@ class TooManyRecords < ActiveRecordError | |
| # hashes if they fail to pass your criteria. For example, the previous | |
| # example could be rewritten as: | |
| # | |
| - # class Member < ActiveRecord::Base | |
| - # has_many :posts | |
| - # accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? } | |
| - # end | |
| + # class Member < ActiveRecord::Base | |
| + # has_many :posts | |
| + # accepts_nested_attributes_for :posts, :reject_if => proc { |attributes| attributes['title'].blank? } | |
| + # end | |
| # | |
| # params = { :member => { | |
| # :name => 'joe', :posts_attributes => [ | |
| @@ -136,19 +137,19 @@ class TooManyRecords < ActiveRecordError | |
| # | |
| # Alternatively, :reject_if also accepts a symbol for using methods: | |
| # | |
| - # class Member < ActiveRecord::Base | |
| - # has_many :posts | |
| - # accepts_nested_attributes_for :posts, :reject_if => :new_record? | |
| - # end | |
| + # class Member < ActiveRecord::Base | |
| + # has_many :posts | |
| + # accepts_nested_attributes_for :posts, :reject_if => :new_record? | |
| + # end | |
| # | |
| - # class Member < ActiveRecord::Base | |
| - # has_many :posts | |
| - # accepts_nested_attributes_for :posts, :reject_if => :reject_posts | |
| + # class Member < ActiveRecord::Base | |
| + # has_many :posts | |
| + # accepts_nested_attributes_for :posts, :reject_if => :reject_posts | |
| # | |
| - # def reject_posts(attributed) | |
| - # attributed['title'].blank? | |
| - # end | |
| - # end | |
| + # def reject_posts(attributed) | |
| + # attributed['title'].blank? | |
| + # end | |
| + # end | |
| # | |
| # If the hash contains an <tt>id</tt> key that matches an already | |
| # associated record, the matching record will be modified: | |
| @@ -185,6 +186,29 @@ class TooManyRecords < ActiveRecordError | |
| # member.save | |
| # member.reload.posts.length # => 1 | |
| # | |
| + # Nested attributes for an associated collection can also be passed in | |
| + # the form of a hash of hashes instead of an array of hashes: | |
| + # | |
| + # Member.create(:name => 'joe', | |
| + # :posts_attributes => { :first => { :title => 'Foo' }, | |
| + # :second => { :title => 'Bar' } }) | |
| + # | |
| + # has the same effect as | |
| + # | |
| + # Member.create(:name => 'joe', | |
| + # :posts_attributes => [ { :title => 'Foo' }, | |
| + # { :title => 'Bar' } ]) | |
| + # | |
| + # The keys of the hash which is the value for +:posts_attributes+ are | |
| + # ignored in this case. | |
| + # However, it is not allowed to use +'id'+ or +:id+ for one of | |
| + # such keys, otherwise the hash will be wrapped in an array and | |
| + # interpreted as an attribute hash for a single post. | |
| + # | |
| + # Passing attributes for an associated collection in the form of a hash | |
| + # of hashes can be used with hashes generated from HTTP/HTML parameters, | |
| + # where there maybe no natural way to submit an array of hashes. | |
| + # | |
| # === Saving | |
| # | |
| # All changes to models, including the destruction of those marked for | |
| diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb | |
| index 055d27d..4e39654 100644 | |
| --- a/activerecord/lib/active_record/railtie.rb | |
| +++ b/activerecord/lib/active_record/railtie.rb | |
| @@ -30,6 +30,7 @@ class Railtie < Rails::Railtie | |
| ) | |
| rake_tasks do | |
| + require "active_record/base" | |
| load "active_record/railties/databases.rake" | |
| end | |
| @@ -38,9 +39,14 @@ class Railtie < Rails::Railtie | |
| # first time. Also, make it output to STDERR. | |
| console do |app| | |
| require "active_record/railties/console_sandbox" if app.sandbox? | |
| + require "active_record/base" | |
| ActiveRecord::Base.logger = Logger.new(STDERR) | |
| end | |
| + runner do |app| | |
| + require "active_record/base" | |
| + end | |
| + | |
| initializer "active_record.initialize_timezone" do | |
| ActiveSupport.on_load(:active_record) do | |
| self.time_zone_aware_attributes = true | |
| @@ -83,13 +89,6 @@ class Railtie < Rails::Railtie | |
| end | |
| end | |
| - initializer "active_record.validate_explain_support" do |app| | |
| - if app.config.active_record[:auto_explain_threshold_in_seconds] && | |
| - !ActiveRecord::Base.connection.supports_explain? | |
| - warn "auto_explain_threshold_in_seconds is set but will be ignored because your adapter does not support this feature. Please unset the configuration to avoid this warning." | |
| - end | |
| - end | |
| - | |
| # Expose database runtime to controller for logging. | |
| initializer "active_record.log_runtime" do |app| | |
| require "active_record/railties/controller_runtime" | |
| diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake | |
| index 8f7c7f6..b8aed49 100644 | |
| --- a/activerecord/lib/active_record/railties/databases.rake | |
| +++ b/activerecord/lib/active_record/railties/databases.rake | |
| @@ -355,7 +355,7 @@ db_namespace = namespace :db do | |
| base_dir = File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten | |
| fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact | |
| - (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.{yml,csv}"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| | |
| + (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file| | |
| ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file) | |
| end | |
| end | |
| @@ -427,6 +427,7 @@ db_namespace = namespace :db do | |
| end | |
| `pg_dump -i -s -x -O -f #{Shellwords.escape(filename)} #{search_path} #{Shellwords.escape(config['database'])}` | |
| raise 'Error dumping database' if $?.exitstatus == 1 | |
| + File.open(filename, "a") { |f| f << "SET search_path TO #{ActiveRecord::Base.connection.schema_search_path};\n\n" } | |
| when /sqlite/ | |
| dbfile = config['database'] | |
| `sqlite3 #{dbfile} .schema > #{filename}` | |
| diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb | |
| index 1f9dbdc..afab793 100644 | |
| --- a/activerecord/lib/active_record/relation/calculations.rb | |
| +++ b/activerecord/lib/active_record/relation/calculations.rb | |
| @@ -179,14 +179,13 @@ def calculate(operation, column_name, options = {}) | |
| def pluck(column_name) | |
| if column_name.is_a?(Symbol) && column_names.include?(column_name.to_s) | |
| column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}" | |
| - else | |
| - column_name = column_name.to_s | |
| end | |
| - relation = clone | |
| - relation.select_values = [column_name] | |
| - klass.connection.select_all(relation.arel).map! do |attributes| | |
| - klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes)) | |
| + result = klass.connection.exec_query(select(column_name).to_sql) | |
| + last_column = result.columns.last | |
| + | |
| + result.map do |attributes| | |
| + klass.type_cast_attribute(last_column, klass.initialize_attributes(attributes)) | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb | |
| index 08cfe4f..1efe4db 100644 | |
| --- a/activerecord/lib/active_record/relation/finder_methods.rb | |
| +++ b/activerecord/lib/active_record/relation/finder_methods.rb | |
| @@ -134,8 +134,8 @@ def first! | |
| def last(*args) | |
| if args.any? | |
| if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) | |
| - if order_values.empty? | |
| - order("#{primary_key} DESC").limit(*args).reverse | |
| + if order_values.empty? && primary_key | |
| + order("#{quoted_table_name}.#{quoted_primary_key} DESC").limit(*args).reverse | |
| else | |
| to_a.last(*args) | |
| end | |
| @@ -254,6 +254,7 @@ def construct_limited_ids_condition(relation) | |
| values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders) | |
| relation = relation.dup.select(values) | |
| + relation.uniq_value = nil | |
| id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values) | |
| ids_array = id_rows.map {|row| row[primary_key]} | |
| diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb | |
| index c25570d..93fa30e 100644 | |
| --- a/activerecord/lib/active_record/relation/spawn_methods.rb | |
| +++ b/activerecord/lib/active_record/relation/spawn_methods.rb | |
| @@ -17,26 +17,27 @@ def merge(r) | |
| if method == :includes | |
| merged_relation = merged_relation.includes(value) | |
| else | |
| - merged_relation.send(:"#{method}_values=", value) | |
| + merge_relation_method(merged_relation, method, value) | |
| end | |
| end | |
| end | |
| (Relation::MULTI_VALUE_METHODS - [:joins, :where, :order]).each do |method| | |
| value = r.send(:"#{method}_values") | |
| - merged_relation.send(:"#{method}_values=", merged_relation.send(:"#{method}_values") + value) if value.present? | |
| + merge_relation_method(merged_relation, method, value) if value.present? | |
| end | |
| - merged_relation.joins_values += r.joins_values | |
| + merge_joins(merged_relation, r) | |
| merged_wheres = @where_values + r.where_values | |
| unless @where_values.empty? | |
| - # Remove duplicates, last one wins. | |
| + # Remove duplicate ARel attributes. Last one wins. | |
| seen = Hash.new { |h,table| h[table] = {} } | |
| merged_wheres = merged_wheres.reverse.reject { |w| | |
| nuke = false | |
| - if w.respond_to?(:operator) && w.operator == :== | |
| + if w.respond_to?(:operator) && w.operator == :== && | |
| + w.left.respond_to?(:relation) | |
| name = w.left.name | |
| table = w.left.relation.name | |
| nuke = seen[table][name] | |
| @@ -144,5 +145,36 @@ def apply_finder_options(options) | |
| relation | |
| end | |
| + private | |
| + | |
| + def merge_joins(relation, other) | |
| + values = other.joins_values | |
| + return if values.blank? | |
| + | |
| + if other.klass >= relation.klass | |
| + relation.joins_values += values | |
| + else | |
| + joins_dependency, rest = values.partition do |join| | |
| + case join | |
| + when Hash, Symbol, Array | |
| + true | |
| + else | |
| + false | |
| + end | |
| + end | |
| + | |
| + join_dependency = ActiveRecord::Associations::JoinDependency.new( | |
| + other.klass, | |
| + joins_dependency, | |
| + [] | |
| + ) | |
| + | |
| + relation.joins_values += join_dependency.join_associations + rest | |
| + end | |
| + end | |
| + | |
| + def merge_relation_method(relation, method, value) | |
| + relation.send(:"#{method}_values=", relation.send(:"#{method}_values") + value) | |
| + end | |
| end | |
| end | |
| diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb | |
| index 49d01de..bacb78e 100644 | |
| --- a/activerecord/lib/active_record/store.rb | |
| +++ b/activerecord/lib/active_record/store.rb | |
| @@ -37,8 +37,8 @@ def store_accessor(store_attribute, *keys) | |
| Array(keys).flatten.each do |key| | |
| define_method("#{key}=") do |value| | |
| send("#{store_attribute}=", {}) unless send(store_attribute).is_a?(Hash) | |
| - send(store_attribute)[key] = value | |
| send("#{store_attribute}_will_change!") | |
| + send(store_attribute)[key] = value | |
| end | |
| define_method(key) do | |
| diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb | |
| index 9069d99..c3ca763 100644 | |
| --- a/activerecord/lib/active_record/version.rb | |
| +++ b/activerecord/lib/active_record/version.rb | |
| @@ -2,7 +2,7 @@ module ActiveRecord | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb | |
| index 9227d3a..cfe8995 100644 | |
| --- a/activerecord/test/cases/adapters/mysql/connection_test.rb | |
| +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb | |
| @@ -16,6 +16,9 @@ def test_mysql_reconnect_attribute_after_connection_with_reconnect_true | |
| def test_connect_with_url | |
| run_without_connection do |orig| | |
| ar_config = ARTest.connection_config['arunit'] | |
| + | |
| + return skip "This test doesn't work with custom socket location" if ar_config['socket'] | |
| + | |
| url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}" | |
| klass = Class.new(ActiveRecord::Base) | |
| klass.establish_connection(url) | |
| diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb | |
| new file mode 100644 | |
| index 0000000..5ed2d8a | |
| --- /dev/null | |
| +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb | |
| @@ -0,0 +1,46 @@ | |
| +# encoding: utf-8 | |
| + | |
| +require "cases/helper" | |
| +require 'active_record/base' | |
| +require 'active_record/connection_adapters/postgresql_adapter' | |
| + | |
| +class PostgresqlByteaTest < ActiveRecord::TestCase | |
| + class ByteaDataType < ActiveRecord::Base | |
| + self.table_name = 'bytea_data_type' | |
| + end | |
| + | |
| + def setup | |
| + @connection = ActiveRecord::Base.connection | |
| + begin | |
| + @connection.transaction do | |
| + @connection.create_table('bytea_data_type') do |t| | |
| + t.binary 'payload' | |
| + t.binary 'serialized' | |
| + end | |
| + end | |
| + end | |
| + @column = ByteaDataType.columns.find { |c| c.name == 'payload' } | |
| + assert(@column.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn)) | |
| + end | |
| + | |
| + def teardown | |
| + @connection.execute 'drop table if exists bytea_data_type' | |
| + end | |
| + | |
| + class Serializer | |
| + def load(str); str; end | |
| + def dump(str); str; end | |
| + end | |
| + | |
| + def test_serialize | |
| + serializer = Serializer.new | |
| + klass = Class.new(ByteaDataType) { | |
| + serialize :serialized, Serializer.new | |
| + } | |
| + obj = klass.new | |
| + obj.serialized = "hello world" | |
| + obj.save! | |
| + obj.reload | |
| + assert_equal "hello world", obj.serialized | |
| + end | |
| +end | |
| diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb | |
| index 0de3786..6c345cd 100644 | |
| --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb | |
| +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb | |
| @@ -188,6 +188,12 @@ def test_distinct_with_nulls | |
| assert_equal "DISTINCT posts.title, posts.updater_id AS alias_0", @connection.distinct("posts.title", ["posts.updater_id desc nulls last"]) | |
| end | |
| + def test_raise_error_when_cannot_translate_exception | |
| + assert_raise TypeError do | |
| + @connection.send(:log, nil) { @connection.execute(nil) } | |
| + end | |
| + end | |
| + | |
| private | |
| def insert(ctx, data) | |
| binds = data.map { |name, value| | |
| diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb | |
| index ee338a3..6556219 100644 | |
| --- a/activerecord/test/cases/ar_schema_test.rb | |
| +++ b/activerecord/test/cases/ar_schema_test.rb | |
| @@ -11,8 +11,8 @@ def setup | |
| def teardown | |
| @connection.drop_table :fruits rescue nil | |
| - @connection.drop_table :"_pre_fruits_suf_" rescue nil | |
| - @connection.drop_table :"_pre_schema_migrations_suf_" rescue nil | |
| + @connection.drop_table :"_p_fruits_s_" rescue nil | |
| + @connection.drop_table :"_p_schema_migrations_s_" rescue nil | |
| end | |
| def test_schema_define | |
| @@ -24,8 +24,9 @@ def test_schema_define | |
| end | |
| def test_schema_define_with_table_prefix_and_suffix | |
| - ActiveRecord::Base.table_name_prefix = '_pre_' | |
| - ActiveRecord::Base.table_name_suffix = '_suf_' | |
| + # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters | |
| + ActiveRecord::Base.table_name_prefix = '_p_' | |
| + ActiveRecord::Base.table_name_suffix = '_s_' | |
| perform_schema_define! | |
| diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb | |
| index f392366..58c788e 100644 | |
| --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb | |
| +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb | |
| @@ -14,6 +14,8 @@ | |
| require 'models/member' | |
| require 'models/essay' | |
| require 'models/toy' | |
| +require 'models/person' | |
| +require 'models/reader' | |
| class BelongsToAssociationsTest < ActiveRecord::TestCase | |
| fixtures :accounts, :companies, :developers, :projects, :topics, | |
| @@ -385,6 +387,26 @@ def test_counter_cache | |
| assert_equal 15, topic.replies.size | |
| end | |
| + def test_counter_cache_double_destroy | |
| + topic = Topic.create :title => "Zoom-zoom-zoom" | |
| + | |
| + 5.times do | |
| + topic.replies.create(:title => "re: zoom", :content => "speedy quick!") | |
| + end | |
| + | |
| + assert_equal 5, topic.reload[:replies_count] | |
| + assert_equal 5, topic.replies.size | |
| + | |
| + reply = topic.replies.first | |
| + | |
| + reply.destroy | |
| + assert_equal 4, topic.reload[:replies_count] | |
| + | |
| + reply.destroy | |
| + assert_equal 4, topic.reload[:replies_count] | |
| + assert_equal 4, topic.replies.size | |
| + end | |
| + | |
| def test_custom_counter_cache | |
| reply = Reply.create(:title => "re: zoom", :content => "speedy quick!") | |
| assert_equal 0, reply[:replies_count] | |
| @@ -716,4 +738,16 @@ def test_polymorphic_with_custom_primary_key | |
| assert_equal toy, sponsor.reload.sponsorable | |
| end | |
| + | |
| + def test_saving_nested_association | |
| + post1, post2 = Post.limit(2) | |
| + person = Person.new(:first_name => 'foo') | |
| + reader = Reader.new(:post => post1) | |
| + | |
| + reader.post_id = post2.id | |
| + person.readers = [reader] | |
| + | |
| + assert person.save | |
| + assert_equal reader.post_id, post2.id | |
| + end | |
| end | |
| diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb | |
| index 944f135..42061d3 100644 | |
| --- a/activerecord/test/cases/associations/eager_test.rb | |
| +++ b/activerecord/test/cases/associations/eager_test.rb | |
| @@ -1118,4 +1118,11 @@ def test_join_eager_with_nil_order_should_generate_valid_sql | |
| assert_no_queries { assert_equal 2, author.comments_with_order_and_conditions.size } | |
| assert_no_queries { assert_equal 5, author.posts.size, "should not cache a subset of the association" } | |
| end | |
| + | |
| + test "preloading a through association twice does not reset it" do | |
| + members = Member.includes(:current_membership => :club).includes(:club).to_a | |
| + assert_no_queries { | |
| + assert_equal 3, members.map(&:current_membership).map(&:club).size | |
| + } | |
| + end | |
| end | |
| diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb | |
| index 869ec1e..53cce33 100644 | |
| --- a/activerecord/test/cases/associations/has_many_associations_test.rb | |
| +++ b/activerecord/test/cases/associations/has_many_associations_test.rb | |
| @@ -653,6 +653,26 @@ def test_transactions_when_adding_to_new_record | |
| end | |
| end | |
| + def test_inverse_on_before_validate | |
| + firm = companies(:first_firm) | |
| + assert_queries(1) do | |
| + client = Client.new("name" => "Natural Company") | |
| + client.touch_firm_on_validate = true | |
| + firm.clients_of_firm << client | |
| + end | |
| + end | |
| + | |
| + def test_inverse_after_find_or_initialize | |
| + firm = companies(:first_firm) | |
| + client = firm.clients_of_firm.find_or_initialize_by_client_of(firm.id) | |
| + assert_no_queries do | |
| + assert_equal firm, client.firm | |
| + end | |
| + | |
| + firm.name = "A new firm" | |
| + assert_equal firm.name, client.firm.name | |
| + end | |
| + | |
| def test_new_aliased_to_build | |
| company = companies(:first_firm) | |
| new_client = assert_no_queries { company.clients_of_firm.new("name" => "Another Client") } | |
| @@ -1311,6 +1331,33 @@ def test_get_ids_for_unloaded_associations_does_not_load_them | |
| assert !company.clients.loaded? | |
| end | |
| + def test_get_ids_for_association_on_new_record_does_not_try_to_find_records | |
| + Company.columns # Load schema information so we don't query below | |
| + Contract.columns # if running just this test. | |
| + | |
| + company = Company.new | |
| + assert_queries(0) do | |
| + company.contract_ids | |
| + end | |
| + | |
| + assert_equal [], company.contract_ids | |
| + end | |
| + | |
| + def test_set_ids_for_association_on_new_record_applies_association_correctly | |
| + contract_a = Contract.create! | |
| + contract_b = Contract.create! | |
| + Contract.create! # another contract | |
| + company = Company.new(:name => "Some Company") | |
| + | |
| + company.contract_ids = [contract_a.id, contract_b.id] | |
| + assert_equal [contract_a.id, contract_b.id], company.contract_ids | |
| + assert_equal [contract_a, contract_b], company.contracts | |
| + | |
| + company.save! | |
| + assert_equal company, contract_a.reload.company | |
| + assert_equal company, contract_b.reload.company | |
| + end | |
| + | |
| def test_get_ids_ignores_include_option | |
| assert_equal [readers(:michael_welcome).id], posts(:welcome).readers_with_person_ids | |
| end | |
| @@ -1492,6 +1539,14 @@ def test_has_many_custom_primary_key | |
| assert_equal david.essays, Essay.find_all_by_writer_id("David") | |
| end | |
| + def test_has_many_assignment_with_custom_primary_key | |
| + david = people(:david) | |
| + | |
| + assert_equal ["A Modest Proposal"], david.essays.map(&:name) | |
| + david.essays = [Essay.create!(:name => "Remote Work" )] | |
| + assert_equal ["Remote Work"], david.essays.map(&:name) | |
| + end | |
| + | |
| def test_blank_custom_primary_key_on_new_record_should_not_run_queries | |
| author = Author.new | |
| assert !author.essays.loaded? | |
| diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb | |
| index e6b8813..cb68f96 100644 | |
| --- a/activerecord/test/cases/autosave_association_test.rb | |
| +++ b/activerecord/test/cases/autosave_association_test.rb | |
| @@ -341,6 +341,19 @@ def test_store_association_with_a_polymorphic_relationship | |
| assert_equal num_tagging + 1, Tagging.count | |
| end | |
| + def test_association_is_not_overwitten_on_autosave | |
| + firm_1 = Firm.create!(:name => 'Apple') | |
| + firm_2 = Firm.create!(:name => 'Microsoft') | |
| + client = Client.create!(:firm => firm_1, :name => 'Business') | |
| + assert_equal firm_1.id, client.client_of | |
| + | |
| + client.client_of = firm_2.id | |
| + assert client.save | |
| + | |
| + client.reload | |
| + assert_equal firm_2, client.firm | |
| + end | |
| + | |
| def test_build_and_then_save_parent_should_not_reload_target | |
| client = Client.find(:first) | |
| apple = client.build_firm(:name => "Apple") | |
| @@ -583,7 +596,7 @@ def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship | |
| end | |
| class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase | |
| - self.use_transactional_fixtures = false unless supports_savepoints? | |
| + self.use_transactional_fixtures = false | |
| def setup | |
| @pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?") | |
| @@ -721,6 +734,13 @@ def test_should_destroy_has_many_as_part_of_the_save_transaction_if_they_were_ma | |
| ids.each { |id| assert_nil klass.find_by_id(id) } | |
| end | |
| + def test_should_not_resave_destroyed_association | |
| + @pirate.birds.create!(:name => :parrot) | |
| + @pirate.birds.first.destroy | |
| + @pirate.save! | |
| + assert @pirate.reload.birds.empty? | |
| + end | |
| + | |
| def test_should_skip_validation_on_has_many_if_marked_for_destruction | |
| 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") } | |
| @@ -780,6 +800,20 @@ def test_when_new_record_a_child_marked_for_destruction_should_not_affect_other_ | |
| assert_equal 2, @pirate.birds.reload.length | |
| end | |
| + def test_should_save_new_record_that_has_same_value_as_existing_record_marked_for_destruction_on_field_that_has_unique_index | |
| + Bird.connection.add_index :birds, :name, :unique => true | |
| + | |
| + 3.times { |i| @pirate.birds.create(:name => "unique_birds_#{i}") } | |
| + | |
| + @pirate.birds[0].mark_for_destruction | |
| + @pirate.birds.build(:name => @pirate.birds[0].name) | |
| + @pirate.save! | |
| + | |
| + assert_equal 3, @pirate.birds.reload.length | |
| + ensure | |
| + Bird.connection.remove_index :birds, :column => :name | |
| + end | |
| + | |
| # Add and remove callbacks tests for association collections. | |
| %w{ method proc }.each do |callback_type| | |
| define_method("test_should_run_add_callback_#{callback_type}s_for_has_many") do | |
| @@ -862,8 +896,10 @@ def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_savi | |
| @pirate.parrots.each { |parrot| parrot.mark_for_destruction } | |
| assert @pirate.save | |
| - assert_queries(0) do | |
| - assert @pirate.save | |
| + Pirate.transaction do | |
| + assert_queries(0) do | |
| + assert @pirate.save | |
| + end | |
| end | |
| end | |
| diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb | |
| index 97d6c0c..67b5d17 100644 | |
| --- a/activerecord/test/cases/base_test.rb | |
| +++ b/activerecord/test/cases/base_test.rb | |
| @@ -1540,6 +1540,16 @@ def test_switching_between_table_name | |
| end | |
| end | |
| + def test_dont_clear_inheritnce_column_when_setting_explicitly | |
| + Joke.inheritance_column = "my_type" | |
| + before_inherit = Joke.inheritance_column | |
| + | |
| + Joke.reset_column_information | |
| + after_inherit = Joke.inheritance_column | |
| + | |
| + assert_equal before_inherit, after_inherit unless before_inherit.blank? && after_inherit.blank? | |
| + end | |
| + | |
| def test_set_table_name_symbol_converted_to_string | |
| Joke.table_name = :cold_jokes | |
| assert_equal 'cold_jokes', Joke.table_name | |
| diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb | |
| index 3652255..32726e8 100644 | |
| --- a/activerecord/test/cases/bind_parameter_test.rb | |
| +++ b/activerecord/test/cases/bind_parameter_test.rb | |
| @@ -30,8 +30,7 @@ def teardown | |
| end | |
| def test_binds_are_logged | |
| - # FIXME: use skip with minitest | |
| - return unless @connection.supports_statement_cache? | |
| + return skip_bind_parameter_test unless supports_statement_cache? | |
| sub = @connection.substitute_at(@pk, 0) | |
| binds = [[@pk, 1]] | |
| @@ -44,8 +43,7 @@ def test_binds_are_logged | |
| end | |
| def test_find_one_uses_binds | |
| - # FIXME: use skip with minitest | |
| - return unless @connection.supports_statement_cache? | |
| + return skip_bind_parameter_test unless supports_statement_cache? | |
| Topic.find(1) | |
| binds = [[@pk, 1]] | |
| @@ -54,8 +52,7 @@ def test_find_one_uses_binds | |
| end | |
| def test_logs_bind_vars | |
| - # FIXME: use skip with minitest | |
| - return unless @connection.supports_statement_cache? | |
| + return skip_bind_parameter_test unless supports_statement_cache? | |
| pk = Topic.columns.find { |x| x.primary } | |
| @@ -86,5 +83,15 @@ def debug str | |
| logger.sql event | |
| assert_match([[pk.name, 10]].inspect, logger.debugs.first) | |
| end | |
| + | |
| + private | |
| + | |
| + def skip_bind_parameter_test | |
| + skip('prepared statement caching is not supported') | |
| + end | |
| + | |
| + def supports_statement_cache? | |
| + @connection.supports_statement_cache? | |
| + end | |
| end | |
| end | |
| diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb | |
| index 67137d1..8755e1f 100644 | |
| --- a/activerecord/test/cases/calculations_test.rb | |
| +++ b/activerecord/test/cases/calculations_test.rb | |
| @@ -493,6 +493,12 @@ def test_pluck_with_qualified_column_name | |
| assert_equal [1,2,3,4], Topic.order(:id).pluck("topics.id") | |
| end | |
| + def test_pluck_does_not_replace_select_clause | |
| + taks_relation = Topic.select("approved, id, id AS foo_id").order('foo_id DESC') | |
| + assert_equal [4,3,2,1], taks_relation.pluck(:id) | |
| + assert_equal [true, true, true, false], taks_relation.pluck(:approved) | |
| + end | |
| + | |
| def test_pluck_auto_table_name_prefix | |
| c = Company.create!(:name => "test", :contracts => [Contract.new]) | |
| assert_equal [c.id], Company.joins(:contracts).pluck(:id) | |
| diff --git a/activerecord/test/cases/disconnected_test.rb b/activerecord/test/cases/disconnected_test.rb | |
| new file mode 100644 | |
| index 0000000..cc2c1f6 | |
| --- /dev/null | |
| +++ b/activerecord/test/cases/disconnected_test.rb | |
| @@ -0,0 +1,26 @@ | |
| +require "cases/helper" | |
| + | |
| +class TestRecord < ActiveRecord::Base | |
| +end | |
| + | |
| +class TestDisconnectedAdapter < ActiveRecord::TestCase | |
| + self.use_transactional_fixtures = false | |
| + | |
| + def setup | |
| + @connection = ActiveRecord::Base.connection | |
| + end | |
| + | |
| + def teardown | |
| + spec = ActiveRecord::Base.connection_config | |
| + ActiveRecord::Base.establish_connection(spec) | |
| + @connection = nil | |
| + end | |
| + | |
| + test "can't execute statements while disconnected" do | |
| + @connection.execute "SELECT count(*) from products" | |
| + @connection.disconnect! | |
| + assert_raises(ActiveRecord::StatementInvalid) do | |
| + @connection.execute "SELECT count(*) from products" | |
| + end | |
| + end | |
| +end | |
| diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb | |
| index 7b852a6..0546e79 100644 | |
| --- a/activerecord/test/cases/explain_subscriber_test.rb | |
| +++ b/activerecord/test/cases/explain_subscriber_test.rb | |
| @@ -38,6 +38,13 @@ def test_collects_pairs_of_queries_and_binds | |
| end | |
| end | |
| + def test_collects_nothing_if_the_statement_is_only_partially_matched | |
| + with_queries([]) do |queries| | |
| + SUBSCRIBER.call(:name => 'SQL', :sql => 'select_db yo_mama') | |
| + assert queries.empty? | |
| + end | |
| + end | |
| + | |
| def test_collects_nothing_if_unexplained_sqls | |
| with_queries([]) do |queries| | |
| SUBSCRIBER.call(:name => 'SQL', :sql => 'SHOW max_identifier_length') | |
| diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb | |
| index 7d63d76..89e1764 100644 | |
| --- a/activerecord/test/cases/finder_test.rb | |
| +++ b/activerecord/test/cases/finder_test.rb | |
| @@ -31,6 +31,13 @@ def test_find | |
| assert_equal(topics(:first).title, Topic.find(1).title) | |
| end | |
| + def test_symbols_table_ref | |
| + Post.first # warm up | |
| + x = Symbol.all_symbols.count | |
| + Post.where("title" => {"xxxqqqq" => "bar"}) | |
| + assert_equal x, Symbol.all_symbols.count | |
| + end | |
| + | |
| # find should handle strings that come from URLs | |
| # (example: Category.find(params[:id])) | |
| def test_find_with_string | |
| @@ -86,6 +93,18 @@ def test_exists_with_includes_limit_and_empty_result | |
| assert !Topic.includes(:replies).limit(1).where('0 = 1').exists? | |
| end | |
| + def test_exists_with_distinct_association_includes_and_limit | |
| + author = Author.first | |
| + assert !author.unique_categorized_posts.includes(:special_comments).limit(0).exists? | |
| + assert author.unique_categorized_posts.includes(:special_comments).limit(1).exists? | |
| + end | |
| + | |
| + def test_exists_with_distinct_association_includes_limit_and_order | |
| + author = Author.first | |
| + assert !author.unique_categorized_posts.includes(:special_comments).order('comments.taggings_count DESC').limit(0).exists? | |
| + assert author.unique_categorized_posts.includes(:special_comments).order('comments.taggings_count DESC').limit(1).exists? | |
| + end | |
| + | |
| def test_exists_with_empty_table_and_no_args_given | |
| Topic.delete_all | |
| assert !Topic.exists? | |
| @@ -286,10 +305,24 @@ def test_first_and_last_with_integer_should_use_sql_limit | |
| assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries } | |
| end | |
| + def test_last_should_use_default_order | |
| + assert_sql(/ORDER BY .topics.\..id. DESC/) { Topic.last } | |
| + end | |
| + | |
| + def test_last_with_integer_should_use_default_order | |
| + assert_sql(/ORDER BY .topics.\..id. DESC/) { Topic.last(5).entries } | |
| + end | |
| + | |
| def test_last_with_integer_and_order_should_keep_the_order | |
| assert_equal Topic.order("title").to_a.last(2), Topic.order("title").last(2) | |
| end | |
| + def test_last_with_integer_should_work_with_joins | |
| + assert_nothing_raised do | |
| + Post.joins(:comments).last(2) | |
| + end | |
| + end | |
| + | |
| def test_last_with_integer_and_order_should_not_use_sql_limit | |
| query = assert_sql { Topic.order("title").last(5).entries } | |
| assert_equal 1, query.length | |
| diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb | |
| index 99dd74c..4c7da6e 100644 | |
| --- a/activerecord/test/cases/fixtures_test.rb | |
| +++ b/activerecord/test/cases/fixtures_test.rb | |
| @@ -549,7 +549,7 @@ class LoadAllFixturesTest < ActiveRecord::TestCase | |
| fixtures :all | |
| def test_all_there | |
| - assert_equal %w(developers people tasks), fixture_table_names.sort | |
| + assert_equal %w(admin/accounts admin/users developers people tasks), fixture_table_names.sort | |
| end | |
| end | |
| diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb | |
| index 54c9152..e4cd89e 100644 | |
| --- a/activerecord/test/cases/inheritance_test.rb | |
| +++ b/activerecord/test/cases/inheritance_test.rb | |
| @@ -236,6 +236,11 @@ def test_inheritance_without_mapping | |
| assert_nothing_raised { s = SpecialSubscriber.new("name" => "And breaaaaathe!"); s.id = 'roger'; s.save } | |
| end | |
| + def test_scope_inherited_properly | |
| + assert_nothing_raised { Company.of_first_firm } | |
| + assert_nothing_raised { Client.of_first_firm } | |
| + end | |
| + | |
| private | |
| def switch_to_alt_inheritance_column | |
| # we don't want misleading test results, so get rid of the values in the type column | |
| @@ -290,3 +295,26 @@ def test_instantiation_doesnt_try_to_require_corresponding_file | |
| ActiveRecord::Base.store_full_sti_class = true | |
| end | |
| end | |
| + | |
| + | |
| +class GlobalInheritanceColumnTest < ActiveRecord::TestCase | |
| + fixtures :companies | |
| + | |
| + setup do | |
| + @inheritance_column = ActiveRecord::Base.inheritance_column | |
| + end | |
| + | |
| + teardown do | |
| + ActiveRecord::Base.inheritance_column = @inheritance_column | |
| + end | |
| + | |
| + def test_changing_global_inheritance_column | |
| + ActiveRecord::Base.inheritance_column = 'ruby_type' | |
| + | |
| + firm = Firm.create('name' => 'FirmWithAltInheritanceColumn') | |
| + assert_equal 'Firm', firm.ruby_type | |
| + | |
| + assert_equal 'ruby_type', Company.inheritance_column | |
| + assert_equal 'ruby_type', Firm.inheritance_column | |
| + end | |
| +end | |
| diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb | |
| index 066a60f..43b3e07 100644 | |
| --- a/activerecord/test/cases/locking_test.rb | |
| +++ b/activerecord/test/cases/locking_test.rb | |
| @@ -23,6 +23,18 @@ class ReadonlyNameShip < Ship | |
| class OptimisticLockingTest < ActiveRecord::TestCase | |
| fixtures :people, :legacy_things, :references, :string_key_objects, :peoples_treasures | |
| + def test_quote_value_passed_lock_col | |
| + p1 = Person.find(1) | |
| + assert_equal 0, p1.lock_version | |
| + | |
| + p1.expects(:quote_value).with(0, Person.columns_hash[Person.locking_column]).returns('0').once | |
| + | |
| + p1.first_name = 'anika2' | |
| + p1.save! | |
| + | |
| + assert_equal 1, p1.lock_version | |
| + end | |
| + | |
| def test_non_integer_lock_existing | |
| s1 = StringKeyObject.find("record1") | |
| s2 = StringKeyObject.find("record1") | |
| diff --git a/activerecord/test/cases/mass_assignment_security_test.rb b/activerecord/test/cases/mass_assignment_security_test.rb | |
| index 5153945..13f90e8 100644 | |
| --- a/activerecord/test/cases/mass_assignment_security_test.rb | |
| +++ b/activerecord/test/cases/mass_assignment_security_test.rb | |
| @@ -300,6 +300,16 @@ def test_find_or_create_by_with_admin_role_with_attr_protected_attributes | |
| assert_admin_attributes(p, true) | |
| end | |
| + def test_attr_protected_with_newline | |
| + p = LoosePerson.new | |
| + assert_raises(ActiveRecord::UnknownAttributeError) do | |
| + p.attributes = {"comments=\n"=>"hax"} | |
| + end | |
| + assert_nil p.comments, "Comments is meant to be attr_protected but I assigned it with attributes=" | |
| + p.attributes= {"comments(1)\n" => "hax"} | |
| + assert_nil p.comments, "Comments is meant to be attr_protected but I assigned it with attributes=" | |
| + end | |
| + | |
| end | |
| diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb | |
| index 5b67d22..7f0d921 100644 | |
| --- a/activerecord/test/cases/migration_test.rb | |
| +++ b/activerecord/test/cases/migration_test.rb | |
| @@ -61,7 +61,7 @@ def teardown | |
| ActiveRecord::Base.connection.initialize_schema_migrations_table | |
| ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}" | |
| - %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table| | |
| + %w(things awesome_things prefix_things_suffix p_awesome_things_s).each do |table| | |
| Thing.connection.drop_table(table) rescue nil | |
| end | |
| Thing.reset_column_information | |
| @@ -874,8 +874,6 @@ def test_remove_column_no_second_parameter_raises_exception | |
| end | |
| def test_remove_column_with_array_as_an_argument_is_deprecated | |
| - return skip "remove_column with array as argument is not supported with OracleAdapter" if current_adapter? :OracleAdapter | |
| - | |
| ActiveRecord::Base.connection.create_table(:hats) do |table| | |
| table.column :hat_name, :string, :limit => 100 | |
| table.column :hat_size, :integer | |
| @@ -886,7 +884,7 @@ def test_remove_column_with_array_as_an_argument_is_deprecated | |
| Person.connection.remove_column("hats", ["hat_name", "hat_size"]) | |
| end | |
| ensure | |
| - ActiveRecord::Base.connection.drop_table(:hats) rescue nil | |
| + ActiveRecord::Base.connection.drop_table(:hats) | |
| end | |
| def test_removing_and_renaming_column_preserves_custom_primary_key | |
| @@ -1647,8 +1645,8 @@ def test_proper_table_name | |
| def test_rename_table_with_prefix_and_suffix | |
| assert !Thing.table_exists? | |
| - ActiveRecord::Base.table_name_prefix = 'prefix_' | |
| - ActiveRecord::Base.table_name_suffix = '_suffix' | |
| + ActiveRecord::Base.table_name_prefix = 'p_' | |
| + ActiveRecord::Base.table_name_suffix = '_s' | |
| Thing.reset_table_name | |
| Thing.reset_sequence_name | |
| WeNeedThings.up | |
| @@ -1657,7 +1655,7 @@ def test_rename_table_with_prefix_and_suffix | |
| assert_equal "hello world", Thing.find(:first).content | |
| RenameThings.up | |
| - Thing.table_name = "prefix_awesome_things_suffix" | |
| + Thing.table_name = "p_awesome_things_s" | |
| assert_equal "hello world", Thing.find(:first).content | |
| ensure | |
| diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb | |
| index ada4294..3963207 100644 | |
| --- a/activerecord/test/cases/relations_test.rb | |
| +++ b/activerecord/test/cases/relations_test.rb | |
| @@ -4,6 +4,7 @@ | |
| require 'models/post' | |
| require 'models/topic' | |
| require 'models/comment' | |
| +require 'models/rating' | |
| require 'models/reply' | |
| require 'models/author' | |
| require 'models/comment' | |
| @@ -19,7 +20,7 @@ | |
| class RelationTest < ActiveRecord::TestCase | |
| fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments, | |
| - :tags, :taggings, :cars, :minivans | |
| + :ratings, :tags, :taggings, :cars, :minivans | |
| def test_do_not_double_quote_string_id | |
| van = Minivan.last | |
| @@ -332,6 +333,13 @@ def test_find_with_preloaded_associations | |
| end | |
| end | |
| + def test_preload_applies_to_all_chained_preloaded_scopes | |
| + assert_queries(3) do | |
| + post = Post.with_tags.with_comments.first | |
| + assert post | |
| + end | |
| + end | |
| + | |
| def test_find_with_included_associations | |
| assert_queries(2) do | |
| posts = Post.includes(:comments).order('posts.id') | |
| @@ -660,6 +668,21 @@ def test_relation_merging | |
| assert_equal [developers(:poor_jamis)], dev_with_count.to_a | |
| end | |
| + def test_relation_merging_with_arel_equalities_keeps_last_equality | |
| + devs = Developer.where(Developer.arel_table[:salary].eq(80000)).merge( | |
| + Developer.where(Developer.arel_table[:salary].eq(9000)) | |
| + ) | |
| + assert_equal [developers(:poor_jamis)], devs.to_a | |
| + end | |
| + | |
| + def test_relation_merging_with_arel_equalities_with_a_non_attribute_left_hand_ignores_non_attributes_when_discarding_equalities | |
| + salary_attr = Developer.arel_table[:salary] | |
| + devs = Developer.where(salary_attr.eq(80000)).merge( | |
| + Developer.where(salary_attr.eq(9000)).where(Arel::Nodes::NamedFunction.new('abs', [salary_attr]).eq(9000)) | |
| + ) | |
| + assert_equal [developers(:poor_jamis)], devs.to_a | |
| + end | |
| + | |
| def test_relation_merging_with_eager_load | |
| relations = [] | |
| relations << Post.order('comments.id DESC').merge(Post.eager_load(:last_comment)).merge(Post.scoped) | |
| @@ -689,6 +712,12 @@ def test_relation_merging_with_joins | |
| assert_equal 1, comments.count | |
| end | |
| + def test_relation_merging_with_merged_joins | |
| + special_comments_with_ratings = SpecialComment.joins(:ratings) | |
| + posts_with_special_comments_with_ratings = Post.group('posts.id').joins(:special_comments).merge(special_comments_with_ratings) | |
| + assert_equal 1, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count.length | |
| + end | |
| + | |
| def test_count | |
| posts = Post.scoped | |
| diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb | |
| index 0a9643b..629e5e0 100644 | |
| --- a/activerecord/test/cases/schema_dumper_test.rb | |
| +++ b/activerecord/test/cases/schema_dumper_test.rb | |
| @@ -206,6 +206,11 @@ def test_schema_dump_includes_decimal_options | |
| end | |
| if current_adapter?(:PostgreSQLAdapter) | |
| + def test_schema_dump_includes_bigint_default | |
| + output = standard_dump | |
| + assert_match %r{t.integer\s+"bigint_default",\s+:limit => 8,\s+:default => 0}, output | |
| + end | |
| + | |
| def test_schema_dump_includes_xml_shorthand_definition | |
| output = standard_dump | |
| if %r{create_table "postgresql_xml_data_type"} =~ output | |
| diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb | |
| index 277fc9d..bbcbeac 100644 | |
| --- a/activerecord/test/cases/store_test.rb | |
| +++ b/activerecord/test/cases/store_test.rb | |
| @@ -40,4 +40,11 @@ class StoreTest < ActiveRecord::TestCase | |
| @john.remember_login = false | |
| assert_equal false, @john.remember_login | |
| end | |
| + | |
| + test "updating the store will track changes correctly" do | |
| + @john.color = "blue" | |
| + assert_equal [{:color => "black"}, {:color => "blue"}], @john.settings_change | |
| + @john.homepage = "37signals.com" | |
| + assert_equal [{:color => "black"}, {:color => "blue", :homepage => "37signals.com"}], @john.settings_change | |
| + end | |
| end | |
| diff --git a/activerecord/test/fixtures/all/admin b/activerecord/test/fixtures/all/admin | |
| new file mode 120000 | |
| index 0000000..59636af | |
| --- /dev/null | |
| +++ b/activerecord/test/fixtures/all/admin | |
| @@ -0,0 +1 @@ | |
| +../admin/ | |
| \ No newline at end of file | |
| diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb | |
| index f8e259f..3b2b79c 100644 | |
| --- a/activerecord/test/models/company.rb | |
| +++ b/activerecord/test/models/company.rb | |
| @@ -12,6 +12,11 @@ class Company < AbstractCompany | |
| has_many :contracts | |
| has_many :developers, :through => :contracts | |
| + scope :of_first_firm, lambda { | |
| + joins(:account => :firm). | |
| + where('firms.id' => 1) | |
| + } | |
| + | |
| def arbitrary_method | |
| "I am Jack's profound disappointment" | |
| end | |
| @@ -44,7 +49,7 @@ class Firm < Company | |
| has_many :unsorted_clients, :class_name => "Client" | |
| has_many :unsorted_clients_with_symbol, :class_name => :Client | |
| has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC" | |
| - has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id" | |
| + has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :inverse_of => :firm | |
| has_many :clients_ordered_by_name, :order => "name", :class_name => "Client" | |
| has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false | |
| has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy | |
| @@ -126,6 +131,11 @@ class Client < Company | |
| has_many :accounts, :through => :firm | |
| belongs_to :account | |
| + attr_accessor :touch_firm_on_validate | |
| + validate do | |
| + firm if touch_firm_on_validate | |
| + end | |
| + | |
| class RaisedOnSave < RuntimeError; end | |
| attr_accessor :raise_on_save | |
| before_save do | |
| diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb | |
| index 07c529d..d316a0b 100644 | |
| --- a/activerecord/test/models/person.rb | |
| +++ b/activerecord/test/models/person.rb | |
| @@ -27,6 +27,7 @@ class Person < ActiveRecord::Base | |
| has_many :agents_posts, :through => :agents, :source => :posts | |
| has_many :agents_posts_authors, :through => :agents_posts, :source => :author | |
| + has_many :essays, :primary_key => "first_name", :foreign_key => "writer_id" | |
| scope :males, :conditions => { :gender => 'M' } | |
| scope :females, :conditions => { :gender => 'F' } | |
| diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb | |
| index 9aa02fa..3cfd1a0 100644 | |
| --- a/activerecord/test/models/post.rb | |
| +++ b/activerecord/test/models/post.rb | |
| @@ -76,6 +76,9 @@ def add_joins_and_select | |
| end | |
| end | |
| + scope :with_comments, preload(:comments) | |
| + scope :with_tags, preload(:taggings) | |
| + | |
| has_many :interpolated_taggings, :class_name => 'Tagging', :as => :taggable, :conditions => proc { "1 = #{1}" } | |
| has_many :interpolated_tags, :through => :taggings | |
| has_many :interpolated_tags_2, :through => :interpolated_taggings, :source => :tag | |
| diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb | |
| index b2c655d..4f546df 100644 | |
| --- a/activerecord/test/schema/postgresql_specific_schema.rb | |
| +++ b/activerecord/test/schema/postgresql_specific_schema.rb | |
| @@ -30,6 +30,7 @@ | |
| char3 text default 'a text field', | |
| positive_integer integer default 1, | |
| negative_integer integer default -1, | |
| + bigint_default bigint default 0::bigint, | |
| decimal_number decimal(3,2) default 2.78, | |
| multiline_default text DEFAULT '--- [] | |
| diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md | |
| index 245b000..c1f0fa3 100644 | |
| --- a/activeresource/CHANGELOG.md | |
| +++ b/activeresource/CHANGELOG.md | |
| @@ -1,12 +1,20 @@ | |
| -## unreleased ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| -* No changes. | |
| +* No changes. | |
| + | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| + | |
| +* Fixes an issue that ActiveResource models ignores ActiveResource::Base.include_root_in_json. | |
| + Backported from the now separate repo rails/activeresouce. | |
| + *Xinjiang Lu* | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| + | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| * No changes. | |
| + | |
| ## Rails 3.2.12 (Feb 11, 2013) ## | |
| * No changes. | |
| diff --git a/activeresource/activeresource.gemspec b/activeresource/activeresource.gemspec | |
| index f5c26f3..ea3a1b4 100644 | |
| --- a/activeresource/activeresource.gemspec | |
| +++ b/activeresource/activeresource.gemspec | |
| @@ -7,6 +7,8 @@ Gem::Specification.new do |s| | |
| s.summary = 'REST modeling framework (part of Rails).' | |
| s.description = 'REST on Rails. Wrap your RESTful web app with Ruby classes and work with them like Active Record models.' | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb | |
| index 032a245..f9cc7d0 100644 | |
| --- a/activeresource/lib/active_resource/base.rb | |
| +++ b/activeresource/lib/active_resource/base.rb | |
| @@ -1336,7 +1336,7 @@ def respond_to?(method, include_priv = false) | |
| end | |
| def to_json(options={}) | |
| - super({ :root => self.class.element_name }.merge(options)) | |
| + super(include_root_in_json ? { :root => self.class.element_name }.merge(options) : options) | |
| end | |
| def to_xml(options={}) | |
| diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb | |
| index 6c3ee00..c67b773 100644 | |
| --- a/activeresource/lib/active_resource/version.rb | |
| +++ b/activeresource/lib/active_resource/version.rb | |
| @@ -2,7 +2,7 @@ module ActiveResource | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb | |
| index 983f054..9176b5d 100644 | |
| --- a/activeresource/test/cases/base_test.rb | |
| +++ b/activeresource/test/cases/base_test.rb | |
| @@ -1020,7 +1020,6 @@ def foo | |
| end | |
| def test_to_json | |
| - Person.include_root_in_json = true | |
| joe = Person.find(6) | |
| encode = joe.encode | |
| json = joe.to_json | |
| @@ -1032,6 +1031,21 @@ def test_to_json | |
| assert_match %r{\}\}$}, json | |
| end | |
| + def test_to_json_without_root | |
| + ActiveResource::Base.include_root_in_json = false | |
| + joe = Person.find(6) | |
| + encode = joe.encode | |
| + json = joe.to_json | |
| + | |
| + assert_equal encode, json | |
| + assert_no_match %r{^\{"person":\}}, json | |
| + assert_match %r{"id":6}, json | |
| + assert_match %r{"name":"Joe"}, json | |
| + assert_match %r{\}$}, json | |
| + ensure | |
| + ActiveResource::Base.include_root_in_json = true | |
| + end | |
| + | |
| def test_to_json_with_element_name | |
| old_elem_name = Person.element_name | |
| Person.include_root_in_json = true | |
| diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md | |
| index ea72f69..29f7db5 100644 | |
| --- a/activesupport/CHANGELOG.md | |
| +++ b/activesupport/CHANGELOG.md | |
| @@ -1,10 +1,46 @@ | |
| -## unreleased ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| -* No changes. | |
| +* Fix ActiveSupport::Cache::FileStore#cleanup to no longer rely on missing each_key method. | |
| + | |
| + *Murray Steele* | |
| + | |
| +* Add respond_to_missing? for TaggedLogging which is best practice when overriding method_missing. This permits | |
| + wrapping TaggedLogging by another log abstraction such as em-logger. | |
| + | |
| + *Wolfram Arnold* | |
| + | |
| + | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| + | |
| +* Make `Time.at_with_coercion` retain the second fraction and return local time. | |
| + | |
| + Fixes #11350 | |
| + | |
| + *Neer Friedman*, *Andrew White* | |
| + | |
| +* Fix `ActiveSupport::TaggedLogging` incorrectly providing program name the same as log message | |
| + even when block is not provided. | |
| + | |
| + *Carson Reinke* | |
| + | |
| +* Override `Time.at` to support the passing of Time-like values when called with a single argument. | |
| + | |
| + *Andrew White* | |
| + | |
| +* Revert the changes on unicode character encoding from `ActiveSupport::JSON.encode`. | |
| + This was causing a regression where the resulting string is always returning UTF-8. | |
| + Also it changes the behavior of this method on a stable release. | |
| + Fixes #9498. | |
| + | |
| + *Rafael Mendonça França* | |
| + | |
| +* Fix `ActiveSupport::TimeZone.parse` when time is at a local DST jump. | |
| + Fixes #9678. | |
| + *Andrew White* | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| * Fix DateTime comparison with DateTime::Infinity object. | |
| diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec | |
| index cbe0947..e131ca7 100644 | |
| --- a/activesupport/activesupport.gemspec | |
| +++ b/activesupport/activesupport.gemspec | |
| @@ -7,6 +7,8 @@ Gem::Specification.new do |s| | |
| s.summary = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework.' | |
| s.description = 'A toolkit of support libraries and Ruby core extensions extracted from the Rails framework. Rich support for multibyte strings, internationalization, time zones, and testing.' | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| @@ -18,6 +20,6 @@ Gem::Specification.new do |s| | |
| s.rdoc_options.concat ['--encoding', 'UTF-8'] | |
| - s.add_dependency('i18n', '= 0.6.1') | |
| + s.add_dependency('i18n', '~> 0.6', '>= 0.6.4') | |
| s.add_dependency('multi_json', '~> 1.0') | |
| end | |
| diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb | |
| index 9460532..c800365 100644 | |
| --- a/activesupport/lib/active_support/cache/file_store.rb | |
| +++ b/activesupport/lib/active_support/cache/file_store.rb | |
| @@ -29,7 +29,8 @@ def clear(options = nil) | |
| def cleanup(options = nil) | |
| options = merged_options(options) | |
| - each_key(options) do |key| | |
| + search_dir(cache_path) do |fname| | |
| + key = file_path_key(fname) | |
| entry = read_entry(key, options) | |
| delete_entry(key, options) if entry && entry.expired? | |
| end | |
| diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb | |
| index 305ed49..cd7877f 100644 | |
| --- a/activesupport/lib/active_support/core_ext/class/attribute.rb | |
| +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb | |
| @@ -67,7 +67,7 @@ class Class | |
| # object.setting = false # => NoMethodError | |
| def class_attribute(*attrs) | |
| options = attrs.extract_options! | |
| - instance_reader = options.fetch(:instance_reader, true) | |
| + instance_reader = instance_reader = options.fetch(:instance_reader, true) | |
| instance_writer = options.fetch(:instance_writer, true) | |
| attrs.each do |name| | |
| diff --git a/activesupport/lib/active_support/core_ext/date_time/acts_like.rb b/activesupport/lib/active_support/core_ext/date_time/acts_like.rb | |
| index c79745c..8fbbe0d 100644 | |
| --- a/activesupport/lib/active_support/core_ext/date_time/acts_like.rb | |
| +++ b/activesupport/lib/active_support/core_ext/date_time/acts_like.rb | |
| @@ -1,3 +1,4 @@ | |
| +require 'date' | |
| require 'active_support/core_ext/object/acts_like' | |
| class DateTime | |
| diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb | |
| index 9146d82..0383a6a 100644 | |
| --- a/activesupport/lib/active_support/core_ext/time/calculations.rb | |
| +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb | |
| @@ -45,6 +45,23 @@ def local_time(*args) | |
| def current | |
| ::Time.zone ? ::Time.zone.now : ::Time.now | |
| end | |
| + | |
| + # Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime | |
| + # instances can be used when called with a single argument | |
| + def at_with_coercion(*args) | |
| + return at_without_coercion(*args) if args.size != 1 | |
| + | |
| + # Time.at can be called with a time or numerical value | |
| + time_or_number = args.first | |
| + | |
| + if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime) | |
| + at_without_coercion(time_or_number.to_f).getlocal | |
| + else | |
| + at_without_coercion(time_or_number) | |
| + end | |
| + end | |
| + alias_method :at_without_coercion, :at | |
| + alias_method :at, :at_with_coercion | |
| end | |
| # Tells whether the Time object's time lies in the past | |
| diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb | |
| index a50e652..bd2f909 100644 | |
| --- a/activesupport/lib/active_support/json/encoding.rb | |
| +++ b/activesupport/lib/active_support/json/encoding.rb | |
| @@ -122,7 +122,13 @@ def escape(string) | |
| if string.respond_to?(:force_encoding) | |
| string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY) | |
| end | |
| - json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] } | |
| + json = string. | |
| + gsub(escape_regex) { |s| ESCAPED_CHARS[s] }. | |
| + gsub(/([\xC0-\xDF][\x80-\xBF]| | |
| + [\xE0-\xEF][\x80-\xBF]{2}| | |
| + [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| | |
| + s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/n, '\\\\u\&') | |
| + } | |
| json = %("#{json}") | |
| json.force_encoding(::Encoding::UTF_8) if json.respond_to?(:force_encoding) | |
| json | |
| diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb | |
| index 6296c1d..ed7c73b 100644 | |
| --- a/activesupport/lib/active_support/log_subscriber.rb | |
| +++ b/activesupport/lib/active_support/log_subscriber.rb | |
| @@ -118,5 +118,9 @@ def color(text, color, bold=false) | |
| bold = bold ? BOLD : "" | |
| "#{bold}#{color}#{text}#{CLEAR}" | |
| end | |
| + | |
| + def format_duration(duration) | |
| + "%.1fms" % duration | |
| + end | |
| end | |
| end | |
| diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb | |
| index 7e7f7ec..232c367 100644 | |
| --- a/activesupport/lib/active_support/tagged_logging.rb | |
| +++ b/activesupport/lib/active_support/tagged_logging.rb | |
| @@ -44,7 +44,14 @@ def silence(temporary_level = Logger::ERROR, &block) | |
| deprecate :silence | |
| def add(severity, message = nil, progname = nil, &block) | |
| - message = (block_given? ? block.call : progname) if message.nil? | |
| + if message.nil? | |
| + if block_given? | |
| + message = block.call | |
| + else | |
| + message = progname | |
| + progname = nil #No instance variable for this like Logger | |
| + end | |
| + end | |
| @logger.add(severity, "#{tags_text}#{message}", progname) | |
| end | |
| @@ -65,6 +72,16 @@ def method_missing(method, *args) | |
| @logger.send(method, *args) | |
| end | |
| + if RUBY_VERSION < '1.9' | |
| + def respond_to?(*args) | |
| + super || @logger.respond_to?(*args) | |
| + end | |
| + else | |
| + def respond_to_missing?(*args) | |
| + @logger.respond_to? *args | |
| + end | |
| + end | |
| + | |
| private | |
| def tags_text | |
| tags = current_tags | |
| diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb | |
| index dd23f8d..5b74e52 100644 | |
| --- a/activesupport/lib/active_support/testing/performance.rb | |
| +++ b/activesupport/lib/active_support/testing/performance.rb | |
| @@ -23,7 +23,7 @@ module Performance | |
| # each implementation should define metrics and freeze the defaults | |
| DEFAULTS = | |
| - if ARGV.include?('--benchmark') # HAX for rake test | |
| + if ENV["BENCHMARK_TESTS"] | |
| { :runs => 4, | |
| :output => 'tmp/performance', | |
| :benchmark => true } | |
| diff --git a/activesupport/lib/active_support/testing/performance/jruby.rb b/activesupport/lib/active_support/testing/performance/jruby.rb | |
| index b347539..af08f0e 100644 | |
| --- a/activesupport/lib/active_support/testing/performance/jruby.rb | |
| +++ b/activesupport/lib/active_support/testing/performance/jruby.rb | |
| @@ -6,7 +6,7 @@ module ActiveSupport | |
| module Testing | |
| module Performance | |
| DEFAULTS.merge!( | |
| - if ARGV.include?('--benchmark') | |
| + if ENV["BENCHMARK_TESTS"] | |
| {:metrics => [:wall_time, :user_time, :memory, :gc_runs, :gc_time]} | |
| else | |
| { :metrics => [:wall_time], | |
| diff --git a/activesupport/lib/active_support/testing/performance/rubinius.rb b/activesupport/lib/active_support/testing/performance/rubinius.rb | |
| index d9ebfbe..baabd9c 100644 | |
| --- a/activesupport/lib/active_support/testing/performance/rubinius.rb | |
| +++ b/activesupport/lib/active_support/testing/performance/rubinius.rb | |
| @@ -4,7 +4,7 @@ module ActiveSupport | |
| module Testing | |
| module Performance | |
| DEFAULTS.merge!( | |
| - if ARGV.include?('--benchmark') | |
| + if ENV["BENCHMARK_TESTS"] | |
| {:metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time]} | |
| else | |
| { :metrics => [:wall_time], | |
| diff --git a/activesupport/lib/active_support/testing/performance/ruby.rb b/activesupport/lib/active_support/testing/performance/ruby.rb | |
| index 50c4852..5aaed2b 100644 | |
| --- a/activesupport/lib/active_support/testing/performance/ruby.rb | |
| +++ b/activesupport/lib/active_support/testing/performance/ruby.rb | |
| @@ -9,7 +9,7 @@ module ActiveSupport | |
| module Testing | |
| module Performance | |
| DEFAULTS.merge!( | |
| - if ARGV.include?('--benchmark') | |
| + if ENV["BENCHMARK_TESTS"] | |
| { :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time] } | |
| else | |
| { :min_percent => 0.01, | |
| diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb | |
| index c7d8fc2..f935180 100644 | |
| --- a/activesupport/lib/active_support/values/time_zone.rb | |
| +++ b/activesupport/lib/active_support/values/time_zone.rb | |
| @@ -268,13 +268,23 @@ def at(secs) | |
| # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00 | |
| # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 | |
| def parse(str, now=now) | |
| - date_parts = Date._parse(str) | |
| - return if date_parts.blank? | |
| - time = Time.parse(str, now) rescue DateTime.parse(str) | |
| - if date_parts[:offset].nil? | |
| - ActiveSupport::TimeWithZone.new(nil, self, time) | |
| + parts = Date._parse(str, false) | |
| + return if parts.empty? | |
| + | |
| + time = Time.utc( | |
| + parts.fetch(:year, now.year), | |
| + parts.fetch(:mon, now.month), | |
| + parts.fetch(:mday, now.day), | |
| + parts.fetch(:hour, 0), | |
| + parts.fetch(:min, 0), | |
| + parts.fetch(:sec, 0), | |
| + parts.fetch(:sec_fraction, 0) * 1000000 | |
| + ) | |
| + | |
| + if parts[:offset] | |
| + TimeWithZone.new(time - parts[:offset], self) | |
| else | |
| - time.in_time_zone(self) | |
| + TimeWithZone.new(nil, self, time) | |
| end | |
| end | |
| diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb | |
| index 03b1e51..c575e60 100644 | |
| --- a/activesupport/lib/active_support/version.rb | |
| +++ b/activesupport/lib/active_support/version.rb | |
| @@ -2,7 +2,7 @@ module ActiveSupport | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb | |
| index 6db1746..61bf808 100644 | |
| --- a/activesupport/test/caching_test.rb | |
| +++ b/activesupport/test/caching_test.rb | |
| @@ -608,6 +608,18 @@ def test_delete_matched_when_cache_directory_does_not_exist | |
| ActiveSupport::Cache::FileStore.new('/test/cache/directory').delete_matched(/does_not_exist/) | |
| end | |
| end | |
| + | |
| + def test_cleanup_removes_all_expired_entries | |
| + time = Time.now | |
| + @cache.write('foo', 'bar', :expires_in => 10) | |
| + @cache.write('baz', 'qux') | |
| + @cache.write('quux', 'corge', :expires_in => 20) | |
| + Time.stubs(:now).returns(time + 15) | |
| + @cache.cleanup | |
| + assert !@cache.exist?('foo') | |
| + assert @cache.exist?('baz') | |
| + assert @cache.exist?('quux') | |
| + end | |
| end | |
| class MemoryStoreTest < ActiveSupport::TestCase | |
| diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb | |
| index 0d68083..2a9d059 100644 | |
| --- a/activesupport/test/core_ext/time_ext_test.rb | |
| +++ b/activesupport/test/core_ext/time_ext_test.rb | |
| @@ -774,6 +774,82 @@ def test_compare_with_time_with_zone | |
| assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) | |
| end | |
| + def test_at_with_datetime | |
| + assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0)) | |
| + | |
| + # Only test this if the underlying Time.at raises a TypeError | |
| + begin | |
| + Time.at_without_coercion(Time.now, 0) | |
| + rescue TypeError | |
| + assert_raise(TypeError) { assert_equal(Time.utc(2000, 1, 1, 0, 0, 0), Time.at(DateTime.civil(2000, 1, 1, 0, 0, 0), 0)) } | |
| + end | |
| + end | |
| + | |
| + def test_at_with_datetime_returns_local_time | |
| + with_env_tz 'US/Eastern' do | |
| + dt = DateTime.civil(2000, 1, 1, 0, 0, 0, '+0') | |
| + assert_equal Time.local(1999, 12, 31, 19, 0, 0), Time.at(dt) | |
| + assert_equal 'EST', Time.at(dt).zone | |
| + assert_equal(-18000, Time.at(dt).utc_offset) | |
| + | |
| + # Daylight savings | |
| + dt = DateTime.civil(2000, 7, 1, 1, 0, 0, '+1') | |
| + assert_equal Time.local(2000, 6, 30, 20, 0, 0), Time.at(dt) | |
| + assert_equal 'EDT', Time.at(dt).zone | |
| + assert_equal(-14400, Time.at(dt).utc_offset) | |
| + end | |
| + end | |
| + | |
| + def test_at_with_time_with_zone | |
| + assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC'])) | |
| + | |
| + # Only test this if the underlying Time.at raises a TypeError | |
| + begin | |
| + Time.at_without_coercion(Time.now, 0) | |
| + rescue TypeError | |
| + assert_raise(TypeError) { assert_equal(Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC']), 0)) } | |
| + end | |
| + end | |
| + | |
| + def test_at_with_time_with_zone_returns_local_time | |
| + with_env_tz 'US/Eastern' do | |
| + twz = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['London']) | |
| + assert_equal Time.local(1999, 12, 31, 19, 0, 0), Time.at(twz) | |
| + assert_equal 'EST', Time.at(twz).zone | |
| + assert_equal(-18000, Time.at(twz).utc_offset) | |
| + | |
| + # Daylight savings | |
| + twz = ActiveSupport::TimeWithZone.new(Time.utc(2000, 7, 1, 0, 0, 0), ActiveSupport::TimeZone['London']) | |
| + assert_equal Time.local(2000, 6, 30, 20, 0, 0), Time.at(twz) | |
| + assert_equal 'EDT', Time.at(twz).zone | |
| + assert_equal(-14400, Time.at(twz).utc_offset) | |
| + end | |
| + end | |
| + | |
| + def test_at_with_time_microsecond_precision | |
| + assert_equal Time.at(Time.utc(2000, 1, 1, 0, 0, 0, 111)).to_f, Time.utc(2000, 1, 1, 0, 0, 0, 111).to_f | |
| + end | |
| + | |
| + def test_at_with_utc_time | |
| + with_env_tz 'US/Eastern' do | |
| + assert_equal Time.utc(2000), Time.at(Time.utc(2000)) | |
| + assert_equal 'UTC', Time.at(Time.utc(2000)).zone | |
| + assert_equal(0, Time.at(Time.utc(2000)).utc_offset) | |
| + end | |
| + end | |
| + | |
| + def test_at_with_local_time | |
| + with_env_tz 'US/Eastern' do | |
| + assert_equal Time.local(2000), Time.at(Time.local(2000)) | |
| + assert_equal 'EST', Time.at(Time.local(2000)).zone | |
| + assert_equal(-18000, Time.at(Time.local(2000)).utc_offset) | |
| + | |
| + assert_equal Time.local(2000, 7, 1), Time.at(Time.local(2000, 7, 1)) | |
| + assert_equal 'EDT', Time.at(Time.local(2000, 7, 1)).zone | |
| + assert_equal(-14400, Time.at(Time.local(2000, 7, 1)).utc_offset) | |
| + end | |
| + end | |
| + | |
| def test_eql? | |
| assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) ) | |
| assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) | |
| diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb | |
| index e4e13c3..72366f9 100644 | |
| --- a/activesupport/test/json/encoding_test.rb | |
| +++ b/activesupport/test/json/encoding_test.rb | |
| @@ -100,11 +100,11 @@ def test_hash_encoding | |
| def test_utf8_string_encoded_properly_when_kcode_is_utf8 | |
| with_kcode 'UTF8' do | |
| result = ActiveSupport::JSON.encode('€2.99') | |
| - assert_equal '"€2.99"', result | |
| + assert_equal '"\\u20ac2.99"', result | |
| assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding) | |
| result = ActiveSupport::JSON.encode('✎☺') | |
| - assert_equal '"✎☺"', result | |
| + assert_equal '"\\u270e\\u263a"', result | |
| assert_equal(Encoding::UTF_8, result.encoding) if result.respond_to?(:encoding) | |
| end | |
| end | |
| @@ -113,22 +113,23 @@ def test_utf8_string_encoded_properly_when_kcode_is_utf8 | |
| def test_non_utf8_string_transcodes | |
| s = '二'.encode('Shift_JIS') | |
| result = ActiveSupport::JSON.encode(s) | |
| - assert_equal '"二"', result | |
| + assert_equal '"\\u4e8c"', result | |
| assert_equal Encoding::UTF_8, result.encoding | |
| end | |
| - end | |
| - def test_wide_utf8_chars | |
| - w = '𠜎' | |
| - result = ActiveSupport::JSON.encode(w) | |
| - assert_equal '"𠜎"', result | |
| - end | |
| + def test_utf8_hash_key_does_not_change_the_encoding | |
| + w = { '𠜎' => 'a' } | |
| + result = ActiveSupport::JSON.encode(w) | |
| + assert_equal '{"\\u070e":"a"}', result | |
| - def test_wide_utf8_roundtrip | |
| - hash = { :string => "𐒑" } | |
| - json = ActiveSupport::JSON.encode(hash) | |
| - decoded_hash = ActiveSupport::JSON.decode(json) | |
| - assert_equal "𐒑", decoded_hash['string'] | |
| + if RUBY_VERSION >= '2.0' | |
| + expected_encoding = Encoding::UTF_8 | |
| + else | |
| + expected_encoding = Encoding::US_ASCII | |
| + end | |
| + | |
| + assert_equal expected_encoding, result.encoding | |
| + end | |
| end | |
| def test_exception_raised_when_encoding_circular_reference_in_array | |
| diff --git a/activesupport/test/tagged_logging_test.rb b/activesupport/test/tagged_logging_test.rb | |
| index 7253eb1..89417ac 100644 | |
| --- a/activesupport/test/tagged_logging_test.rb | |
| +++ b/activesupport/test/tagged_logging_test.rb | |
| @@ -4,14 +4,24 @@ | |
| class TaggedLoggingTest < ActiveSupport::TestCase | |
| class MyLogger < ::Logger | |
| + attr_accessor :last_message | |
| + attr_accessor :last_progname | |
| + | |
| def flush(*) | |
| info "[FLUSHED]" | |
| end | |
| + | |
| + def add(severity, message = nil, progname = nil, &block) | |
| + @last_message = message | |
| + @last_progname = progname | |
| + super(severity, message, progname, &block) | |
| + end | |
| end | |
| setup do | |
| @output = StringIO.new | |
| - @logger = ActiveSupport::TaggedLogging.new(MyLogger.new(@output)) | |
| + @my_logger = MyLogger.new(@output) | |
| + @logger = ActiveSupport::TaggedLogging.new(@my_logger) | |
| end | |
| test "tagged once" do | |
| @@ -56,6 +66,10 @@ def flush(*) | |
| assert_equal "[BCX] Funky time\n", @output.string | |
| end | |
| + test "correctly answers responds_to_missing? for methods on logger instance" do | |
| + assert @logger.respond_to?(:debug?) | |
| + end | |
| + | |
| test "tagged once with blank and nil" do | |
| @logger.tagged(nil, "", "New") { @logger.info "Funky time" } | |
| assert_equal "[New] Funky time\n", @output.string | |
| diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb | |
| index 8ecfc1e..bd4bfca 100644 | |
| --- a/activesupport/test/time_zone_test.rb | |
| +++ b/activesupport/test/time_zone_test.rb | |
| @@ -198,6 +198,62 @@ def test_parse_with_incomplete_date | |
| assert_equal Time.utc(1999,12,31,19), twz.time | |
| end | |
| + def test_parse_should_not_black_out_system_timezone_dst_jump | |
| + with_env_tz('EET') do | |
| + zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)'] | |
| + twz = zone.parse('2012-03-25 03:29:00') | |
| + assert_equal [0, 29, 3, 25, 3, 2012], twz.to_a[0,6] | |
| + end | |
| + end | |
| + | |
| + def test_parse_should_black_out_app_timezone_dst_jump | |
| + with_env_tz('EET') do | |
| + zone = ActiveSupport::TimeZone['Pacific Time (US & Canada)'] | |
| + twz = zone.parse('2012-03-11 02:29:00') | |
| + assert_equal [0, 29, 3, 11, 3, 2012], twz.to_a[0,6] | |
| + end | |
| + end | |
| + | |
| + def test_parse_with_javascript_date | |
| + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] | |
| + twz = zone.parse("Mon May 28 2012 00:00:00 GMT-0700 (PDT)") | |
| + assert_equal Time.utc(2012, 5, 28, 7, 0, 0), twz.utc | |
| + end | |
| + | |
| + def test_parse_with_missing_time_components | |
| + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] | |
| + zone.stubs(:now).returns zone.local(1999, 12, 31, 12, 59, 59) | |
| + twz = zone.parse('2012-12-01') | |
| + assert_equal Time.utc(2012, 12, 1), twz.time | |
| + end | |
| + | |
| + def test_parse_doesnt_use_local_dst | |
| + with_env_tz 'US/Eastern' do | |
| + zone = ActiveSupport::TimeZone['UTC'] | |
| + twz = zone.parse('2013-03-10 02:00:00') | |
| + assert_equal Time.utc(2013, 3, 10, 2, 0, 0), twz.time | |
| + end | |
| + end | |
| + | |
| + def test_parse_handles_dst_jump | |
| + with_env_tz 'US/Eastern' do | |
| + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] | |
| + twz = zone.parse('2013-03-10 02:00:00') | |
| + assert_equal Time.utc(2013, 3, 10, 3, 0, 0), twz.time | |
| + end | |
| + end | |
| + | |
| + def test_parse_with_fractional_seconds | |
| + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] | |
| + twz = zone.parse('2013-03-13 00:00:00.000001') | |
| + assert_equal 1, twz.usec | |
| + | |
| + if twz.respond_to?(:nsec) | |
| + twz = zone.parse('2013-03-13 00:00:00.000000001') | |
| + assert_equal 1, twz.nsec | |
| + end | |
| + end | |
| + | |
| def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize | |
| tzinfo = TZInfo::Timezone.get('America/New_York') | |
| zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo) | |
| diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb | |
| index ec81ada..dfe2c41 100644 | |
| --- a/activesupport/test/xml_mini/jdom_engine_test.rb | |
| +++ b/activesupport/test/xml_mini/jdom_engine_test.rb | |
| @@ -42,7 +42,7 @@ def test_not_allowed_to_expand_entities_to_files | |
| assert_equal 'x', Hash.from_xml(attack_xml)["member"] | |
| end | |
| - def test_not_allowed_to_expand_parameter_entities_to_files | |
| + def test_not_allowed_to_expand_parameter_entities_to_files | |
| attack_xml = <<-EOT | |
| <!DOCTYPE member [ | |
| <!ENTITY % b SYSTEM "file://#{FILES_DIR}/jdom_entities.txt"> | |
| diff --git a/rails.gemspec b/rails.gemspec | |
| index 3377b4e..a5da042 100644 | |
| --- a/rails.gemspec | |
| +++ b/rails.gemspec | |
| @@ -7,6 +7,8 @@ Gem::Specification.new do |s| | |
| s.summary = 'Full-stack web application framework.' | |
| s.description = 'Ruby on Rails is a full-stack web framework optimized for programmer happiness and sustainable productivity. It encourages beautiful code by favoring convention over configuration.' | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.required_rubygems_version = ">= 1.3.6" | |
| diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md | |
| index 8064b0b..3e075a8 100644 | |
| --- a/railties/CHANGELOG.md | |
| +++ b/railties/CHANGELOG.md | |
| @@ -1,9 +1,29 @@ | |
| -## unreleased ## | |
| +## Rails 3.2.15 (Oct 16, 2013) ## | |
| -* No changes. | |
| +* No changes. | |
| + | |
| +## Rails 3.2.14 (Jul 22, 2013) ## | |
| + | |
| +* Fix bugs that crashed `rake test:benchmark`, `rails profiler` and | |
| + `rails benchmarker`. | |
| + Fixes #4938. | |
| + Backport rails/rails-perftest#2. | |
| + | |
| + *Dmitry Vorotilin + Yves Senn* | |
| + | |
| +* Add support for runner hook. | |
| + | |
| + Backport #7695. | |
| + | |
| + *Ben Holley* | |
| + | |
| +* Fixes bug with scaffold generator with `--assets=false --resource-route=false`. | |
| + Fixes #9525. | |
| + | |
| + *Arun Agrawal* | |
| -## Rails 3.2.13 (Feb 17, 2013) ## | |
| +## Rails 3.2.13 (Mar 18, 2013) ## | |
| * No changes. | |
| diff --git a/railties/guides/rails_guides.rb b/railties/guides/rails_guides.rb | |
| index feb5fe3..524299f 100644 | |
| --- a/railties/guides/rails_guides.rb | |
| +++ b/railties/guides/rails_guides.rb | |
| @@ -25,7 +25,7 @@ def bundler? | |
| begin | |
| require 'redcloth' | |
| -rescue Gem::LoadError | |
| +rescue LoadError | |
| # This can happen if doc:guides is executed in an application. | |
| $stderr.puts('Generating guides requires RedCloth 4.1.1+.') | |
| $stderr.puts(<<ERROR) if bundler? | |
| diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile | |
| index 26c95be..73f4ce3 100644 | |
| --- a/railties/guides/source/action_mailer_basics.textile | |
| +++ b/railties/guides/source/action_mailer_basics.textile | |
| @@ -480,7 +480,7 @@ As Action Mailer now uses the Mail gem, this becomes as simple as adding to your | |
| <ruby> | |
| config.action_mailer.delivery_method = :smtp | |
| config.action_mailer.smtp_settings = { | |
| - :address => "smtp.gmail.com", | |
| + :address => 'smtp.gmail.com', | |
| :port => 587, | |
| :domain => 'baci.lindsaar.net', | |
| :user_name => '<username>', | |
| diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile | |
| index 0e811a2..444215f 100644 | |
| --- a/railties/guides/source/caching_with_rails.textile | |
| +++ b/railties/guides/source/caching_with_rails.textile | |
| @@ -86,9 +86,9 @@ Or, you can set custom gzip compression level (level names are taken from +Zlib+ | |
| caches_page :image, :gzip => :best_speed | |
| </ruby> | |
| -NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. A workaround for this limitation is to include the parameters in the page's path, e.g. +/productions/page/1+. | |
| +NOTE: Page caching ignores all parameters. For example +/products?page=1+ will be written out to the filesystem as +products.html+ with no reference to the +page+ parameter. Thus, if someone requests +/products?page=2+ later, they will get the cached first page. A workaround for this limitation is to include the parameters in the products's path, e.g. +/products/page/1+. | |
| -INFO: Page caching runs in an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job. | |
| +INFO: Page caching runs as an after filter. Thus, invalid requests won't generate spurious cache entries as long as you halt them. Typically, a redirection in some before filter that checks request preconditions does the job. | |
| h4. Action Caching | |
| @@ -301,8 +301,6 @@ config.cache_store = :memory_store, :size => 64.megabytes | |
| If you're running multiple Ruby on Rails server processes (which is the case if you're using mongrel_cluster or Phusion Passenger), then your Rails server process instances won't be able to share cache data with each other. This cache store is not appropriate for large application deployments, but can work well for small, low traffic sites with only a couple of server processes or for development and test environments. | |
| -This is the default cache store implementation. | |
| - | |
| h4. ActiveSupport::Cache::FileStore | |
| This cache store uses the file system to store entries. The path to the directory where the store files will be stored must be specified when initializing the cache. | |
| @@ -315,6 +313,8 @@ With this cache store, multiple server processes on the same host can share a ca | |
| Note that the cache will grow until the disk is full unless you periodically clear out old entries. | |
| +This is the default cache store if config.cache_store is not defined and tmp/cache is writable. | |
| + | |
| h4. ActiveSupport::Cache::MemCacheStore | |
| This cache store uses Danga's +memcached+ server to provide a centralized cache for your application. Rails uses the bundled +memcache-client+ gem by default. This is currently the most popular cache store for production websites. It can be used to provide a single, shared cache cluster with very a high performance and redundancy. | |
| @@ -434,4 +434,4 @@ end | |
| h3. Further reading | |
| -* "Scaling Rails Screencasts":http://railslab.newrelic.com/scaling-rails | |
| +* "Scaling Rails Screencasts":https://www.youtube.com/playlist?list=PLuVcDOUVjW2ePvFapFSHBZ71ya2fLHZS5 | |
| diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile | |
| index c32a23c..99409ed 100644 | |
| --- a/railties/guides/source/getting_started.textile | |
| +++ b/railties/guides/source/getting_started.textile | |
| @@ -1153,6 +1153,7 @@ First, take a look at +comment.rb+: | |
| <ruby> | |
| class Comment < ActiveRecord::Base | |
| + attr_accesssible :body, :commenter, :post | |
| belongs_to :post | |
| end | |
| </ruby> | |
| @@ -1215,6 +1216,7 @@ makes each comment belong to a Post: | |
| <ruby> | |
| class Comment < ActiveRecord::Base | |
| + attr_accessible :body, :commenter, :post | |
| belongs_to :post | |
| end | |
| </ruby> | |
| diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile | |
| index 5ae9cf0..7d768fc 100644 | |
| --- a/railties/guides/source/initialization.textile | |
| +++ b/railties/guides/source/initialization.textile | |
| @@ -383,7 +383,7 @@ ensure | |
| end | |
| </ruby> | |
| -This is where the first output of the Rails initialization happens. This method creates a trap for +INT+ signals, so if you +CTRL+C+ the server, it will exit the process. As we can see from the code here, it will create the +tmp/cache+, +tmp/pids+, +tmp/sessions+ and +tmp/sockets+ directories if they don't already exist prior to calling +super+. The +super+ method will call +Rack::Server.start+ which begins its definition like this: | |
| +This is where the first output of the Rails initialization happens. This method creates a trap for +INT+ signals, so if you <tt>CTRL+C</tt> the server, it will exit the process. As we can see from the code here, it will create the +tmp/cache+, +tmp/pids+, +tmp/sessions+ and +tmp/sockets+ directories if they don't already exist prior to calling +super+. The +super+ method will call +Rack::Server.start+ which begins its definition like this: | |
| <ruby> | |
| def start | |
| diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb | |
| index 35b6fc7..4f4cedb 100644 | |
| --- a/railties/guides/source/layout.html.erb | |
| +++ b/railties/guides/source/layout.html.erb | |
| @@ -81,11 +81,11 @@ | |
| </p> | |
| <p> | |
| If you see any typos or factual errors you are confident to | |
| - patch, please clone <%= link_to 'docrails', 'https://github.com/lifo/docrails' %> | |
| - and push the change yourself. That branch of Rails has public write access. | |
| - Commits are still reviewed, but that happens after you've submitted your | |
| - contribution. <%= link_to 'docrails', 'https://github.com/lifo/docrails' %> is | |
| - cross-merged with master periodically. | |
| + patch, please clone the <%= link_to 'rails', 'https://github.com/rails/rails' %> | |
| + repository and open a new pull request. You can also ask for commit rights on | |
| + <%= link_to 'docrails', 'https://github.com/rails/docrails' %> if you plan to submit | |
| + several patches. Commits are reviewed, but that happens after you've submitted your | |
| + contribution. This repository is cross-merged with master periodically. | |
| </p> | |
| <p> | |
| You may also find incomplete content, or stuff that is not up to date. | |
| diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb | |
| index 854ac2c..2281b96 100644 | |
| --- a/railties/lib/rails/application.rb | |
| +++ b/railties/lib/rails/application.rb | |
| @@ -154,6 +154,14 @@ def load_console(app=self) | |
| self | |
| end | |
| + # Load the application runner and invoke the registered hooks. | |
| + # Check <tt>Rails::Railtie.runner</tt> for more info. | |
| + def load_runner(app=self) | |
| + initialize_runner | |
| + super | |
| + self | |
| + end | |
| + | |
| # Rails.application.env_config stores some of the Rails initial environment parameters. | |
| # Currently stores: | |
| # | |
| @@ -167,7 +175,7 @@ def load_console(app=self) | |
| # These parameters will be used by middlewares and engines to configure themselves. | |
| # | |
| def env_config | |
| - @env_config ||= super.merge({ | |
| + @app_env_config ||= super.merge({ | |
| "action_dispatch.parameter_filter" => config.filter_parameters, | |
| "action_dispatch.secret_token" => config.secret_token, | |
| "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions, | |
| @@ -305,6 +313,9 @@ def initialize_console #:nodoc: | |
| require "rails/console/helpers" | |
| end | |
| + def initialize_runner #:nodoc: | |
| + end | |
| + | |
| def build_original_fullpath(env) | |
| path_info = env["PATH_INFO"] | |
| query_string = env["QUERY_STRING"] | |
| diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb | |
| index b745b45..a2d92f8 100644 | |
| --- a/railties/lib/rails/commands/benchmarker.rb | |
| +++ b/railties/lib/rails/commands/benchmarker.rb | |
| @@ -2,9 +2,8 @@ | |
| require 'rails/test_help' | |
| require 'rails/performance_test_help' | |
| -ARGV.push('--benchmark') # HAX | |
| +ENV["BENCHMARK_TESTS"] = '1' | |
| require 'active_support/testing/performance' | |
| -ARGV.pop | |
| def options | |
| options = {} | |
| @@ -31,4 +30,5 @@ def test_#{expression.parameterize('_')} | |
| end | |
| RUBY | |
| end | |
| + ARGV.clear | |
| end | |
| diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb | |
| index 3f6966b..9b3b4f4 100644 | |
| --- a/railties/lib/rails/commands/profiler.rb | |
| +++ b/railties/lib/rails/commands/profiler.rb | |
| @@ -29,4 +29,5 @@ def test_#{expression.parameterize('_')} | |
| end | |
| RUBY | |
| end | |
| + ARGV.clear | |
| end | |
| diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb | |
| index e8cc5d9..a694218 100644 | |
| --- a/railties/lib/rails/commands/runner.rb | |
| +++ b/railties/lib/rails/commands/runner.rb | |
| @@ -42,6 +42,7 @@ | |
| require APP_PATH | |
| Rails.application.require_environment! | |
| + Rails.application.load_runner | |
| if code_or_file.nil? | |
| $stderr.puts "Run '#{$0} -h' for help." | |
| diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb | |
| index 993dfe4..77f335e 100644 | |
| --- a/railties/lib/rails/engine.rb | |
| +++ b/railties/lib/rails/engine.rb | |
| @@ -430,6 +430,11 @@ def load_console(app=self) | |
| super | |
| end | |
| + def load_runner(app=self) | |
| + railties.all { |r| r.load_runner(app) } | |
| + super | |
| + end | |
| + | |
| def eager_load! | |
| railties.all(&:eager_load!) | |
| diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb | |
| index 2a6bd57..cd9372a 100644 | |
| --- a/railties/lib/rails/generators/rails/app/app_generator.rb | |
| +++ b/railties/lib/rails/generators/rails/app/app_generator.rb | |
| @@ -103,7 +103,7 @@ def script | |
| directory "script" do |content| | |
| "#{shebang}\n" + content | |
| end | |
| - chmod "script", 0755, :verbose => false | |
| + chmod "script", 0755 & ~File.umask, :verbose => false | |
| end | |
| def test | |
| diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile | |
| index f4efd3a..8f8ebbb 100644 | |
| --- a/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile | |
| +++ b/railties/lib/rails/generators/rails/plugin_new/templates/Gemfile | |
| @@ -1,4 +1,4 @@ | |
| -source "http://rubygems.org" | |
| +source "https://rubygems.org" | |
| # Declare your gem's dependencies in <%= name %>.gemspec. | |
| # Bundler will treat runtime dependencies like base dependencies, and | |
| diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb | |
| index 03a61a0..353ebe9 100644 | |
| --- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb | |
| +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb | |
| @@ -8,6 +8,8 @@ class ScaffoldGenerator < ResourceGenerator #metagenerator | |
| class_option :stylesheets, :type => :boolean, :desc => "Generate Stylesheets" | |
| class_option :stylesheet_engine, :desc => "Engine for Stylesheets" | |
| + class_option :assets, :type => :boolean | |
| + class_option :resource_route, :type => :boolean | |
| hook_for :scaffold_controller, :required => true | |
| diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb | |
| index 07a122e..9eb7e7c 100644 | |
| --- a/railties/lib/rails/railtie.rb | |
| +++ b/railties/lib/rails/railtie.rb | |
| @@ -145,6 +145,12 @@ def console(&blk) | |
| @load_console | |
| end | |
| + def runner(&blk) | |
| + @load_runner ||= [] | |
| + @load_runner << blk if blk | |
| + @load_runner | |
| + end | |
| + | |
| def generators(&blk) | |
| @generators ||= [] | |
| @generators << blk if blk | |
| @@ -179,6 +185,10 @@ def load_console(app=self) | |
| self.class.console.each { |block| block.call(app) } | |
| end | |
| + def load_runner(app=self) | |
| + self.class.runner.each { |block| block.call(app) } | |
| + end | |
| + | |
| def load_tasks(app=self) | |
| extend Rake::DSL if defined? Rake::DSL | |
| self.class.rake_tasks.each { |block| self.instance_exec(app, &block) } | |
| diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake | |
| index 1760f52..648af72 100644 | |
| --- a/railties/lib/rails/tasks/documentation.rake | |
| +++ b/railties/lib/rails/tasks/documentation.rake | |
| @@ -120,8 +120,8 @@ namespace :doc do | |
| # desc "Generate Rails Guides" | |
| task :guides do | |
| - # FIXME: Reaching outside lib directory is a bad idea | |
| - require File.expand_path('../../../../guides/rails_guides', __FILE__) | |
| + rails_gem_dir = Gem::Specification.find_by_name("rails").gem_dir | |
| + require File.expand_path(File.join(rails_gem_dir, "railties/guides/rails_guides")) | |
| RailsGuides::Generator.new(Rails.root.join("doc/guides")).generate | |
| end | |
| diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake | |
| index 2900059..8d624c6 100644 | |
| --- a/railties/lib/rails/test_unit/testing.rake | |
| +++ b/railties/lib/rails/test_unit/testing.rake | |
| @@ -123,10 +123,13 @@ namespace :test do | |
| t.pattern = 'test/integration/**/*_test.rb' | |
| end | |
| - Rails::SubTestTask.new(:benchmark => 'test:prepare') do |t| | |
| + task 'test:benchmark_mode' do | |
| + ENV["BENCHMARK_TESTS"] = '1' | |
| + end | |
| + | |
| + Rails::SubTestTask.new(:benchmark => ['test:prepare', 'test:benchmark_mode']) do |t| | |
| t.libs << 'test' | |
| t.pattern = 'test/performance/**/*_test.rb' | |
| - t.options = '-- --benchmark' | |
| end | |
| Rails::SubTestTask.new(:profile => 'test:prepare') do |t| | |
| diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb | |
| index 0145879..64eb805 100644 | |
| --- a/railties/lib/rails/version.rb | |
| +++ b/railties/lib/rails/version.rb | |
| @@ -2,7 +2,7 @@ module Rails | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') | |
| diff --git a/railties/railties.gemspec b/railties/railties.gemspec | |
| index 6101b86..fdc4d2d 100644 | |
| --- a/railties/railties.gemspec | |
| +++ b/railties/railties.gemspec | |
| @@ -6,6 +6,9 @@ Gem::Specification.new do |s| | |
| s.version = version | |
| s.summary = 'Tools for creating, working with, and running Rails applications.' | |
| s.description = 'Rails internals: application bootup, plugins, generators, and rake tasks.' | |
| + | |
| + s.license = 'MIT' | |
| + | |
| s.required_ruby_version = '>= 1.8.7' | |
| s.author = 'David Heinemeier Hansson' | |
| diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb | |
| index da291f0..126f483 100644 | |
| --- a/railties/test/application/middleware/remote_ip_test.rb | |
| +++ b/railties/test/application/middleware/remote_ip_test.rb | |
| @@ -46,6 +46,16 @@ def remote_ip(env = {}) | |
| end | |
| end | |
| + test "works with both headers individually" do | |
| + make_basic_app | |
| + assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do | |
| + assert_equal "1.1.1.1", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1") | |
| + end | |
| + assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do | |
| + assert_equal "1.1.1.2", remote_ip("HTTP_CLIENT_IP" => "1.1.1.2") | |
| + end | |
| + end | |
| + | |
| test "can disable IP spoofing check" do | |
| make_basic_app do |app| | |
| app.config.action_dispatch.ip_spoofing_check = false | |
| diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb | |
| index ab9084d..767a60f 100644 | |
| --- a/railties/test/application/rake_test.rb | |
| +++ b/railties/test/application/rake_test.rb | |
| @@ -122,6 +122,18 @@ def test_loading_specific_fixtures | |
| assert_equal 0, ::AppTemplate::Application::User.count | |
| end | |
| + def test_loading_only_yml_fixtures | |
| + Dir.chdir(app_path) do | |
| + `rake db:migrate` | |
| + end | |
| + | |
| + app_file "test/fixtures/products.csv", "" | |
| + | |
| + require "#{rails_root}/config/environment" | |
| + errormsg = Dir.chdir(app_path) { `rake db:fixtures:load` } | |
| + assert $?.success?, errormsg | |
| + end | |
| + | |
| def test_scaffold_tests_pass_by_default | |
| content = Dir.chdir(app_path) do | |
| `rails generate scaffold user username:string password:string` | |
| @@ -162,5 +174,27 @@ def test_copy_templates | |
| end | |
| end | |
| end | |
| + | |
| + def test_load_activerecord_base_when_we_use_observers | |
| + Dir.chdir(app_path) do | |
| + `bundle exec rails g model user; | |
| + bundle exec rake db:migrate; | |
| + bundle exec rails g observer user;` | |
| + | |
| + add_to_config "config.active_record.observers = :user_observer" | |
| + | |
| + assert_equal "0", `bundle exec rails r "puts User.count"`.strip | |
| + | |
| + app_file "lib/tasks/count_user.rake", <<-RUBY | |
| + namespace :user do | |
| + task :count => :environment do | |
| + puts User.count | |
| + end | |
| + end | |
| + RUBY | |
| + | |
| + assert_equal "0", `bundle exec rake user:count`.strip | |
| + end | |
| + end | |
| end | |
| end | |
| diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb | |
| index e50d744..7d6033c 100644 | |
| --- a/railties/test/application/routing_test.rb | |
| +++ b/railties/test/application/routing_test.rb | |
| @@ -229,6 +229,30 @@ def baz | |
| end | |
| end | |
| + def test_root_path | |
| + app('development') | |
| + | |
| + controller :foo, <<-RUBY | |
| + class FooController < ApplicationController | |
| + def index | |
| + render :text => "foo" | |
| + end | |
| + end | |
| + RUBY | |
| + | |
| + app_file 'config/routes.rb', <<-RUBY | |
| + AppTemplate::Application.routes.draw do | |
| + get 'foo', :to => 'foo#index' | |
| + root :to => 'foo#index' | |
| + end | |
| + RUBY | |
| + | |
| + remove_file 'public/index.html' | |
| + | |
| + get '/' | |
| + assert_equal 'foo', last_response.body | |
| + end | |
| + | |
| test 'routes are added and removed when reloading' do | |
| app('development') | |
| @@ -300,6 +324,51 @@ def index | |
| end | |
| end | |
| + test 'named routes are cleared when reloading' do | |
| + app('development') | |
| + | |
| + controller :foo, <<-RUBY | |
| + class FooController < ApplicationController | |
| + def index | |
| + render :text => "foo" | |
| + end | |
| + end | |
| + RUBY | |
| + | |
| + controller :bar, <<-RUBY | |
| + class BarController < ApplicationController | |
| + def index | |
| + render :text => "bar" | |
| + end | |
| + end | |
| + RUBY | |
| + | |
| + app_file 'config/routes.rb', <<-RUBY | |
| + Rails.application.routes.draw do | |
| + get ':locale/foo', :to => 'foo#index', :as => 'foo' | |
| + end | |
| + RUBY | |
| + | |
| + get '/en/foo' | |
| + assert_equal 'foo', last_response.body | |
| + assert_equal '/en/foo', Rails.application.routes.url_helpers.foo_path(:locale => 'en') | |
| + | |
| + app_file 'config/routes.rb', <<-RUBY | |
| + Rails.application.routes.draw do | |
| + get ':locale/bar', :to => 'bar#index', :as => 'foo' | |
| + end | |
| + RUBY | |
| + | |
| + Rails.application.reload_routes! | |
| + | |
| + get '/en/foo' | |
| + assert_equal 404, last_response.status | |
| + | |
| + get '/en/bar' | |
| + assert_equal 'bar', last_response.body | |
| + assert_equal '/en/bar', Rails.application.routes.url_helpers.foo_path(:locale => 'en') | |
| + end | |
| + | |
| test 'resource routing with irregular inflection' do | |
| app_file 'config/initializers/inflection.rb', <<-RUBY | |
| ActiveSupport::Inflector.inflections do |inflect| | |
| diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb | |
| index 4468fa2..d086f13 100644 | |
| --- a/railties/test/application/runner_test.rb | |
| +++ b/railties/test/application/runner_test.rb | |
| @@ -57,5 +57,15 @@ def test_should_set_dollar_program_name_to_file | |
| assert_match "script/program_name.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/program_name.rb"` } | |
| end | |
| + | |
| + def test_with_hook | |
| + add_to_config <<-RUBY | |
| + runner do |app| | |
| + app.config.ran = true | |
| + end | |
| + RUBY | |
| + | |
| + assert_match "true", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.application.config.ran"` } | |
| + end | |
| end | |
| end | |
| diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb | |
| index 5891af5..86f0962 100644 | |
| --- a/railties/test/generators/scaffold_generator_test.rb | |
| +++ b/railties/test/generators/scaffold_generator_test.rb | |
| @@ -269,13 +269,27 @@ def test_scaffold_generator_on_revoke_does_not_mutilate_legacy_map_parameter | |
| assert_file "config/routes.rb", /\.routes\.draw do\s*\|map\|\s*$/ | |
| end | |
| - def test_scaffold_generator_no_assets | |
| + def test_scaffold_generator_no_assets_with_switch_no_assets | |
| run_generator [ "posts", "--no-assets" ] | |
| assert_file "app/assets/stylesheets/scaffold.css" | |
| assert_no_file "app/assets/javascripts/posts.js" | |
| assert_no_file "app/assets/stylesheets/posts.css" | |
| end | |
| + def test_scaffold_generator_no_assets_with_switch_assets_false | |
| + run_generator [ "posts", "--assets=false" ] | |
| + assert_file "app/assets/stylesheets/scaffold.css" | |
| + assert_no_file "app/assets/javascripts/posts.js" | |
| + assert_no_file "app/assets/stylesheets/posts.css" | |
| + end | |
| + | |
| + def test_scaffold_generator_no_assets_with_switch_resource_route_false | |
| + run_generator [ "posts", "--resource-route=false" ] | |
| + assert_file "config/routes.rb" do |route| | |
| + assert_no_match(/resources :posts$/, route) | |
| + end | |
| + end | |
| + | |
| def test_scaffold_generator_no_stylesheets | |
| run_generator [ "posts", "--no-stylesheets" ] | |
| assert_no_file "app/assets/stylesheets/scaffold.css" | |
| diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb | |
| index 55f85c7..335a74a 100644 | |
| --- a/railties/test/railties/railtie_test.rb | |
| +++ b/railties/test/railties/railtie_test.rb | |
| @@ -163,6 +163,22 @@ class MyTie < Rails::Railtie | |
| assert $ran_block | |
| end | |
| + test "runner block is executed when MyApp.load_runner is called" do | |
| + $ran_block = false | |
| + | |
| + class MyTie < Rails::Railtie | |
| + runner do | |
| + $ran_block = true | |
| + end | |
| + end | |
| + | |
| + require "#{app_path}/config/environment" | |
| + | |
| + assert !$ran_block | |
| + AppTemplate::Application.load_runner | |
| + assert $ran_block | |
| + end | |
| + | |
| test "railtie can add initializers" do | |
| $ran_block = false | |
| diff --git a/version.rb b/version.rb | |
| index 0145879..64eb805 100644 | |
| --- a/version.rb | |
| +++ b/version.rb | |
| @@ -2,7 +2,7 @@ module Rails | |
| module VERSION #:nodoc: | |
| MAJOR = 3 | |
| MINOR = 2 | |
| - TINY = 13 | |
| + TINY = 15 | |
| PRE = nil | |
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment