diff options
author | Xavier Noria <fxn@hashref.com> | 2010-05-04 19:36:26 +0200 |
---|---|---|
committer | Xavier Noria <fxn@hashref.com> | 2010-05-04 19:36:26 +0200 |
commit | 583b60d109522907020700225f1c739737297a2d (patch) | |
tree | a36d986cbbb73c94d217cbe86c9af7ef97a89567 | |
parent | 44a98967676492995d19fd4d541dbc9d52bf6b53 (diff) | |
parent | 0dd3b4630fea4bd4d4010b7096c9ee79d34c4501 (diff) | |
download | rails-583b60d109522907020700225f1c739737297a2d.tar.gz rails-583b60d109522907020700225f1c739737297a2d.tar.bz2 rails-583b60d109522907020700225f1c739737297a2d.zip |
Merge remote branch 'rails/master'
61 files changed, 412 insertions, 248 deletions
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index efef5da6d7..88298966eb 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -354,7 +354,7 @@ module ActionMailer #:nodoc: # end # end def receive(raw_mail) - ActiveSupport::Notifications.instrument("action_mailer.receive") do |payload| + ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload| mail = Mail.new(raw_mail) set_payload_for_mail(payload, mail) new.receive(mail) @@ -366,7 +366,7 @@ module ActionMailer #:nodoc: # when you call <tt>:deliver</tt> on the Mail::Message, calling +deliver_mail+ directly # and passing a Mail::Message will do nothing except tell the logger you sent the email. def deliver_mail(mail) #:nodoc: - ActiveSupport::Notifications.instrument("action_mailer.deliver") do |payload| + ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload| self.set_payload_for_mail(payload, mail) yield # Let Mail do the delivery actions end @@ -566,8 +566,13 @@ module ActionMailer #:nodoc: content_type = headers[:content_type] parts_order = headers[:parts_order] + # Call all the procs (if any) + default_values = self.class.default.merge(self.class.default) do |k,v| + v.respond_to?(:call) ? v.bind(self).call : v + end + # Handle defaults - headers = headers.reverse_merge(self.class.default) + headers = headers.reverse_merge(default_values) headers[:subject] ||= default_i18n_subject # Apply charset at the beginning so all fields are properly quoted diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 8e69073009..5506d62d6a 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -113,6 +113,23 @@ class BaseTest < ActiveSupport::TestCase end end end + + class ProcMailer < ActionMailer::Base + default :to => 'system@test.lindsaar.net', + 'X-Proc-Method' => Proc.new { Time.now.to_i.to_s }, + :subject => Proc.new { give_a_greeting } + + def welcome + mail + end + + private + + def give_a_greeting + "Thanks for signing up this afternoon" + end + + end test "method call to mail does not raise error" do assert_nothing_raised { BaseMailer.welcome } @@ -560,6 +577,19 @@ class BaseTest < ActiveSupport::TestCase MyInterceptor.expects(:delivering_email).with(mail) mail.deliver end + + test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do + mail1 = ProcMailer.welcome + yesterday = 1.day.ago + Time.stubs(:now).returns(yesterday) + mail2 = ProcMailer.welcome + assert(mail1['X-Proc-Method'].to_s.to_i > mail2['X-Proc-Method'].to_s.to_i) + end + + test "we can call other defined methods on the class as needed" do + mail = ProcMailer.welcome + assert_equal("Thanks for signing up this afternoon", mail.subject) + end protected diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index 473a2fe214..460273dac1 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -99,7 +99,7 @@ module ActionController #:nodoc: end def instrument_fragment_cache(name, key) - ActiveSupport::Notifications.instrument("action_controller.#{name}", :key => key){ yield } + ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield } end end end diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index cefd1f48c0..4f7a5d3f55 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -109,7 +109,7 @@ module ActionController #:nodoc: end def instrument_page_cache(name, path) - ActiveSupport::Notifications.instrument("action_controller.#{name}", :path => path){ yield } + ActiveSupport::Notifications.instrument("#{name}.action_controller", :path => path){ yield } end end diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index d69de65f28..ba38b186d6 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -23,9 +23,9 @@ module ActionController :path => (request.fullpath rescue "unknown") } - ActiveSupport::Notifications.instrument("action_controller.start_processing", raw_payload.dup) + ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup) - ActiveSupport::Notifications.instrument("action_controller.process_action", raw_payload) do |payload| + ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload| result = super payload[:status] = response.status append_info_to_payload(payload) @@ -42,20 +42,20 @@ module ActionController end def send_file(path, options={}) - ActiveSupport::Notifications.instrument("action_controller.send_file", + ActiveSupport::Notifications.instrument("send_file.action_controller", options.merge(:path => path)) do super end end def send_data(data, options = {}) - ActiveSupport::Notifications.instrument("action_controller.send_data", options) do + ActiveSupport::Notifications.instrument("send_data.action_controller", options) do super end end def redirect_to(*args) - ActiveSupport::Notifications.instrument("action_controller.redirect_to") do |payload| + ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload| result = super payload[:status] = self.status payload[:location] = self.location diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 2b9b34961e..34499fa784 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -16,12 +16,12 @@ module ActionController @templates = Hash.new(0) @layouts = Hash.new(0) - ActiveSupport::Notifications.subscribe("action_view.render_template") do |name, start, finish, id, payload| + ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload| path = payload[:layout] @layouts[path] += 1 end - ActiveSupport::Notifications.subscribe("action_view.render_template!") do |name, start, finish, id, payload| + ActiveSupport::Notifications.subscribe("!render_template.action_view") do |name, start, finish, id, payload| path = payload[:virtual_path] next unless path partial = path =~ /^.*\/_[^\/]*$/ @@ -36,8 +36,8 @@ module ActionController end def teardown_subscriptions - ActiveSupport::Notifications.unsubscribe("action_view.render_template") - ActiveSupport::Notifications.unsubscribe("action_view.render_template!") + ActiveSupport::Notifications.unsubscribe("render_template.action_view") + ActiveSupport::Notifications.unsubscribe("!render_template.action_view") end # Asserts that the request was rendered with the appropriate template file or partials @@ -57,7 +57,8 @@ module ActionController validate_request! case options - when NilClass, String + when NilClass, String, Symbol + options = options.to_s if Symbol === options rendered = @templates msg = build_message(message, "expecting <?> but rendering with <?>", diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index d3604925e8..6e26ae6c29 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -1165,13 +1165,13 @@ module ActionView # submit button label, otherwise, it uses "Update Post". # # Those labels can be customized using I18n, under the helpers.submit key and accept - # the {{model}} as translation interpolation: + # the %{model} as translation interpolation: # # en: # helpers: # submit: - # create: "Create a {{model}}" - # update: "Confirm changes to {{model}}" + # create: "Create a %{model}" + # update: "Confirm changes to %{model}" # # It also searches for a key specific for the given object: # @@ -1179,7 +1179,7 @@ module ActionView # helpers: # submit: # post: - # create: "Add {{model}}" + # create: "Add %{model}" # def submit(value=nil, options={}) value, options = nil, value if value.is_a?(Hash) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 89c1b4a275..086ad261c8 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -20,7 +20,7 @@ module ActionView options[:raise] = true translation = I18n.translate(scope_key_by_partial(key), options) translation = (translation.respond_to?(:join) ? translation.join : translation) - if html_safe_translation_key? key + if html_safe_translation_key?(key) translation.html_safe else translation @@ -53,12 +53,8 @@ module ActionView end def html_safe_translation_key?(key) - last_key = if key.is_a? Array - key.last - else - key.to_s.split('.').last - end - (last_key == "html") || (last_key.ends_with? "_html") + key = key.is_a?(Array) ? key.last : key.to_s + key =~ /(\b|_|\.)html$/ end end end diff --git a/actionpack/lib/action_view/locale/en.yml b/actionpack/lib/action_view/locale/en.yml index 187e010e30..9004e52c5b 100644 --- a/actionpack/lib/action_view/locale/en.yml +++ b/actionpack/lib/action_view/locale/en.yml @@ -102,37 +102,37 @@ half_a_minute: "half a minute" less_than_x_seconds: one: "less than 1 second" - other: "less than {{count}} seconds" + other: "less than %{count} seconds" x_seconds: one: "1 second" - other: "{{count}} seconds" + other: "%{count} seconds" less_than_x_minutes: one: "less than a minute" - other: "less than {{count}} minutes" + other: "less than %{count} minutes" x_minutes: one: "1 minute" - other: "{{count}} minutes" + other: "%{count} minutes" about_x_hours: one: "about 1 hour" - other: "about {{count}} hours" + other: "about %{count} hours" x_days: one: "1 day" - other: "{{count}} days" + other: "%{count} days" about_x_months: one: "about 1 month" - other: "about {{count}} months" + other: "about %{count} months" x_months: one: "1 month" - other: "{{count}} months" + other: "%{count} months" about_x_years: one: "about 1 year" - other: "about {{count}} years" + other: "about %{count} years" over_x_years: one: "over 1 year" - other: "over {{count}} years" + other: "over %{count} years" almost_x_years: one: "almost 1 year" - other: "almost {{count}} years" + other: "almost %{count} years" prompts: year: "Year" month: "Month" @@ -148,7 +148,7 @@ # Default translation keys for submit FormHelper submit: - create: 'Create {{model}}' - update: 'Update {{model}}' - submit: 'Save {{model}}' + create: 'Create %{model}' + update: 'Update %{model}' + submit: 'Save %{model}' diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 4d23d55687..974345633c 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -211,12 +211,12 @@ module ActionView identifier = ((@template = find_template) ? @template.identifier : @path) if @collection - ActiveSupport::Notifications.instrument("action_view.render_collection", + ActiveSupport::Notifications.instrument("render_collection.action_view", :identifier => identifier || "collection", :count => @collection.size) do render_collection end else - content = ActiveSupport::Notifications.instrument("action_view.render_partial", + content = ActiveSupport::Notifications.instrument("render_partial.action_view", :identifier => identifier) do render_partial end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 492326964a..4198013f57 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -52,7 +52,7 @@ module ActionView locals = options[:locals] || {} layout = find_layout(layout) if layout - ActiveSupport::Notifications.instrument("action_view.render_template", + ActiveSupport::Notifications.instrument("render_template.action_view", :identifier => template.identifier, :layout => layout.try(:virtual_path)) do content = template.render(self, locals) { |*name| _layout_for(*name) } diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 3c0cd35359..a1a970e2d2 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -41,7 +41,7 @@ module ActionView def render(view, locals, &block) # Notice that we use a bang in this instrumentation because you don't want to # consume this in production. This is only slow if it's being listened to. - ActiveSupport::Notifications.instrument("action_view.render_template!", :virtual_path => @virtual_path) do + ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do method_name = compile(locals, view) view.send(method_name, locals, &block) end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index 5222ffa89c..a947d746e3 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -21,17 +21,18 @@ module ActionView super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}") end end + class Template # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a # bunch of intimate details and uses it to report a very precise exception message. class Error < ActionViewError #:nodoc: SOURCE_CODE_RADIUS = 3 - attr_reader :original_exception + attr_reader :original_exception, :backtrace def initialize(template, assigns, original_exception) @template, @assigns, @original_exception = template, assigns.dup, original_exception - @backtrace = compute_backtrace + @backtrace = original_exception.backtrace end def file_name @@ -42,14 +43,6 @@ module ActionView ActiveSupport::Deprecation.silence { original_exception.message } end - def clean_backtrace - if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) - Rails.backtrace_cleaner.clean(original_exception.backtrace) - else - original_exception.backtrace - end - end - def sub_template_message if @sub_templates "Trace of template inclusion: " + @@ -87,29 +80,16 @@ module ActionView @line_number ||= if file_name regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - - $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } + $1 if message =~ regexp || backtrace.find { |line| line =~ regexp } end end def to_s - "\n#{self.class} (#{message}) #{source_location}:\n" + - "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" - end - - # don't do anything nontrivial here. Any raised exception from here becomes fatal - # (and can't be rescued). - def backtrace - @backtrace + "\n#{self.class} (#{message}) #{source_location}:\n\n" + + "#{source_extract(4)}\n #{backtrace.join("\n ")}\n\n" end private - def compute_backtrace - [ - "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + - clean_backtrace.join("\n ") - ] - end def source_location if line_number diff --git a/actionpack/test/lib/fixture_template.rb b/actionpack/lib/action_view/testing/resolvers.rb index b49ccd39ca..578c56c6c4 100644 --- a/actionpack/test/lib/fixture_template.rb +++ b/actionpack/lib/action_view/testing/resolvers.rb @@ -1,4 +1,10 @@ +require 'action_view/template/resolver' + module ActionView #:nodoc: + # Use FixtureResolver in your tests to simulate the presence of files on the + # file system. This is used internally by Rails' own test suite, and is + # useful for testing extensions that have no way of knowing what the file + # system will look like at runtime. class FixtureResolver < PathResolver attr_reader :hash @@ -24,6 +30,14 @@ module ActionView #:nodoc: templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size } end + end + class NullResolver < ActionView::PathResolver + def query(path, exts, formats) + handler, format = extract_handler_and_format(path, formats) + [ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format)] + end end -end
\ No newline at end of file + +end + diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index f3ff258016..89ba0990f1 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -16,8 +16,8 @@ require 'test/unit' require 'abstract_controller' require 'action_controller' require 'action_view' +require 'action_view/testing/resolvers' require 'action_dispatch' -require 'fixture_template' require 'active_support/dependencies' require 'active_model' diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 1741b58f72..765e111226 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -344,25 +344,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase end end - def test_assert_template_with_partial - get :partial - assert_template :partial => '_partial' - end - - def test_assert_template_with_nil - get :nothing - assert_template nil - end - - def test_assert_template_with_string - get :hello_world - assert_template 'hello_world' - end - - def test_assert_template_with_symbol - get :hello_world - assert_template :hello_world - end # check if we were rendered by a file-based template? def test_rendered_action @@ -387,7 +368,6 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase assert_nil @response.redirect_url end - # check server errors def test_server_error_response_code process :response500 @@ -538,6 +518,52 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase end end +class AssertTemplateTest < ActionController::TestCase + tests ActionPackAssertionsController + + def test_with_partial + get :partial + assert_template :partial => '_partial' + end + + def test_with_nil_passes_when_no_template_rendered + get :nothing + assert_template nil + end + + def test_with_nil_fails_when_template_rendered + get :hello_world + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template nil + end + end + + def test_passes_with_correct_string + get :hello_world + assert_template 'hello_world' + assert_template 'test/hello_world' + end + + def test_passes_with_correct_symbol + get :hello_world + assert_template :hello_world + end + + def test_fails_with_incorrect_string + get :hello_world + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template 'hello_planet' + end + end + + def test_fails_with_incorrect_symbol + get :hello_world + assert_raise(ActiveSupport::TestCase::Assertion) do + assert_template :hello_planet + end + end +end + class ActionPackHeaderTest < ActionController::TestCase tests ActionPackAssertionsController diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 48be7571ea..165c61ffad 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -10,8 +10,6 @@ ActionView::Template::register_template_handler :mab, ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ] -require "fixture_template" - class LayoutTest < ActionController::Base def self.controller_path; 'views' end def self._implied_layout_name; to_s.underscore.gsub(/_controller$/, '') ; end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 47ac911540..2234fbece9 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -33,11 +33,11 @@ class FormHelperTest < ActionView::TestCase I18n.backend.store_translations 'submit', { :helpers => { :submit => { - :create => 'Create {{model}}', - :update => 'Confirm {{model}} changes', + :create => 'Create %{model}', + :update => 'Confirm %{model} changes', :submit => 'Save changes', :another_post => { - :update => 'Update your {{model}}' + :update => 'Update your %{model}' } } } diff --git a/actionpack/test/template/testing/fixture_resolver_test.rb b/actionpack/test/template/testing/fixture_resolver_test.rb new file mode 100644 index 0000000000..de83540468 --- /dev/null +++ b/actionpack/test/template/testing/fixture_resolver_test.rb @@ -0,0 +1,18 @@ +require 'abstract_unit' + +class FixtureResolverTest < ActiveSupport::TestCase + def test_should_return_empty_list_for_unknown_path + resolver = ActionView::FixtureResolver.new() + templates = resolver.find_all("path", "arbitrary", false, {:locale => [], :formats => [:html], :handlers => []}) + assert_equal [], templates, "expected an empty list of templates" + end + + def test_should_return_template_for_declared_path + resolver = ActionView::FixtureResolver.new("arbitrary/path" => "this text") + templates = resolver.find_all("path", "arbitrary", false, {:locale => [], :formats => [:html], :handlers => []}) + assert_equal 1, templates.size, "expected one template" + assert_equal "this text", templates.first.source + assert_equal "arbitrary/path", templates.first.virtual_path + assert_equal [:html], templates.first.formats + end +end diff --git a/actionpack/test/template/testing/null_resolver_test.rb b/actionpack/test/template/testing/null_resolver_test.rb new file mode 100644 index 0000000000..e142506e6a --- /dev/null +++ b/actionpack/test/template/testing/null_resolver_test.rb @@ -0,0 +1,12 @@ +require 'abstract_unit' + +class NullResolverTest < ActiveSupport::TestCase + def test_should_return_template_for_any_path + resolver = ActionView::NullResolver.new() + templates = resolver.find_all("path", "arbitrary", false, {:locale => [], :formats => [:html], :handlers => []}) + assert_equal 1, templates.size, "expected one template" + assert_equal "Template generated by Null Resolver", templates.first.source + assert_equal "arbitrary/path", templates.first.virtual_path + assert_equal [:html], templates.first.formats + end +end diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index e6c86c7843..14afc5265f 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -223,7 +223,7 @@ module ActiveModel else attr_name = attribute.to_s.gsub('.', '_').humanize attr_name = @base.class.human_attribute_name(attribute, :default => attr_name) - options = { :default => "{{attribute}} {{message}}", :attribute => attr_name } + options = { :default => "%{attribute} %{message}", :attribute => attr_name } messages.each do |m| full_messages << I18n.t(:"errors.format", options.merge(:message => m)) diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index d05c04967c..602a530dc0 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -1,7 +1,7 @@ en: errors: # The default format use in full error messages. - format: "{{attribute}} {{message}}" + format: "%{attribute} %{message}" # The values :model, :attribute and :value are always available for interpolation # The value :count is available when applicable. Can be used for pluralization. @@ -13,15 +13,15 @@ en: accepted: "must be accepted" empty: "can't be empty" blank: "can't be blank" - too_long: "is too long (maximum is {{count}} characters)" - too_short: "is too short (minimum is {{count}} characters)" - wrong_length: "is the wrong length (should be {{count}} characters)" + too_long: "is too long (maximum is %{count} characters)" + too_short: "is too short (minimum is %{count} characters)" + wrong_length: "is the wrong length (should be %{count} characters)" not_a_number: "is not a number" not_an_integer: "must be an integer" - greater_than: "must be greater than {{count}}" - greater_than_or_equal_to: "must be greater than or equal to {{count}}" - equal_to: "must be equal to {{count}}" - less_than: "must be less than {{count}}" - less_than_or_equal_to: "must be less than or equal to {{count}}" + greater_than: "must be greater than %{count}" + greater_than_or_equal_to: "must be greater than or equal to %{count}" + equal_to: "must be equal to %{count}" + less_than: "must be less than %{count}" + less_than_or_equal_to: "must be less than or equal to %{count}" odd: "must be odd" even: "must be even" diff --git a/activemodel/lib/active_model/validations/exclusion.rb b/activemodel/lib/active_model/validations/exclusion.rb index 7ee718cf3c..2f51edfa9a 100644 --- a/activemodel/lib/active_model/validations/exclusion.rb +++ b/activemodel/lib/active_model/validations/exclusion.rb @@ -18,7 +18,7 @@ module ActiveModel # class Person < ActiveRecord::Base # validates_exclusion_of :username, :in => %w( admin superuser ), :message => "You don't belong here" # validates_exclusion_of :age, :in => 30..60, :message => "This site is only for under 30 and over 60" - # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension {{value}} is not allowed" + # validates_exclusion_of :format, :in => %w( mov avi ), :message => "extension %{value} is not allowed" # end # # Configuration options: diff --git a/activemodel/lib/active_model/validations/inclusion.rb b/activemodel/lib/active_model/validations/inclusion.rb index c1838bb93e..96dc8b6e15 100644 --- a/activemodel/lib/active_model/validations/inclusion.rb +++ b/activemodel/lib/active_model/validations/inclusion.rb @@ -18,7 +18,7 @@ module ActiveModel # class Person < ActiveRecord::Base # validates_inclusion_of :gender, :in => %w( m f ) # validates_inclusion_of :age, :in => 0..99 - # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension {{value}} is not included in the list" + # validates_inclusion_of :format, :in => %w( jpg gif png ), :message => "extension %{value} is not included in the list" # end # # Configuration options: diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb index 9ceb75487f..95da3e93ea 100644 --- a/activemodel/lib/active_model/validations/length.rb +++ b/activemodel/lib/active_model/validations/length.rb @@ -74,9 +74,9 @@ module ActiveModel # * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>. # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation. # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation. - # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is {{count}} characters)"). - # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is {{count}} characters)"). - # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be {{count}} characters)"). + # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %{count} characters)"). + # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %{count} characters)"). + # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %{count} characters)"). # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message. # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>). # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index 5260162a58..6866bfcf24 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -13,7 +13,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_method_true # When the method returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => :condition_is_true ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -22,7 +22,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_method_true # When the method returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => :condition_is_true ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert !t.errors[:title].any? @@ -30,7 +30,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_method_false # When the method returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => :condition_is_true_but_its_not ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -38,7 +38,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_method_false # When the method returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => :condition_is_true_but_its_not ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -47,7 +47,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_string_true # When the evaluated string returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => "a = 1; a == 1" ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -56,7 +56,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_string_true # When the evaluated string returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => "a = 1; a == 1" ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -64,7 +64,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_string_false # When the evaluated string returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false") + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => "false") t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -72,7 +72,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_string_false # When the evaluated string returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false") + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => "false") t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -81,7 +81,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_block_true # When the block returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => Proc.new { |r| r.content.size > 4 } ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? @@ -91,7 +91,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_block_true # When the block returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => Proc.new { |r| r.content.size > 4 } ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert t.valid? @@ -100,7 +100,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_block_false # When the block returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => Proc.new { |r| r.title != "uhohuhoh"} ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert t.valid? @@ -109,7 +109,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_block_false # When the block returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => Proc.new { |r| r.title != "uhohuhoh"} ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb index 7d851f546c..fffd290fa3 100644 --- a/activemodel/test/cases/validations/exclusion_validation_test.rb +++ b/activemodel/test/cases/validations/exclusion_validation_test.rb @@ -20,7 +20,7 @@ class ExclusionValidationTest < ActiveModel::TestCase end def test_validates_exclusion_of_with_formatted_message - Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" ) + Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %{value} is restricted" ) assert Topic.create("title" => "something", "content" => "abc") diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb index e10089208a..1aa6e30f6b 100644 --- a/activemodel/test/cases/validations/format_validation_test.rb +++ b/activemodel/test/cases/validations/format_validation_test.rb @@ -67,7 +67,7 @@ class PresenceValidationTest < ActiveModel::TestCase end def test_validate_format_with_formatted_message - Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}") + Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be %{value}") t = Topic.create(:title => 'Invalid title') assert_equal ["can't be Invalid title"], t.errors[:title] end diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb index 6116ef71d4..3a644c92c9 100644 --- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb @@ -15,7 +15,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_inclusion_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :inclusion, :default => 'custom message %{value}', :value => 'title') end # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) @@ -24,7 +24,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_exclusion_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :exclusion, :default => 'custom message %{value}', :value => 'title') end # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) @@ -33,7 +33,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_invalid_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :invalid, :default => 'custom message %{value}', :value => 'title') end # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) @@ -78,7 +78,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_too_long_with_custom_message - assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10) + assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_long, :default => 'custom message %{count}', :count => 10) end # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) @@ -87,7 +87,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_too_short_with_custom_message - assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10) + assert_equal 'custom message 10', @person.errors.generate_message(:title, :too_short, :default => 'custom message %{count}', :count => 10) end # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) @@ -96,7 +96,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_wrong_length_with_custom_message - assert_equal 'custom message 10', @person.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10) + assert_equal 'custom message 10', @person.errors.generate_message(:title, :wrong_length, :default => 'custom message %{count}', :count => 10) end # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) @@ -105,7 +105,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase end def test_generate_message_not_a_number_with_custom_message - assert_equal 'custom message title', @person.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title') + assert_equal 'custom message title', @person.errors.generate_message(:title, :not_a_number, :default => 'custom message %{value}', :value => 'title') end # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 1b4c1699ae..d65d94d599 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -58,7 +58,7 @@ class I18nValidationTest < ActiveModel::TestCase end def test_errors_full_messages_uses_format - I18n.backend.store_translations('en', :errors => {:format => "Field {{attribute}} {{message}}"}) + I18n.backend.store_translations('en', :errors => {:format => "Field %{attribute} %{message}"}) @person.errors.add('name', 'empty') assert_equal ["Field Name empty"], @person.errors.full_messages end diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb index 6b2bcd9c60..45ff0175d1 100644 --- a/activemodel/test/cases/validations/inclusion_validation_test.rb +++ b/activemodel/test/cases/validations/inclusion_validation_test.rb @@ -44,7 +44,7 @@ class InclusionValidationTest < ActiveModel::TestCase end def test_validates_inclusion_of_with_formatted_message - Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" ) + Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %{value} is not in the list" ) assert Topic.create("title" => "a", "content" => "abc").valid? diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index 99d0268b67..254e823b7c 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -138,7 +138,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_optionally_validates_length_of_using_within_on_create - Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}" + Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: %{count}" t = Topic.create("title" => "thisisnotvalid", "content" => "whatever") assert !t.save @@ -159,7 +159,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_optionally_validates_length_of_using_within_on_update - Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}" + Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: %{count}" t = Topic.create("title" => "vali", "content" => "whatever") assert !t.save @@ -230,7 +230,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_minimum_with_message - Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" ) + Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %{count}" ) t = Topic.create("title" => "uhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -238,7 +238,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_minimum_with_too_short - Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" ) + Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %{count}" ) t = Topic.create("title" => "uhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -246,7 +246,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_maximum_with_message - Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" ) + Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %{count}" ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -254,7 +254,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_in - Topic.validates_length_of(:title, :in => 10..20, :message => "hoo {{count}}") + Topic.validates_length_of(:title, :in => 10..20, :message => "hoo %{count}") t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -267,7 +267,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_maximum_with_too_long - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" ) + Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}" ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -275,7 +275,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_is_with_message - Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" ) + Topic.validates_length_of( :title, :is=>5, :message=>"boo %{count}" ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -283,7 +283,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_is_with_wrong_length - Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" ) + Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %{count}" ) t = Topic.create("title" => "uhohuhoh", "content" => "whatever") assert !t.valid? assert t.errors[:title].any? @@ -349,7 +349,7 @@ class LengthValidationTest < ActiveModel::TestCase def test_optionally_validates_length_of_using_within_on_create_utf8 with_kcode('UTF8') do - Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}" + Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: %{count}" t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever") assert !t.save @@ -372,7 +372,7 @@ class LengthValidationTest < ActiveModel::TestCase def test_optionally_validates_length_of_using_within_on_update_utf8 with_kcode('UTF8') do - Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}" + Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: %{count}" t = Topic.create("title" => "一二三4", "content" => "whatever") assert !t.save @@ -407,7 +407,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_with_block - Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.", + Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least %{count} words.", :tokenizer => lambda {|str| str.scan(/\w+/) } t = Topic.create!(:content => "this content should be long enough") assert t.valid? diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 38b3f87e93..1e73744649 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -126,13 +126,13 @@ class NumericalityValidationTest < ActiveModel::TestCase end def test_validates_numericality_with_numeric_message - Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}" + Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than %{count}" topic = Topic.new("title" => "numeric test", "approved" => 10) assert !topic.valid? assert_equal ["smaller than 4"], topic.errors[:approved] - Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}" + Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than %{count}" topic = Topic.new("title" => "numeric test", "approved" => 1) assert !topic.valid? diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 98c14e6eb0..7ebeb6079e 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -17,8 +17,13 @@ module ActiveRecord # * (-) <tt>create</tt> # * (5) <tt>after_create</tt> # * (6) <tt>after_save</tt> + # * (7) <tt>after_commit</tt> # - # That's a total of eight callbacks, which gives you immense power to react and prepare for each state in the + # Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued. + # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and + # <tt>after_rollback</tt>. + # + # That's a total of ten callbacks, which gives you immense power to react and prepare for each state in the # Active Record lifecycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar, except that each # <tt>_on_create</tt> callback is replaced by the corresponding <tt>_on_update</tt> callback. # diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index 2f36bec764..2493095a04 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -10,8 +10,8 @@ module ActiveRecord ## # :singleton-method: # The connection handler - cattr_accessor :connection_handler, :instance_writer => false - @@connection_handler = ConnectionAdapters::ConnectionHandler.new + class_inheritable_accessor :connection_handler, :instance_writer => false + self.connection_handler = ConnectionAdapters::ConnectionHandler.new # Returns the connection currently associated with the class. This can # also be used to "borrow" the connection to do database work that isn't @@ -54,7 +54,7 @@ module ActiveRecord raise AdapterNotSpecified unless defined?(Rails.env) establish_connection(Rails.env) when ConnectionSpecification - @@connection_handler.establish_connection(name, spec) + self.connection_handler.establish_connection(name, spec) when Symbol, String if configuration = configurations[spec.to_s] establish_connection(configuration) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 3c560903f7..533a7bb8e6 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -76,7 +76,7 @@ module ActiveRecord def cache_sql(sql) result = if @query_cache.has_key?(sql) - ActiveSupport::Notifications.instrument("active_record.sql", + ActiveSupport::Notifications.instrument("sql.active_record", :sql => sql, :name => "CACHE", :connection_id => self.object_id) @query_cache[sql] else diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 578297029b..28a59c1e62 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -197,7 +197,7 @@ module ActiveRecord def log(sql, name) name ||= "SQL" result = nil - ActiveSupport::Notifications.instrument("active_record.sql", + ActiveSupport::Notifications.instrument("sql.active_record", :sql => sql, :name => name, :connection_id => self.object_id) do @runtime += Benchmark.ms { result = yield } end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 74fed4ad62..6389094b8a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -490,12 +490,8 @@ module ActiveRecord execute "ROLLBACK" end - if defined?(PGconn::PQTRANS_IDLE) - # The ruby-pg driver supports inspecting the transaction status, - # while the ruby-postgres driver does not. - def outside_transaction? - @connection.transaction_status == PGconn::PQTRANS_IDLE - end + def outside_transaction? + @connection.transaction_status == PGconn::PQTRANS_IDLE end def create_savepoint diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 0bc49c1daa..4bf33c3856 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -516,7 +516,7 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) # Cap primary key sequences to max(pk). if connection.respond_to?(:reset_pk_sequence!) table_names.each do |table_name| - connection.reset_pk_sequence!(table_name) + connection.reset_pk_sequence!(table_name.tr('/', '_')) end end end diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index 810359fef3..9d5cb54180 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -9,7 +9,7 @@ en: errors: messages: taken: "has already been taken" - record_invalid: "Validation failed: {{errors}}" + record_invalid: "Validation failed: %{errors}" # Append your own errors here or at the model/attributes scope. # You can define own errors for models or model attributes. @@ -18,7 +18,7 @@ en: # For example, # models: # user: - # blank: "This is a custom blank message for {{model}}: {{attribute}}" + # blank: "This is a custom blank message for %{model}: %{attribute}" # attributes: # login: # blank: "This is a custom blank message for User login" diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 796dd99f02..1a195fbb81 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -31,10 +31,10 @@ module ActiveRecord # mary.deposit(100) # end # - # This example will only take money from David and give to Mary if neither - # +withdrawal+ nor +deposit+ raises an exception. Exceptions will force a - # ROLLBACK that returns the database to the state before the transaction was - # begun. Be aware, though, that the objects will _not_ have their instance + # This example will only take money from David and give it to Mary if neither + # +withdrawal+ nor +deposit+ raise an exception. Exceptions will force a + # ROLLBACK that returns the database to the state before the transaction + # began. Be aware, though, that the objects will _not_ have their instance # data returned to their pre-transactional state. # # == Different Active Record classes in a single transaction @@ -44,16 +44,16 @@ module ActiveRecord # that class. This is because transactions are per-database connection, not # per-model. # - # In this example a <tt>Balance</tt> record is transactionally saved even - # though <tt>transaction</tt> is called on the <tt>Account</tt> class: + # In this example a +balance+ record is transactionally saved even + # though +transaction+ is called on the +Account+ class: # # Account.transaction do # balance.save! # account.save! # end # - # Note that the +transaction+ method is also available as a model instance - # method. For example, you can also do this: + # The +transaction+ method is also available as a model instance method. + # For example, you can also do this: # # balance.transaction do # balance.save! @@ -62,9 +62,9 @@ module ActiveRecord # # == Transactions are not distributed across database connections # - # A transaction acts on a single database connection. If you have + # A transaction acts on a single database connection. If you have # multiple class-specific databases, the transaction will not protect - # interaction among them. One workaround is to begin a transaction + # interaction among them. One workaround is to begin a transaction # on each class whose models you alter: # # Student.transaction do @@ -74,16 +74,22 @@ module ActiveRecord # end # end # - # This is a poor solution, but full distributed transactions are beyond + # This is a poor solution, but fully distributed transactions are beyond # the scope of Active Record. # - # == Save and destroy are automatically wrapped in a transaction + # == +save+ and +destroy+ are automatically wrapped in a transaction # - # Both Base#save and Base#destroy come wrapped in a transaction that ensures - # that whatever you do in validations or callbacks will happen under the - # protected cover of a transaction. So you can use validations to check for - # values that the transaction depends on or you can raise exceptions in the - # callbacks to rollback, including <tt>after_*</tt> callbacks. + # Both +save+ and +destroy+ come wrapped in a transaction that ensures + # that whatever you do in validations or callbacks will happen under its + # protected cover. So you can use validations to check for values that + # the transaction depends on or you can raise exceptions in the callbacks + # to rollback, including <tt>after_*</tt> callbacks. + # + # As a consequence changes to the database are not seen outside your connection + # until the operation is complete. For example, if you try to update the index + # of a search engine in +after_save+ the indexer won't see the updated record. + # The +after_commit+ callback is the only one that is triggered once the update + # is committed. See below. # # == Exception handling and rolling back # @@ -91,14 +97,14 @@ module ActiveRecord # be propagated (after triggering the ROLLBACK), so you should be ready to # catch those in your application code. # - # One exception is the ActiveRecord::Rollback exception, which will trigger + # One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger # a ROLLBACK when raised, but not be re-raised by the transaction block. # - # *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions - # inside a transaction block. StatementInvalid exceptions indicate that an + # *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions + # inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an # error occurred at the database level, for example when a unique constraint # is violated. On some database systems, such as PostgreSQL, database errors - # inside a transaction causes the entire transaction to become unusable + # inside a transaction cause the entire transaction to become unusable # until it's restarted from the beginning. Here is an example which # demonstrates the problem: # @@ -120,11 +126,12 @@ module ActiveRecord # # ignored until end of transaction block" # end # - # One should restart the entire transaction if a StatementError occurred. + # One should restart the entire transaction if an + # <tt>ActiveRecord::StatementInvalid</tt> occurred. # # == Nested transactions # - # #transaction calls can be nested. By default, this makes all database + # +transaction+ calls can be nested. By default, this makes all database # statements in the nested transaction block become part of the parent # transaction. For example: # @@ -139,7 +146,7 @@ module ActiveRecord # User.find(:all) # => empty # # It is also possible to requires a sub-transaction by passing - # <tt>:requires_new => true</tt>. If anything goes wrong, the + # <tt>:requires_new => true</tt>. If anything goes wrong, the # database rolls back to the beginning of the sub-transaction # without rolling back the parent transaction. For example: # diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb index 15730c2a87..8ee2a5868c 100644 --- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -15,7 +15,7 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase end def test_generate_message_invalid_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') + assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message %{value}', :value => 'title') end # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) @@ -24,7 +24,7 @@ class I18nGenerateMessageValidationTest < ActiveRecord::TestCase end def test_generate_message_taken_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title') + assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message %{value}', :value => 'title') end # ActiveRecord#RecordInvalid exception diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb index 2f0ccd7dae..b7befe110d 100644 --- a/activeresource/lib/active_resource/connection.rb +++ b/activeresource/lib/active_resource/connection.rb @@ -106,7 +106,7 @@ module ActiveResource private # Makes a request to the remote service. def request(method, path, *arguments) - result = ActiveSupport::Notifications.instrument("active_resource.request") do |payload| + result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload| payload[:method] = method payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}" payload[:result] = http.send(method, path, *arguments) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index f24a1b1c6c..0652a20035 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [beta 4/release candidate] (unreleased)* +* Deprecate {{}} as interpolation syntax for I18n in favor of %{} [José Valim] + * Array#to_xml is more powerful and able to handle the same types as Hash#to_xml #4490 [Neeraj Singh] * Harmonize the caching API and refactor the backends. #4452 [Brian Durand] diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index 0fea84a6ef..cfd85f61c9 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.has_rdoc = true - s.add_dependency('i18n', '~> 0.4.0.beta') + s.add_dependency('i18n', '~> 0.4.0.beta1') s.add_dependency('tzinfo', '~> 0.3.16') s.add_dependency('builder', '~> 2.1.2') s.add_dependency('memcache-client', '>= 1.7.5') diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index ec5007c284..2605a3f2b8 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -502,7 +502,7 @@ module ActiveSupport if self.class.instrument payload = { :key => key } payload.merge!(options) if options.is_a?(Hash) - ActiveSupport::Notifications.instrument("active_support.cache_#{operation}", payload){ yield } + ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield } else yield end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 9d2ad2bbcf..fef49e1003 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -7,12 +7,12 @@ class Date class << self # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). def yesterday - ::Date.today.yesterday + ::Date.current.yesterday end # Returns a new Date representing the date 1 day after today (i.e. tomorrow's date). def tomorrow - ::Date.today.tomorrow + ::Date.current.tomorrow end # Returns Time.zone.today when config.time_zone is set, otherwise just returns Date.today. diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 90ab1eb281..13ef703f49 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,5 +1,6 @@ require 'date' require 'active_support/inflector' +require 'active_support/core_ext/time/calculations' class Date DATE_FORMATS = { diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index 5ec87372d0..2344bb1bb3 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -24,8 +24,9 @@ module ActiveSupport # # Store the transliterations in locales/de.yml # i18n: # transliterate: - # ü: "ue" - # ö: "oe" + # rule: + # ü: "ue" + # ö: "oe" # # # Or set them using Ruby # I18n.backend.store_translations(:de, :i18n => { diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index e692f6d142..02c233595d 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'bigdecimal' require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/big_decimal/conversions' # for #to_s require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/module/delegation' @@ -178,6 +179,14 @@ class Numeric end class BigDecimal + # A BigDecimal would be naturally represented as a JSON number. Most libraries, + # however, parse non-integer JSON numbers directly as floats. Clients using + # those libraries would get in general a wrong number and no way to recover + # other than manually inspecting the string with the JSON code itself. + # + # That's why a JSON string is returned. The JSON literal is not numeric, but if + # the other end knows by contract that the data is supposed to be a BigDecimal, + # it still has the chance to post-process the string and get the real value. def as_json(options = nil) to_s end #:nodoc: end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb index f3d877efe7..7e89402822 100644 --- a/activesupport/lib/active_support/notifications/instrumenter.rb +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -12,12 +12,18 @@ module ActiveSupport end # Instrument the given block by measuring the time taken to execute it - # and publish it. + # and publish it. Notice that events get sent even if an error occurs + # in the passed-in block def instrument(name, payload={}) time = Time.now - result = yield(payload) if block_given? - @notifier.publish(name, time, Time.now, @id, payload) - result + begin + yield(payload) if block_given? + rescue Exception => e + payload[:exception] = [e.class.name, e.message] + raise e + ensure + @notifier.publish(name, time, Time.now, @id, payload) + end end private diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 23c9bc7fb1..c403d7fb11 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -176,11 +176,47 @@ class DateExtCalculationsTest < Test::Unit::TestCase end def test_yesterday_constructor - assert_equal Date.today - 1, Date.yesterday + assert_equal Date.current - 1, Date.yesterday + end + + def test_yesterday_constructor_when_zone_default_is_not_set + with_env_tz 'UTC' do + with_tz_default do + Time.stubs(:now).returns Time.local(2000, 1, 1) + assert_equal Date.new(1999, 12, 31), Date.yesterday + end + end + end + + def test_yesterday_constructor_when_zone_default_is_set + with_env_tz 'UTC' do + with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5 + Time.stubs(:now).returns Time.local(2000, 1, 1) + assert_equal Date.new(1999, 12, 30), Date.yesterday + end + end end def test_tomorrow_constructor - assert_equal Date.today + 1, Date.tomorrow + assert_equal Date.current + 1, Date.tomorrow + end + + def test_tomorrow_constructor_when_zone_default_is_not_set + with_env_tz 'UTC' do + with_tz_default do + Time.stubs(:now).returns Time.local(1999, 12, 31) + assert_equal Date.new(2000, 1, 1), Date.tomorrow + end + end + end + + def test_tomorrow_constructor_when_zone_default_is_set + with_env_tz 'UTC' do + with_tz_default ActiveSupport::TimeZone['Europe/Paris'] do # UTC +1 + Time.stubs(:now).returns Time.local(1999, 12, 31, 23) + assert_equal Date.new(2000, 1, 2), Date.tomorrow + end + end end def test_since @@ -264,6 +300,14 @@ class DateExtCalculationsTest < Test::Unit::TestCase ensure old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') end + + def with_tz_default(tz = nil) + old_tz = Time.zone_default + Time.zone_default = tz + yield + ensure + Time.zone_default = old_tz + end end class DateExtBehaviorTest < Test::Unit::TestCase diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index ac7ca96c4d..a8ecf4e4cf 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -1,5 +1,7 @@ # encoding: utf-8 require 'abstract_unit' +require 'bigdecimal' +require 'active_support/core_ext/big_decimal/conversions' require 'active_support/json' class TestJSONEncoding < Test::Unit::TestCase @@ -26,7 +28,7 @@ class TestJSONEncoding < Test::Unit::TestCase NilTests = [[ nil, %(null) ]] NumericTests = [[ 1, %(1) ], [ 2.5, %(2.5) ], - [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s('F')}") ]] + [ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]] StringTests = [[ 'this is the <string>', %("this is the \\u003Cstring\\u003E")], [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index 2fba62bdd6..684b931176 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -1,4 +1,12 @@ require 'abstract_unit' + +begin + require 'openssl' + OpenSSL::Digest::SHA1 +rescue LoadError, NameError + $stderr.puts "Skipping MessageEncryptor test: broken OpenSSL install" +else + require 'active_support/time' class MessageEncryptorTest < Test::Unit::TestCase @@ -45,3 +53,5 @@ class MessageEncryptorTest < Test::Unit::TestCase ActiveSupport::Base64.encode64s(bits) end end + +end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index c2e1c588f0..e11de5f67a 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -168,7 +168,7 @@ module Notifications assert_equal Hash[:payload => "notifications"], @events.last.payload end - def test_instrument_does_not_publish_when_exception_is_raised + def test_instrument_publishes_when_exception_is_raised begin instrument(:awesome, :payload => "notifications") do raise "FAIL" @@ -178,7 +178,9 @@ module Notifications end drain - assert_equal 0, @events.size + assert_equal 1, @events.size + assert_equal Hash[:payload => "notifications", + :exception => ["RuntimeError", "FAIL"]], @events.last.payload end def test_event_is_pushed_even_without_block diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile index 126a6efff5..97915d5d55 100644 --- a/railties/guides/source/activerecord_validations_callbacks.textile +++ b/railties/guides/source/activerecord_validations_callbacks.textile @@ -234,7 +234,7 @@ This helper validates that the attributes' values are not included in a given se <ruby> class Account < ActiveRecord::Base validates_exclusion_of :subdomain, :in => %w(www), - :message => "Subdomain {{value}} is reserved." + :message => "Subdomain %{value} is reserved." end </ruby> @@ -262,7 +262,7 @@ This helper validates that the attributes' values are included in a given set. I <ruby> class Coffee < ActiveRecord::Base validates_inclusion_of :size, :in => %w(small medium large), - :message => "{{value}} is not a valid size" + :message => "%{value} is not a valid size" end </ruby> @@ -290,12 +290,12 @@ The possible length constraint options are: * +:in+ (or +:within+) - The attribute length must be included in a given interval. The value for this option must be a range. * +:is+ - The attribute length must be equal to the given value. -The default error messages depend on the type of length validation being performed. You can personalize these messages using the +:wrong_length+, +:too_long+, and +:too_short+ options and <tt>{{count}}</tt> as a placeholder for the number corresponding to the length constraint being used. You can still use the +:message+ option to specify an error message. +The default error messages depend on the type of length validation being performed. You can personalize these messages using the +:wrong_length+, +:too_long+, and +:too_short+ options and <tt>%{count}</tt> as a placeholder for the number corresponding to the length constraint being used. You can still use the +:message+ option to specify an error message. <ruby> class Person < ActiveRecord::Base validates_length_of :bio, :maximum => 1000, - :too_long => "{{count}} characters is the maximum allowed" + :too_long => "%{count} characters is the maximum allowed" end </ruby> @@ -307,8 +307,8 @@ class Essay < ActiveRecord::Base :minimum => 300, :maximum => 400, :tokenizer => lambda { |str| str.scan(/\w+/) }, - :too_short => "must have at least {{count}} words", - :too_long => "must have at most {{count}} words" + :too_short => "must have at least %{count} words", + :too_long => "must have at most %{count} words" end </ruby> @@ -337,11 +337,11 @@ end Besides +:only_integer+, the +validates_numericality_of+ helper also accepts the following options to add constraints to acceptable values: -* +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than {{count}}_". -* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to {{count}}_". -* +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to {{count}}_". -* +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than {{count}}_". -* +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to {{count}}_". +* +:greater_than+ - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than %{count}_". +* +:greater_than_or_equal_to+ - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to %{count}_". +* +:equal_to+ - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to %{count}_". +* +:less_than+ - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than %{count}_". +* +:less_than_or_equal_to+ - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less or equal to %{count}_". * +:odd+ - Specifies the value must be an odd number if set to true. The default error message for this option is "_must be odd_". * +:even+ - Specifies the value must be an even number if set to true. The default error message for this option is "_must be even_". @@ -469,7 +469,7 @@ The +:allow_nil+ option skips the validation when the value being validated is + <ruby> class Coffee < ActiveRecord::Base validates_inclusion_of :size, :in => %w(small medium large), - :message => "{{value}} is not a valid size", :allow_nil => true + :message => "%{value} is not a valid size", :allow_nil => true end </ruby> diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index dcad451e23..bb383d3cf9 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -531,7 +531,7 @@ In many cases you want to abstract your translations so that *variables can be i All options besides +:default+ and +:scope+ that are passed to +#translate+ will be interpolated to the translation: <ruby> -I18n.backend.store_translations :en, :thanks => 'Thanks {{name}}!' +I18n.backend.store_translations :en, :thanks => 'Thanks %{name}!' I18n.translate :thanks, :name => 'Jeremy' # => 'Thanks Jeremy!' </ruby> @@ -547,7 +547,7 @@ The +:count+ interpolation variable has a special role in that it both is interp <ruby> I18n.backend.store_translations :en, :inbox => { :one => '1 message', - :other => '{{count}} messages' + :other => '%{count} messages' } I18n.translate :inbox, :count => 2 # => '2 messages' @@ -711,7 +711,7 @@ h5. Error Message Interpolation The translated model name, translated attribute name, and value are always available for interpolation. -So, for example, instead of the default error message +"can not be blank"+ you could use the attribute name like this : +"Please fill in your {{attribute}}"+. +So, for example, instead of the default error message +"can not be blank"+ you could use the attribute name like this : +"Please fill in your %{attribute}"+. * +count+, where available, can be used for pluralization if present: @@ -750,8 +750,8 @@ en: errors: template: header: - one: "1 error prohibited this {{model}} from being saved" - other: "{{count}} errors prohibited this {{model}} from being saved" + one: "1 error prohibited this %{model} from being saved" + other: "%{count} errors prohibited this %{model} from being saved" body: "There were problems with the following fields:" </yaml> diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb index ee2635d4cd..bedefaa51c 100644 --- a/railties/lib/rails/backtrace_cleaner.rb +++ b/railties/lib/rails/backtrace_cleaner.rb @@ -2,29 +2,28 @@ require 'active_support/backtrace_cleaner' module Rails class BacktraceCleaner < ActiveSupport::BacktraceCleaner - ERB_METHOD_SIG = /:in `_run_erb_.*/ - APP_DIRS = %w( app config lib test ) + APP_DIRS_PATTERN = /^\/?(app|config|lib|test)/ + RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/ def initialize super add_filter { |line| line.sub("#{Rails.root}/", '') } - add_filter { |line| line.sub(ERB_METHOD_SIG, '') } + add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, '') } add_filter { |line| line.sub('./', '/') } # for tests add_gem_filters - - add_silencer { |line| !APP_DIRS.any? { |dir| line =~ /^\/?#{dir}/ } } + add_silencer { |line| line !~ APP_DIRS_PATTERN } end private def add_gem_filters - return unless defined? Gem - (Gem.path + [Gem.default_dir]).uniq.each do |path| - # http://gist.github.com/30430 - add_filter { |line| - line.sub(%r{(#{path})/gems/([^/]+)-([0-9.]+)/(.*)}, '\2 (\3) \4') - } - end + return unless defined?(Gem) + + gems_paths = (Gem.path + [Gem.default_dir]).uniq.map!{ |p| Regexp.escape(p) } + return if gems_paths.empty? + + gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w\.]+)/(.*)} + add_filter { |line| line.sub(gems_regexp, '\2 (\3) \4') } end end diff --git a/railties/lib/rails/log_subscriber.rb b/railties/lib/rails/log_subscriber.rb index 42697d2e32..145c7e0ace 100644 --- a/railties/lib/rails/log_subscriber.rb +++ b/railties/lib/rails/log_subscriber.rb @@ -22,7 +22,7 @@ module Rails # # Rails::LogSubscriber.add :active_record, ActiveRecord::Railtie::LogSubscriber.new # - # So whenever a "active_record.sql" notification arrive to Rails::LogSubscriber, + # So whenever a "sql.active_record" notification arrive to Rails::LogSubscriber, # it will properly dispatch the event (ActiveSupport::Notifications::Event) to # the sql method. # @@ -54,7 +54,7 @@ module Rails log_subscribers << log_subscriber log_subscriber.public_methods(false).each do |event| - notifier.subscribe("#{namespace}.#{event}") do |*args| + notifier.subscribe("#{event}.#{namespace}") do |*args| next if log_subscriber.logger.nil? begin @@ -105,4 +105,4 @@ module Rails "#{bold}#{color}#{text}#{CLEAR}" end end -end
\ No newline at end of file +end diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb index b99cf5bb4f..276950c78c 100644 --- a/railties/test/application/initializers/notifications_test.rb +++ b/railties/test/application/initializers/notifications_test.rb @@ -38,7 +38,7 @@ module ApplicationTests ActiveRecord::Base.logger = logger = MockLogger.new # Mimic ActiveRecord notifications - instrument "active_record.sql", :name => "SQL", :sql => "SHOW tables" + instrument "sql.active_record", :name => "SQL", :sql => "SHOW tables" wait assert_equal 1, logger.logged.size diff --git a/railties/test/log_subscriber_test.rb b/railties/test/log_subscriber_test.rb index 49288cfaa8..a3a755ae62 100644 --- a/railties/test/log_subscriber_test.rb +++ b/railties/test/log_subscriber_test.rb @@ -61,21 +61,21 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase def test_event_is_sent_to_the_registered_class Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "my_log_subscriber.some_event" + instrument "some_event.my_log_subscriber" wait - assert_equal %w(my_log_subscriber.some_event), @logger.logged(:info) + assert_equal %w(some_event.my_log_subscriber), @logger.logged(:info) end def test_event_is_an_active_support_notifications_event Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "my_log_subscriber.some_event" + instrument "some_event.my_log_subscriber" wait assert_kind_of ActiveSupport::Notifications::Event, @log_subscriber.event end def test_does_not_send_the_event_if_it_doesnt_match_the_class Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "my_log_subscriber.unknown_event" + instrument "unknown_event.my_log_subscriber" wait # If we get here, it means that NoMethodError was not raised. end @@ -84,7 +84,7 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase Rails.logger = nil @log_subscriber.expects(:some_event).never Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "my_log_subscriber.some_event" + instrument "some_event.my_log_subscriber" wait end @@ -110,14 +110,14 @@ class SyncLogSubscriberTest < ActiveSupport::TestCase def test_logging_does_not_die_on_failures Rails::LogSubscriber.add :my_log_subscriber, @log_subscriber - instrument "my_log_subscriber.puke" - instrument "my_log_subscriber.some_event" + instrument "puke.my_log_subscriber" + instrument "some_event.my_log_subscriber" wait assert_equal 1, @logger.logged(:info).size - assert_equal 'my_log_subscriber.some_event', @logger.logged(:info).last + assert_equal 'some_event.my_log_subscriber', @logger.logged(:info).last assert_equal 1, @logger.logged(:error).size - assert_equal 'Could not log "my_log_subscriber.puke" event. RuntimeError: puke', @logger.logged(:error).last + assert_equal 'Could not log "puke.my_log_subscriber" event. RuntimeError: puke', @logger.logged(:error).last end end
\ No newline at end of file |