From 45d41f0dadd9fa171f306ff356770c4492726f30 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 16:25:27 +0200 Subject: integrating I18n into Rails --- actionpack/lib/action_view.rb | 2 + .../action_view/helpers/active_record_helper.rb | 33 +- actionpack/lib/action_view/helpers/date_helper.rb | 81 ++-- .../lib/action_view/helpers/form_options_helper.rb | 52 +- .../lib/action_view/helpers/number_helper.rb | 18 +- actionpack/lib/action_view/lang/en-US.rb | 93 ++++ .../template/active_record_helper_i18n_test.rb | 47 ++ actionpack/test/template/date_helper_i18n_test.rb | 99 ++++ .../test/template/form_options_helper_i18n_test.rb | 26 + .../test/template/number_helper_i18n_test.rb | 27 ++ activerecord/lib/active_record.rb | 2 + activerecord/lib/active_record/lang/en-US.rb | 25 + activerecord/lib/active_record/validations.rb | 184 ++++--- activerecord/test/cases/validations_i18n_test.rb | 539 +++++++++++++++++++++ .../active_support/core_ext/array/conversions.rb | 14 +- activesupport/lib/active_support/vendor.rb | 7 + activesupport/lib/active_support/vendor/i18n-0.0.1 | 1 + 17 files changed, 1082 insertions(+), 168 deletions(-) create mode 100644 actionpack/lib/action_view/lang/en-US.rb create mode 100644 actionpack/test/template/active_record_helper_i18n_test.rb create mode 100644 actionpack/test/template/date_helper_i18n_test.rb create mode 100644 actionpack/test/template/form_options_helper_i18n_test.rb create mode 100644 actionpack/test/template/number_helper_i18n_test.rb create mode 100644 activerecord/lib/active_record/lang/en-US.rb create mode 100644 activerecord/test/cases/validations_i18n_test.rb create mode 160000 activesupport/lib/active_support/vendor/i18n-0.0.1 diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 973020a768..33d50a61c4 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -32,6 +32,8 @@ require 'action_view/base' require 'action_view/partials' require 'action_view/template_error' +require 'action_view/lang/en-US.rb' + ActionView::Base.class_eval do include ActionView::Partials diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index f3f204cc97..5ad9d5f76d 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -151,12 +151,17 @@ module ActionView # instance yourself and set it up. View the source of this method to see how easy it is. def error_messages_for(*params) options = params.extract_options!.symbolize_keys + if object = options.delete(:object) objects = [object].flatten else objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact end - count = objects.inject(0) {|sum, object| sum + object.errors.count } + + count = objects.inject(0) {|sum, object| sum + object.errors.count } + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + unless count.zero? html = {} [:id, :class].each do |key| @@ -168,21 +173,29 @@ module ActionView end end options[:object_name] ||= params.first - options[:header_message] = "#{pluralize(count, 'error')} prohibited this #{options[:object_name].to_s.gsub('_', ' ')} from being saved" unless options.include?(:header_message) - options[:message] ||= 'There were problems with the following fields:' unless options.include?(:message) - error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join - contents = '' - contents << content_tag(options[:header_tag] || :h2, options[:header_message]) unless options[:header_message].blank? - contents << content_tag(:p, options[:message]) unless options[:message].blank? - contents << content_tag(:ul, error_messages) + I18n.with_options :locale => locale, :scope => [:active_record, :error] do |locale| + header_message = if options.include?(:header_message) + options[:header_message] + else + object_name = options[:object_name].to_s.gsub('_', ' ') + locale.t :header_message, :count => count, :object_name => object_name + end + message = options.include?(:message) ? options[:message] : locale.t(:message) + error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join + + contents = '' + contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank? + contents << content_tag(:p, message) unless message.blank? + contents << content_tag(:ul, error_messages) - content_tag(:div, contents, html) + content_tag(:div, contents, html) + end else '' end end - + private def all_input_tags(record, record_name, options) input_block = options[:input_block] || default_input_block diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 7ed6272898..0337be0744 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -58,35 +58,43 @@ module ActionView # distance_of_time_in_words(to_time, from_time, true) # => over 6 years # distance_of_time_in_words(Time.now, Time.now) # => less than a minute # - def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false) + def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) distance_in_minutes = (((to_time - from_time).abs)/60).round distance_in_seconds = ((to_time - from_time).abs).round - case distance_in_minutes - when 0..1 - return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds - case distance_in_seconds - when 0..4 then 'less than 5 seconds' - when 5..9 then 'less than 10 seconds' - when 10..19 then 'less than 20 seconds' - when 20..39 then 'half a minute' - when 40..59 then 'less than a minute' - else '1 minute' - end + I18n.with_options :locale => locale, :scope => :'datetime.distance_in_words' do |locale| + case distance_in_minutes + when 0..1 + return distance_in_minutes == 0 ? + locale.t(:less_than_x_minutes, :count => 1) : + locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds + + case distance_in_seconds + when 0..4 then locale.t :less_than_x_seconds, :count => 5 + when 5..9 then locale.t :less_than_x_seconds, :count => 10 + when 10..19 then locale.t :less_than_x_seconds, :count => 20 + when 20..39 then locale.t :half_a_minute + when 40..59 then locale.t :less_than_x_minutes, :count => 1 + else locale.t :x_minutes, :count => 1 + end - when 2..44 then "#{distance_in_minutes} minutes" - when 45..89 then 'about 1 hour' - when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours" - when 1440..2879 then '1 day' - when 2880..43199 then "#{(distance_in_minutes / 1440).round} days" - when 43200..86399 then 'about 1 month' - when 86400..525599 then "#{(distance_in_minutes / 43200).round} months" - when 525600..1051199 then 'about 1 year' - else "over #{(distance_in_minutes / 525600).round} years" + when 2..44 then locale.t :x_minutes, :count => distance_in_minutes + when 45..89 then locale.t :about_x_hours, :count => 1 + when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round + when 1440..2879 then locale.t :x_days, :count => 1 + when 2880..43199 then locale.t :x_days, :count => (distance_in_minutes / 1440).round + when 43200..86399 then locale.t :about_x_months, :count => 1 + when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes / 43200).round + when 525600..1051199 then locale.t :about_x_years, :count => 1 + else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round + end end - end + end # Like distance_of_time_in_words, but where to_time is fixed to Time.now. # @@ -498,13 +506,19 @@ module ActionView # select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...)) # def select_month(date, options = {}, html_options = {}) + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + val = date ? (date.kind_of?(Fixnum) ? date : date.month) : '' if options[:use_hidden] hidden_html(options[:field_name] || 'month', val, options) else month_options = [] - month_names = options[:use_month_names] || (options[:use_short_month] ? Date::ABBR_MONTHNAMES : Date::MONTHNAMES) + month_names = options[:use_month_names] || begin + (options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names').t locale + end month_names.unshift(nil) if month_names.size < 13 + 1.upto(12) do |month_number| month_name = if options[:use_month_numbers] month_number @@ -522,7 +536,7 @@ module ActionView end select_html(options[:field_name] || 'month', month_options.join, options, html_options) end - end + end # Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius # can be changed using the :start_year and :end_year keys in the +options+. Both ascending and descending year @@ -612,15 +626,17 @@ module ActionView private def date_or_time_select(options, html_options = {}) + locale = options[:locale] + defaults = { :discard_type => true } options = defaults.merge(options) datetime = value(object) datetime ||= default_time_from_options(options[:default]) unless options[:include_blank] - + position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 } - order = (options[:order] ||= [:year, :month, :day]) - + order = options[:order] ||= :'date.order'.t(locale) + # Discard explicit and implicit by not being included in the :order discard = {} discard[:year] = true if options[:discard_year] or !order.include?(:year) @@ -629,19 +645,19 @@ module ActionView discard[:hour] = true if options[:discard_hour] discard[:minute] = true if options[:discard_minute] or discard[:hour] discard[:second] = true unless options[:include_seconds] && !discard[:minute] - + # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are valid # (otherwise it could be 31 and february wouldn't be a valid date) if datetime && discard[:day] && !discard[:month] datetime = datetime.change(:day => 1) end - + # Maintain valid dates by including hidden fields for discarded elements [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) } - + # Ensure proper ordering of :hour, :minute and :second [:hour, :minute, :second].each { |o| order.delete(o); order.push(o) } - + date_or_time_select = '' order.reverse.each do |param| # Send hidden fields for discarded elements once output has started @@ -656,9 +672,8 @@ module ActionView when :second then options[:include_seconds] ? " : " : "" else "" end) - end - + date_or_time_select end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index b3f8e63c1b..c782c0a816 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -276,16 +276,25 @@ module ActionView # that they will be listed above the rest of the (long) list. # # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. - def country_options_for_select(selected = nil, priority_countries = nil) + def country_options_for_select(*args) + options = args.extract_options! + + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + + selected, priority_countries = *args + countries = :'countries.names'.t options[:locale] country_options = "" if priority_countries + # TODO priority_countries need to be translated? country_options += options_for_select(priority_countries, selected) country_options += "\n" end - return country_options + options_for_select(COUNTRIES, selected) + return country_options + options_for_select(countries, selected) end + # Returns a string of option tags for pretty much any time zone in the # world. Supply a TimeZone name as +selected+ to have it marked as the @@ -340,43 +349,8 @@ module ActionView end # All the countries included in the country_options output. - COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", - "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", - "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", - "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", - "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", - "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", - "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", - "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", - "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", - "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", - "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", - "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", - "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", - "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", - "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", - "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", - "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", - "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", - "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", - "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", - "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", - "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", - "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", - "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", - "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", - "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", - "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", - "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", - "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", - "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", - "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", - "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", - "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", - "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", - "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", - "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", - "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES") + # only included for backwards compatibility, please use the I18n interface + COUNTRIES = :'countries.names'.t 'en-US' unless const_defined?("COUNTRIES") end class InstanceTag #:nodoc: diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 120bb4cc1f..9d98036f2d 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -69,13 +69,19 @@ module ActionView # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u") # # => 1234567890,50 £ def number_to_currency(number, options = {}) - options = options.stringify_keys - precision = options["precision"] || 2 - unit = options["unit"] || "$" - separator = precision > 0 ? options["separator"] || "." : "" - delimiter = options["delimiter"] || "," - format = options["format"] || "%u%n" + options = options.symbolize_keys + + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + defaults = :'currency.format'.t(locale) || {} + precision = options[:precision] || defaults[:precision] + unit = options[:unit] || defaults[:unit] + separator = options[:separator] || defaults[:separator] + delimiter = options[:delimiter] || defaults[:delimiter] + format = options[:format] || defaults[:format] + separator = '' if precision == 0 + begin parts = number_with_precision(number, precision).split('.') format.gsub(/%n/, number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s).gsub(/%u/, unit) diff --git a/actionpack/lib/action_view/lang/en-US.rb b/actionpack/lib/action_view/lang/en-US.rb new file mode 100644 index 0000000000..659f96a5f3 --- /dev/null +++ b/actionpack/lib/action_view/lang/en-US.rb @@ -0,0 +1,93 @@ +I18n.backend.add_translations :'en-US', { + :date => { + :formats => { + :default => "%Y-%m-%d", + :short => "%b %d", + :long => "%B %d, %Y", + }, + :day_names => Date::DAYNAMES, + :abbr_day_names => Date::ABBR_DAYNAMES, + :month_names => Date::MONTHNAMES, + :abbr_month_names => Date::ABBR_MONTHNAMES, + :order => [:year, :month, :day] + }, + :time => { + :formats => { + :default => "%a, %d %b %Y %H:%M:%S %z", + :short => "%d %b %H:%M", + :long => "%B %d, %Y %H:%M", + }, + :am => 'am', + :pm => 'pm' + }, + :datetime => { + :distance_in_words => { + :half_a_minute => 'half a minute', + :less_than_x_seconds => ['less than 1 second', 'less than {{count}} seconds'], + :x_seconds => ['1 second', '{{count}} seconds'], + :less_than_x_minutes => ['less than a minute', 'less than {{count}} minutes'], + :x_minutes => ['1 minute', '{{count}} minutes'], + :about_x_hours => ['about 1 hour', 'about {{count}} hours'], + :x_days => ['1 day', '{{count}} days'], + :about_x_months => ['about 1 month', 'about {{count}} months'], + :x_months => ['1 month', '{{count}} months'], + :about_x_years => ['about 1 year', 'about {{count}} year'], + :over_x_years => ['over 1 year', 'over {{count}} years'] + } + }, + :currency => { + :format => { + :unit => '$', + :precision => 2, + :separator => '.', + :delimiter => ',', + :format => '%u%n', + } + }, + :countries => { + :names => ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", + "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", + "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", + "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", + "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", + "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", + "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", + "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", + "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", + "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", + "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", + "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", + "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", + "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", + "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", + "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", + "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", + "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", + "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", + "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", + "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", + "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", + "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", + "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", + "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", + "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", + "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", + "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", + "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", + "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", + "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", + "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", + "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", + "Yemen", "Zambia", "Zimbabwe"] + }, + :active_record => { + :error => { + :header_message => ["1 error prohibited this {{object_name}} from being saved", "{{count}} errors prohibited this {{object_name}} from being saved"], + :message => "There were problems with the following fields:" + } + } +} diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb new file mode 100644 index 0000000000..057fb9bd1a --- /dev/null +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -0,0 +1,47 @@ +require 'abstract_unit' + +class ActiveRecordHelperI18nTest < Test::Unit::TestCase + include ActionView::Helpers::ActiveRecordHelper + + attr_reader :request + def setup + @request = mock + @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) + stubs(:content_tag).returns 'content_tag' + + I18n.stubs(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns "1 error prohibited this from being saved" + I18n.stubs(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' + end + + def test_error_messages_for_given_a_locale_it_does_not_check_request_for_locale + request.expects(:locale).never + @object.errors.stubs(:count).returns 0 + error_messages_for(:object => @object, :locale => 'en-US') + end + + def test_error_messages_for_given_no_locale_it_checks_request_for_locale + request.expects(:locale).returns 'en-US' + @object.errors.stubs(:count).returns 0 + error_messages_for(:object => @object) + end + + def test_error_messages_for_given_a_header_message_option_it_does_not_translate_header_message + I18n.expects(:translate).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').never + error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en-US') + end + + def test_error_messages_for_given_no_header_message_option_it_translates_header_message + I18n.expects(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns 'header message' + error_messages_for(:object => @object, :locale => 'en-US') + end + + def test_error_messages_for_given_a_message_option_it_does_not_translate_message + I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).never + error_messages_for(:object => @object, :message => 'message', :locale => 'en-US') + end + + def test_error_messages_for_given_no_message_option_it_translates_message + I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' + error_messages_for(:object => @object, :locale => 'en-US') + end +end \ No newline at end of file diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb new file mode 100644 index 0000000000..9b7c03a400 --- /dev/null +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -0,0 +1,99 @@ +require 'abstract_unit' + +class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase + include ActionView::Helpers::DateHelper + attr_reader :request + + def setup + @request = mock + @from = Time.mktime(2004, 6, 6, 21, 45, 0) + end + + # distance_of_time_in_words + + def test_distance_of_time_in_words_given_a_locale_it_does_not_check_request_for_locale + request.expects(:locale).never + distance_of_time_in_words @from, @from + 1.second, false, :locale => 'en-US' + end + + def test_distance_of_time_in_words_given_no_locale_it_checks_request_for_locale + request.expects(:locale).returns 'en-US' + distance_of_time_in_words @from, @from + 1.second + end + + def test_distance_of_time_in_words_calls_i18n + { # with include_seconds + [2.seconds, true] => [:'less_than_x_seconds', 5], + [9.seconds, true] => [:'less_than_x_seconds', 10], + [19.seconds, true] => [:'less_than_x_seconds', 20], + [30.seconds, true] => [:'half_a_minute', nil], + [59.seconds, true] => [:'less_than_x_minutes', 1], + [60.seconds, true] => [:'x_minutes', 1], + + # without include_seconds + [29.seconds, false] => [:'less_than_x_minutes', 1], + [60.seconds, false] => [:'x_minutes', 1], + [44.minutes, false] => [:'x_minutes', 44], + [61.minutes, false] => [:'about_x_hours', 1], + [24.hours, false] => [:'x_days', 1], + [30.days, false] => [:'about_x_months', 1], + [60.days, false] => [:'x_months', 2], + [1.year, false] => [:'about_x_years', 1], + [3.years, false] => [:'over_x_years', 3] + + }.each do |passed, expected| + assert_distance_of_time_in_words_translates_key passed, expected + end + end + + def assert_distance_of_time_in_words_translates_key(passed, expected) + diff, include_seconds = *passed + key, count = *expected + to = @from + diff + + options = {:locale => 'en-US', :scope => :'datetime.distance_in_words'} + options[:count] = count if count + + I18n.expects(:t).with(key, options) + distance_of_time_in_words(@from, to, include_seconds, :locale => 'en-US') + end +end + +class DateHelperSelectTagsI18nTests < Test::Unit::TestCase + include ActionView::Helpers::DateHelper + attr_reader :request + + def setup + @request = mock + I18n.stubs(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES + end + + # select_month + + def test_select_month_given_use_month_names_option_does_not_translate_monthnames + I18n.expects(:translate).never + select_month(8, :locale => 'en-US', :use_month_names => Date::MONTHNAMES) + end + + def test_select_month_translates_monthnames + I18n.expects(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES + select_month(8, :locale => 'en-US') + end + + def test_select_month_given_use_short_month_option_translates_abbr_monthnames + I18n.expects(:translate).with(:'date.abbr_month_names', 'en-US').returns Date::ABBR_MONTHNAMES + select_month(8, :locale => 'en-US', :use_short_month => true) + end + + # date_or_time_select + + def test_date_or_time_select_given_an_order_options_does_not_translate_order + I18n.expects(:translate).never + datetime_select('post', 'updated_at', :order => [:year, :month, :day], :locale => 'en-US') + end + + def test_date_or_time_select_given_no_order_options_translates_order + I18n.expects(:translate).with(:'date.order', 'en-US').returns [:year, :month, :day] + datetime_select('post', 'updated_at', :locale => 'en-US') + end +end \ No newline at end of file diff --git a/actionpack/test/template/form_options_helper_i18n_test.rb b/actionpack/test/template/form_options_helper_i18n_test.rb new file mode 100644 index 0000000000..c9fc0768bb --- /dev/null +++ b/actionpack/test/template/form_options_helper_i18n_test.rb @@ -0,0 +1,26 @@ +require 'abstract_unit' + +class FormOptionsHelperI18nTests < Test::Unit::TestCase + include ActionView::Helpers::FormOptionsHelper + attr_reader :request + + def setup + @request = mock + end + + def test_country_options_for_select_given_a_locale_it_does_not_check_request_for_locale + request.expects(:locale).never + country_options_for_select :locale => 'en-US' + end + + def test_country_options_for_select_given_no_locale_it_checks_request_for_locale + request.expects(:locale).returns 'en-US' + country_options_for_select + end + + def test_country_options_for_select_translates_country_names + countries = ActionView::Helpers::FormOptionsHelper::COUNTRIES + I18n.expects(:translate).with(:'countries.names', 'en-US').returns countries + country_options_for_select :locale => 'en-US' + end +end \ No newline at end of file diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb new file mode 100644 index 0000000000..47cb035f56 --- /dev/null +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -0,0 +1,27 @@ +require 'abstract_unit' + +class NumberHelperI18nTests < Test::Unit::TestCase + include ActionView::Helpers::NumberHelper + + attr_reader :request + def setup + @request = mock + @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} + I18n.backend.add_translations 'en-US', :currency => {:format => @defaults} + end + + def test_number_to_currency_given_a_locale_it_does_not_check_request_for_locale + request.expects(:locale).never + number_to_currency(1, :locale => 'en-US') + end + + def test_number_to_currency_given_no_locale_it_checks_request_for_locale + request.expects(:locale).returns 'en-US' + number_to_currency(1) + end + + def test_number_to_currency_translates_currency_formats + I18n.expects(:translate).with(:'currency.format', 'en-US').returns @defaults + number_to_currency(1, :locale => 'en-US') + end +end \ No newline at end of file diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index d4f7170305..b379bd26f8 100755 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -80,3 +80,5 @@ end require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' + +require 'active_record/lang/en-US.rb' diff --git a/activerecord/lib/active_record/lang/en-US.rb b/activerecord/lib/active_record/lang/en-US.rb new file mode 100644 index 0000000000..7c3bcfd85e --- /dev/null +++ b/activerecord/lib/active_record/lang/en-US.rb @@ -0,0 +1,25 @@ +I18n.backend.add_translations :'en-US', { + :active_record => { + :error_messages => { + :inclusion => "is not included in the list", + :exclusion => "is reserved", + :invalid => "is invalid", + :confirmation => "doesn't match confirmation", + :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)", + :taken => "has already been taken", + :not_a_number => "is not a number", + :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" + } + } +} \ No newline at end of file diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index c4e370d017..f54fb80137 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -23,30 +23,30 @@ module ActiveRecord @base, @errors = base, {} end - @@default_error_messages = { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is %d characters)", - :too_short => "is too short (minimum is %d characters)", - :wrong_length => "is the wrong length (should be %d characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than %d", - :greater_than_or_equal_to => "must be greater than or equal to %d", - :equal_to => "must be equal to %d", - :less_than => "must be less than %d", - :less_than_or_equal_to => "must be less than or equal to %d", - :odd => "must be odd", - :even => "must be even" - } - - # Holds a hash with all the default error messages that can be replaced by your own copy or localizations. - cattr_accessor :default_error_messages + # @@default_error_messages = { + # :inclusion => "is not included in the list", + # :exclusion => "is reserved", + # :invalid => "is invalid", + # :confirmation => "doesn't match confirmation", + # :accepted => "must be accepted", + # :empty => "can't be empty", + # :blank => "can't be blank", + # :too_long => "is too long (maximum is %d characters)", + # :too_short => "is too short (minimum is %d characters)", + # :wrong_length => "is the wrong length (should be %d characters)", + # :taken => "has already been taken", + # :not_a_number => "is not a number", + # :greater_than => "must be greater than %d", + # :greater_than_or_equal_to => "must be greater than or equal to %d", + # :equal_to => "must be equal to %d", + # :less_than => "must be less than %d", + # :less_than_or_equal_to => "must be less than or equal to %d", + # :odd => "must be odd", + # :even => "must be even" + # } + # + # # Holds a hash with all the default error messages that can be replaced by your own copy or localizations. + # cattr_accessor :default_error_messages # Adds an error to the base object instead of any particular attribute. This is used @@ -61,27 +61,34 @@ module ActiveRecord # for the same attribute and ensure that this error object returns false when asked if empty?. More than one # error can be added to the same +attribute+ in which case an array will be returned on a call to on(attribute). # If no +msg+ is supplied, "invalid" is assumed. - def add(attribute, msg = @@default_error_messages[:invalid]) - @errors[attribute.to_s] = [] if @errors[attribute.to_s].nil? - @errors[attribute.to_s] << msg - end + def add(attribute, message = nil) + message ||= :"active_record.error_messages.invalid".t + @errors[attribute.to_s] ||= [] + @errors[attribute.to_s] << message + end # Will add an error message to each of the attributes in +attributes+ that is empty. - def add_on_empty(attributes, msg = @@default_error_messages[:empty]) + def add_on_empty(attributes, custom_message = nil) for attr in [attributes].flatten value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] - is_empty = value.respond_to?("empty?") ? value.empty? : false - add(attr, msg) unless !value.nil? && !is_empty + is_empty = value.respond_to?("empty?") ? value.empty? : false + add(attr, generate_message(attr, :empty, :default => custom_message)) unless !value.nil? && !is_empty end end # Will add an error message to each of the attributes in +attributes+ that is blank (using Object#blank?). - def add_on_blank(attributes, msg = @@default_error_messages[:blank]) + def add_on_blank(attributes, custom_message = nil) for attr in [attributes].flatten value = @base.respond_to?(attr.to_s) ? @base.send(attr.to_s) : @base[attr.to_s] - add(attr, msg) if value.blank? + add(attr, generate_message(attr, :blank, :default => custom_message)) if value.blank? end end + + def generate_message(attr, key, options = {}) + scope = [:active_record, :error_messages] + key.t(options.merge(:scope => scope + [:custom, @base.class.name.downcase, attr])) || + key.t(options.merge(:scope => scope)) + end # Returns true if the specified +attribute+ has errors associated with it. # @@ -166,22 +173,25 @@ module ActiveRecord # company = Company.create(:address => '123 First St.') # company.errors.full_messages # => # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"] - def full_messages + def full_messages(options = {}) full_messages = [] + locale = options[:locale] @errors.each_key do |attr| - @errors[attr].each do |msg| - next if msg.nil? - + @errors[attr].each do |message| + next unless message + if attr == "base" - full_messages << msg + full_messages << message else - full_messages << @base.class.human_attribute_name(attr) + " " + msg + key = :"active_record.human_attribute_names.#{@base.class.name.underscore.to_sym}.#{attr}" + attr_name = key.t(locale) || @base.class.human_attribute_name(attr) + full_messages << attr_name + " " + message end end end full_messages - end + end # Returns true if no errors have been added. def empty? @@ -388,15 +398,18 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_confirmation_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save } + configuration = { :on => :save } configuration.update(attr_names.extract_options!) attr_accessor(*(attr_names.map { |n| "#{n}_confirmation" })) validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message]) unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation") + unless record.send("#{attr_name}_confirmation").nil? or value == record.send("#{attr_name}_confirmation") + message = record.errors.generate_message(attr_name, :confirmation, :default => configuration[:message]) + record.errors.add(attr_name, message) + end end - end + end # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example: # @@ -422,7 +435,7 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_acceptance_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" } + configuration = { :on => :save, :allow_nil => true, :accept => "1" } configuration.update(attr_names.extract_options!) db_cols = begin @@ -434,7 +447,10 @@ module ActiveRecord attr_accessor(*names) validates_each(attr_names,configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message]) unless value == configuration[:accept] + unless value == configuration[:accept] + message = record.errors.generate_message(attr_name, :accepted, :default => configuration[:message]) + record.errors.add(attr_name, message) + end end end @@ -461,7 +477,7 @@ module ActiveRecord # method, proc or string should return or evaluate to a true or false value. # def validates_presence_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save } + configuration = { :on => :save } configuration.update(attr_names.extract_options!) # can't use validates_each here, because it cannot cope with nonexistent attributes, @@ -505,11 +521,7 @@ module ActiveRecord # method, proc or string should return or evaluate to a true or false value. def validates_length_of(*attrs) # Merge given options with defaults. - options = { - :too_long => ActiveRecord::Errors.default_error_messages[:too_long], - :too_short => ActiveRecord::Errors.default_error_messages[:too_short], - :wrong_length => ActiveRecord::Errors.default_error_messages[:wrong_length] - }.merge(DEFAULT_VALIDATION_OPTIONS) + options = {}.merge(DEFAULT_VALIDATION_OPTIONS) options.update(attrs.extract_options!.symbolize_keys) # Ensure that one and only one range option is specified. @@ -531,15 +543,14 @@ module ActiveRecord when :within, :in raise ArgumentError, ":#{option} must be a Range" unless option_value.is_a?(Range) - too_short = options[:too_short] % option_value.begin - too_long = options[:too_long] % option_value.end - validates_each(attrs, options) do |record, attr, value| value = value.split(//) if value.kind_of?(String) if value.nil? or value.size < option_value.begin - record.errors.add(attr, too_short) + message = record.errors.generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) + record.errors.add(attr, message) elsif value.size > option_value.end - record.errors.add(attr, too_long) + message = record.errors.generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) + record.errors.add(attr, message) end end when :is, :minimum, :maximum @@ -549,11 +560,14 @@ module ActiveRecord validity_checks = { :is => "==", :minimum => ">=", :maximum => "<=" } message_options = { :is => :wrong_length, :minimum => :too_short, :maximum => :too_long } - message = (options[:message] || options[message_options[option]]) % option_value - validates_each(attrs, options) do |record, attr, value| value = value.split(//) if value.kind_of?(String) - record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value] + unless !value.nil? and value.size.method(validity_checks[option])[option_value] + key = message_options[option] + custom_message = options[:message] || options[key] + message = record.errors.generate_message(attr, key, :default => custom_message, :count => option_value) + record.errors.add(attr, message) + end end end end @@ -595,7 +609,7 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_uniqueness_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true } + configuration = { :case_sensitive => true } configuration.update(attr_names.extract_options!) validates_each(attr_names,configuration) do |record, attr_name, value| @@ -654,8 +668,11 @@ module ActiveRecord if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text? found = results.any? { |a| a[attr_name.to_s] == value } end - - record.errors.add(attr_name, configuration[:message]) if found + + if found + message = record.errors.generate_message(attr_name, :taken, :default => configuration[:message]) + record.errors.add(attr_name, message) + end end end end @@ -685,13 +702,16 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_format_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil } + configuration = { :on => :save, :with => nil } configuration.update(attr_names.extract_options!) raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp) validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message] % value) unless value.to_s =~ configuration[:with] + unless value.to_s =~ configuration[:with] + message = record.errors.generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + record.errors.add(attr_name, message) + end end end @@ -715,7 +735,7 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_inclusion_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save } + configuration = { :on => :save, :with => nil } configuration.update(attr_names.extract_options!) enum = configuration[:in] || configuration[:within] @@ -723,7 +743,10 @@ module ActiveRecord raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?") validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message] % value) unless enum.include?(value) + unless enum.include?(value) + message = record.errors.generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) + record.errors.add(attr_name, message) + end end end @@ -747,7 +770,7 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_exclusion_of(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save } + configuration = { :on => :save, :with => nil } configuration.update(attr_names.extract_options!) enum = configuration[:in] || configuration[:within] @@ -755,7 +778,10 @@ module ActiveRecord raise(ArgumentError, "An object with the method include? is required must be supplied as the :in option of the configuration hash") unless enum.respond_to?("include?") validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message] % value) if enum.include?(value) + if enum.include?(value) + message = record.errors.generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) + record.errors.add(attr_name, message) + end end end @@ -791,12 +817,14 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_associated(*attr_names) - configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save } + configuration = { :on => :save } configuration.update(attr_names.extract_options!) validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message]) unless - (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v } + unless (value.is_a?(Array) ? value : [value]).inject(true) { |v, r| (r.nil? || r.valid?) && v } + message = record.errors.generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + record.errors.add(attr_name, message) + end end end @@ -844,7 +872,8 @@ module ActiveRecord if configuration[:only_integer] unless raw_value.to_s =~ /\A[+-]?\d+\Z/ - record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number]) + message = record.errors.generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) + record.errors.add(attr_name, message) next end raw_value = raw_value.to_i @@ -852,7 +881,8 @@ module ActiveRecord begin raw_value = Kernel.Float(raw_value.to_s) rescue ArgumentError, TypeError - record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[:not_a_number]) + message = record.errors.generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) + record.errors.add(attr_name, message) next end end @@ -860,10 +890,12 @@ module ActiveRecord numericality_options.each do |option| case option when :odd, :even - record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[] + unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[] + message = record.errors.generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) + record.errors.add(attr_name, message) + end else - message = configuration[:message] || ActiveRecord::Errors.default_error_messages[option] - message = message % configuration[option] if configuration[option] + message = record.errors.generate_message(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb new file mode 100644 index 0000000000..eb454fca20 --- /dev/null +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -0,0 +1,539 @@ +require "cases/helper" +require 'models/topic' +require 'models/reply' + +class ActiveRecordValidationsI18nTests < Test::Unit::TestCase + def setup + reset_callbacks Topic + @topic = Topic.new + I18n.backend.add_translations('en-US', :active_record => {:error_messages => {:custom => nil}}) + end + + def teardown + reset_callbacks Topic + load 'active_record/lang/en-US.rb' + end + + def unique_topic + @unique ||= Topic.create :title => 'unique!' + end + + def replied_topic + @replied_topic ||= begin + topic = Topic.create(:title => "topic") + topic.replies << Reply.new + topic + end + end + + def reset_callbacks(*models) + models.each do |model| + model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + end + end + + # ActiveRecord::Errors + + def test_errors_generate_message_translates_custom_model_attribute_key + global_scope = [:active_record, :error_messages] + custom_scope = global_scope + [:custom, 'topic', :title] + + I18n.expects(:translate).with(:invalid, :scope => custom_scope).returns 'translation' + I18n.expects(:translate).with(:invalid, :scope => global_scope).never + + @topic.errors.generate_message :title, :invalid + end + + def test_errors_generate_message_given_a_custom_message_translates_custom_model_attribute_key_with_custom_message_as_default + custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] + + I18n.expects(:translate).with(:invalid, :scope => custom_scope, :default => 'default from class def').returns 'translation' + @topic.errors.generate_message :title, :invalid, :default => 'default from class def' + end + + def test_errors_generate_message_given_no_custom_message_falls_back_to_global_default_key_translation + global_scope = [:active_record, :error_messages] + custom_scope = global_scope + [:custom, 'topic', :title] + + I18n.stubs(:translate).with(:invalid, :scope => custom_scope).returns nil + I18n.expects(:translate).with(:invalid, :scope => global_scope) + @topic.errors.generate_message :title, :invalid + end + + def test_errors_add_given_no_message_it_translates_invalid + I18n.expects(:translate).with(:"active_record.error_messages.invalid") + @topic.errors.add :title + end + + def test_errors_add_on_empty_generates_message + @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) + @topic.errors.add_on_empty :title + end + + def test_errors_add_on_empty_generates_message_with_custom_default_message + @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) + @topic.errors.add_on_empty :title, 'custom' + end + + def test_errors_add_on_blank_generates_message + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) + @topic.errors.add_on_blank :title + end + + def test_errors_add_on_blank_generates_message_with_custom_default_message + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) + @topic.errors.add_on_blank :title, 'custom' + end + + def test_errors_full_messages_translates_human_attribute_name_for_model_attributes + @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } + I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US').returns('Title') + @topic.errors.full_messages :locale => 'en-US' + end + + + # ActiveRecord::Validations + + # validates_confirmation_of + + def test_validates_confirmation_of_generates_message + Topic.validates_confirmation_of :title + @topic.title_confirmation = 'foo' + @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil}) + @topic.valid? + end + + def test_validates_confirmation_of_generates_message_with_custom_default_message + Topic.validates_confirmation_of :title, :message => 'custom' + @topic.title_confirmation = 'foo' + @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'}) + @topic.valid? + end + + def test_validates_confirmation_of_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + + Topic.validates_confirmation_of :title + @topic.title_confirmation = 'foo' + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_confirmation_of_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + + Topic.validates_confirmation_of :title + @topic.title_confirmation = 'foo' + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_acceptance_of + + def test_validates_acceptance_of_generates_message + Topic.validates_acceptance_of :title, :allow_nil => false + @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil}) + @topic.valid? + end + + def test_validates_acceptance_of_generates_message_with_custom_default_message + Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false + @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) + @topic.valid? + end + + def test_validates_acceptance_of_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + + Topic.validates_acceptance_of :title, :allow_nil => false + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_acceptance_of_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + + Topic.validates_acceptance_of :title, :allow_nil => false + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_presence_of + + def test_validates_presence_of_generates_message + Topic.validates_presence_of :title + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) + @topic.valid? + end + + def test_validates_presence_of_generates_message_with_custom_default_message + Topic.validates_presence_of :title, :message => 'custom' + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) + @topic.valid? + end + + def test_validates_presence_of_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + + Topic.validates_presence_of :title + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_presence_of_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + + Topic.validates_presence_of :title + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_length_of :within + + def test_validates_length_of_within_generates_message + Topic.validates_length_of :title, :within => 3..5 + @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_custom_default_message + Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' + @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) + @topic.valid? + end + + def test_validates_length_of_within_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + + Topic.validates_length_of :title, :within => 3..5 + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_length_of_within_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + + Topic.validates_length_of :title, :within => 3..5 + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_length_of :is + + def test_validates_length_of_is_generates_message + Topic.validates_length_of :title, :is => 5 + @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_is_generates_message_with_custom_default_message + Topic.validates_length_of :title, :is => 5, :message => 'custom' + @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'}) + @topic.valid? + end + + def test_validates_length_of_within_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + + Topic.validates_length_of :title, :is => 5 + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_length_of_within_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + + Topic.validates_length_of :title, :is => 5 + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_uniqueness_of + + def test_validates_uniqueness_of_generates_message + Topic.validates_uniqueness_of :title + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil}) + @topic.valid? + end + + def test_validates_uniqueness_of_generates_message_with_custom_default_message + Topic.validates_uniqueness_of :title, :message => 'custom' + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom'}) + @topic.valid? + end + + def test_validates_length_of_within_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + + Topic.validates_length_of :title, :is => 5 + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_length_of_within_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + + Topic.validates_length_of :title, :is => 5 + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_format_of + + def test_validates_format_of_generates_message + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ + @topic.title = '72x' + @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil}) + @topic.valid? + end + + def test_validates_format_of_generates_message_with_custom_default_message + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom' + @topic.title = '72x' + @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'}) + @topic.valid? + end + + def test_validates_format_of_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_format_of_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_inclusion_of + + def test_validates_inclusion_of_generates_message + Topic.validates_inclusion_of :title, :in => %w(a b c) + @topic.title = 'z' + @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil}) + @topic.valid? + end + + def test_validates_inclusion_of_generates_message_with_custom_default_message + Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom' + @topic.title = 'z' + @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'}) + @topic.valid? + end + + def test_validates_inclusion_of_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + + Topic.validates_inclusion_of :title, :in => %w(a b c) + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_inclusion_of_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + + Topic.validates_inclusion_of :title, :in => %w(a b c) + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_exclusion_of + + def test_validates_exclusion_of_generates_message + Topic.validates_exclusion_of :title, :in => %w(a b c) + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil}) + @topic.valid? + end + + def test_validates_exclusion_of_generates_message_with_custom_default_message + Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom' + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'}) + @topic.valid? + end + + def test_validates_exclusion_of_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + + Topic.validates_exclusion_of :title, :in => %w(a b c) + @topic.title = 'a' + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_exclusion_of_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + + Topic.validates_exclusion_of :title, :in => %w(a b c) + @topic.title = 'a' + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_numericality_of :only_integer + + def test_validates_numericality_of_only_integer_generates_message + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom' + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) + @topic.valid? + end + + def test_validates_numericality_of_only_integer_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_numericality_of_only_integer_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_numericality_of :odd + + def test_validates_numericality_of_odd_generates_message + Topic.validates_numericality_of :title, :only_integer => true, :odd => true + @topic.title = 0 + @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_odd_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom' + @topic.title = 0 + @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'}) + @topic.valid? + end + + def test_validates_numericality_of_odd_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true, :odd => true + @topic.title = 0 + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_numericality_of_odd_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true, :odd => true + @topic.title = 0 + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_numericality_of :less_than + + def test_validates_numericality_of_less_than_generates_message + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 + @topic.title = 1 + @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_odd_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' + @topic.title = 1 + @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) + @topic.valid? + end + + def test_validates_numericality_of_less_than_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 + @topic.title = 1 + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_numericality_of_less_than_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 + @topic.title = 1 + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_associated + + def test_validates_associated_generates_message + Topic.validates_associated :replies + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) + replied_topic.valid? + end + + def test_validates_associated_generates_message_with_custom_default_message + Topic.validates_associated :replies + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) + replied_topic.valid? + end + + def test_validates_associated_finds_custom_model_key_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + + Topic.validates_associated :replies + replied_topic.valid? + assert_equal 'custom message', replied_topic.errors.on(:replies) + end + + def test_validates_associated_finds_global_default_translation + I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + + Topic.validates_associated :replies + replied_topic.valid? + assert_equal 'global message', replied_topic.errors.on(:replies) + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index a9882828ca..08e608d346 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -6,10 +6,15 @@ module ActiveSupport #:nodoc: module Conversions # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: # * :connector - The word used to join the last element in arrays with two or more elements (default: "and") - # * :skip_last_comma - Set to true to return "a, b and c" instead of "a, b, and c". - def to_sentence(options = {}) - options.assert_valid_keys(:connector, :skip_last_comma) - options.reverse_merge! :connector => 'and', :skip_last_comma => false + # * :skip_last_comma - Set to true to return "a, b and c" instead of "a, b, and c". + def to_sentence(options = {}) + options.assert_valid_keys(:connector, :skip_last_comma, :locale) + + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + + default = :'support.array.sentence_connector'.t(locale) + options.reverse_merge! :connector => default, :skip_last_comma => false options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == '' case length @@ -23,6 +28,7 @@ module ActiveSupport #:nodoc: "#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]}#{self[-1]}" end end + # Calls to_param on all its elements and joins the result with # slashes. This is used by url_for in Action Pack. diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index a02e42f791..bca2664fa4 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -23,4 +23,11 @@ begin gem 'tzinfo', '~> 0.3.9' rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.9" +end + +begin + gem 'i18n', '~> 0.3.9' +rescue Gem::LoadError + $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1/lib" # TODO + require 'i18n' end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 new file mode 160000 index 0000000000..70ab0f3cc5 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -0,0 +1 @@ +Subproject commit 70ab0f3cc5921cc67e09741939a08b2582d707cb -- cgit v1.2.3 From 4a8486a1b1aa28d4cab5571b55301917221870e9 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 16:43:30 +0200 Subject: add lang file for active_support --- activesupport/lib/active_support.rb | 1 + activesupport/lib/active_support/lang/en-US.rb | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 activesupport/lib/active_support/lang/en-US.rb diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 1a8603e892..acdb3056d2 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -55,6 +55,7 @@ require 'active_support/multibyte' require 'active_support/base64' require 'active_support/time_with_zone' +require 'active_support/lang/en-US.rb' Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') Dependencies = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Dependencies', 'ActiveSupport::Dependencies') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb new file mode 100644 index 0000000000..8732927f48 --- /dev/null +++ b/activesupport/lib/active_support/lang/en-US.rb @@ -0,0 +1,7 @@ +I18n.backend.add_translations :'en-US', { + :support => { + :array => { + :sentence_connector => 'and' + } + } +} \ No newline at end of file -- cgit v1.2.3 From fdb5f810dc41f54f8cdb9c51154ff8987362c13a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 16:55:47 +0200 Subject: I18n has not been released as a gem, yet --- activesupport/lib/active_support/vendor.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index bca2664fa4..381471b833 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -25,9 +25,10 @@ rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.9" end -begin - gem 'i18n', '~> 0.3.9' -rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1/lib" # TODO +# TODO I18n gem has not been released yet +# begin +# gem 'i18n', '~> 0.0.1' +# rescue Gem::LoadError + $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1/lib" require 'i18n' -end \ No newline at end of file +# end \ No newline at end of file -- cgit v1.2.3 From 2fe4d350e98d7f825cf3d1f9233075a5a79e32a1 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 18:31:11 +0200 Subject: make ActiveRecord::Errors.default_error_messages look up translated error messages --- activerecord/lib/active_record/validations.rb | 33 ++++++--------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index f54fb80137..0ca68989d2 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -18,37 +18,18 @@ module ActiveRecord # determine whether the object is in a valid state to be saved. See usage example in Validations. class Errors include Enumerable + + class << self + def default_error_messages + # TODO deprecate this? + :'active_record.error_messages'.t + end + end def initialize(base) # :nodoc: @base, @errors = base, {} end - # @@default_error_messages = { - # :inclusion => "is not included in the list", - # :exclusion => "is reserved", - # :invalid => "is invalid", - # :confirmation => "doesn't match confirmation", - # :accepted => "must be accepted", - # :empty => "can't be empty", - # :blank => "can't be blank", - # :too_long => "is too long (maximum is %d characters)", - # :too_short => "is too short (minimum is %d characters)", - # :wrong_length => "is the wrong length (should be %d characters)", - # :taken => "has already been taken", - # :not_a_number => "is not a number", - # :greater_than => "must be greater than %d", - # :greater_than_or_equal_to => "must be greater than or equal to %d", - # :equal_to => "must be equal to %d", - # :less_than => "must be less than %d", - # :less_than_or_equal_to => "must be less than or equal to %d", - # :odd => "must be odd", - # :even => "must be even" - # } - # - # # Holds a hash with all the default error messages that can be replaced by your own copy or localizations. - # cattr_accessor :default_error_messages - - # Adds an error to the base object instead of any particular attribute. This is used # to report errors that don't tie to any specific attribute, but rather to the object # as a whole. These error messages don't get prepended with any field name when iterating -- cgit v1.2.3 From b09c6e7444cf05f986e601bcc22cf17ede7b63bd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 19:08:14 +0200 Subject: add a generic tranlate view helper --- actionpack/lib/action_view/helpers/i18n_helper.rb | 19 +++++++++++++ actionpack/test/template/i18n_helper_test.rb | 33 +++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 actionpack/lib/action_view/helpers/i18n_helper.rb create mode 100644 actionpack/test/template/i18n_helper_test.rb diff --git a/actionpack/lib/action_view/helpers/i18n_helper.rb b/actionpack/lib/action_view/helpers/i18n_helper.rb new file mode 100644 index 0000000000..1b1d1f301d --- /dev/null +++ b/actionpack/lib/action_view/helpers/i18n_helper.rb @@ -0,0 +1,19 @@ +module ActionView + module Helpers + module I18nHelper + def translate(*args) + # inserts the locale or current request locale to the argument list if no locale + # has been passed or the locale has been passed as part of the options hash + options = args.extract_options! + if args.size != 2 + locale = options.delete :locale + locale ||= request.locale if respond_to? :request + args << locale if locale + end + args << options unless options.empty? + I18n.translate *args + end + alias :t :translate + end + end +end \ No newline at end of file diff --git a/actionpack/test/template/i18n_helper_test.rb b/actionpack/test/template/i18n_helper_test.rb new file mode 100644 index 0000000000..598731568c --- /dev/null +++ b/actionpack/test/template/i18n_helper_test.rb @@ -0,0 +1,33 @@ +require 'abstract_unit' +require 'action_view/helpers/i18n_helper' + +class I18nHelperTests < Test::Unit::TestCase + include ActionView::Helpers::I18nHelper + + attr_reader :request + def setup + @request = stub :locale => 'en-US' + I18n.stubs(:translate).with(:'foo.bar', 'en-US').returns 'Foo Bar' + end + + def test_translate_given_a_locale_argument_it_does_not_check_request_for_locale + request.expects(:locale).never + assert_equal 'Foo Bar', translate(:'foo.bar', :locale => 'en-US') + end + + def test_translate_given_a_locale_option_it_does_not_check_request_for_locale + request.expects(:locale).never + I18n.expects(:translate).with(:'foo.bar', 'en-US').returns 'Foo Bar' + assert_equal 'Foo Bar', translate(:'foo.bar', :locale => 'en-US') + end + + def test_translate_given_no_locale_it_checks_request_for_locale + request.expects(:locale).returns 'en-US' + assert_equal 'Foo Bar', translate(:'foo.bar') + end + + def test_translate_delegates_to_i18n_translate + I18n.expects(:translate).with(:'foo.bar', 'en-US').returns 'Foo Bar' + assert_equal 'Foo Bar', translate(:'foo.bar') + end +end \ No newline at end of file -- cgit v1.2.3 From 585c8c17c303fc46fcf014a644a541eae6cb5ffd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Fri, 20 Jun 2008 09:13:20 +0200 Subject: rename Backend::Simple#add_translations to set_translations because it overwrites existing translations --- .../lib/action_view/helpers/form_options_helper.rb | 2 +- actionpack/lib/action_view/lang/en-US.rb | 2 +- .../test/template/number_helper_i18n_test.rb | 2 +- activerecord/lib/active_record/lang/en-US.rb | 2 +- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_i18n_test.rb | 80 +++++++++++----------- activesupport/lib/active_support/lang/en-US.rb | 2 +- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- 8 files changed, 47 insertions(+), 47 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index c782c0a816..a6b9e65a77 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -349,7 +349,7 @@ module ActionView end # All the countries included in the country_options output. - # only included for backwards compatibility, please use the I18n interface + # deprecated. please use :'countries.names'.t directly COUNTRIES = :'countries.names'.t 'en-US' unless const_defined?("COUNTRIES") end diff --git a/actionpack/lib/action_view/lang/en-US.rb b/actionpack/lib/action_view/lang/en-US.rb index 659f96a5f3..70eb1b79de 100644 --- a/actionpack/lib/action_view/lang/en-US.rb +++ b/actionpack/lib/action_view/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.add_translations :'en-US', { +I18n.backend.set_translations :'en-US', { :date => { :formats => { :default => "%Y-%m-%d", diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index 47cb035f56..d002ad4a2f 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -7,7 +7,7 @@ class NumberHelperI18nTests < Test::Unit::TestCase def setup @request = mock @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} - I18n.backend.add_translations 'en-US', :currency => {:format => @defaults} + I18n.backend.set_translations 'en-US', :currency => {:format => @defaults} end def test_number_to_currency_given_a_locale_it_does_not_check_request_for_locale diff --git a/activerecord/lib/active_record/lang/en-US.rb b/activerecord/lib/active_record/lang/en-US.rb index 7c3bcfd85e..f307f40f1a 100644 --- a/activerecord/lib/active_record/lang/en-US.rb +++ b/activerecord/lib/active_record/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.add_translations :'en-US', { +I18n.backend.set_translations :'en-US', { :active_record => { :error_messages => { :inclusion => "is not included in the list", diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 0ca68989d2..bcb204f1ba 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -21,7 +21,7 @@ module ActiveRecord class << self def default_error_messages - # TODO deprecate this? + # ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use :'active_record.error_messages'.t.") :'active_record.error_messages'.t end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index eb454fca20..8f8574c242 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -6,7 +6,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def setup reset_callbacks Topic @topic = Topic.new - I18n.backend.add_translations('en-US', :active_record => {:error_messages => {:custom => nil}}) + I18n.backend.set_translations('en-US', :active_record => {:error_messages => {:custom => nil}}) end def teardown @@ -113,8 +113,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_confirmation_of_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @@ -123,7 +123,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_confirmation_of_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @@ -147,8 +147,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_acceptance_of_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? @@ -156,7 +156,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_acceptance_of_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? @@ -179,8 +179,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_presence_of_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} Topic.validates_presence_of :title @topic.valid? @@ -188,7 +188,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_presence_of_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} Topic.validates_presence_of :title @topic.valid? @@ -211,8 +211,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} Topic.validates_length_of :title, :within => 3..5 @topic.valid? @@ -220,7 +220,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} Topic.validates_length_of :title, :within => 3..5 @topic.valid? @@ -243,8 +243,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -252,7 +252,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -277,8 +277,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -286,7 +286,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -311,8 +311,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_format_of_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? @@ -320,7 +320,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_format_of_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? @@ -345,8 +345,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_inclusion_of_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? @@ -354,7 +354,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_inclusion_of_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? @@ -379,8 +379,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_exclusion_of_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @@ -389,7 +389,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_exclusion_of_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @@ -415,8 +415,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_only_integer_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -425,7 +425,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_only_integer_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -451,8 +451,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_odd_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @@ -461,7 +461,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_odd_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @@ -487,8 +487,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_less_than_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @@ -497,7 +497,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_less_than_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @@ -521,8 +521,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_associated_finds_custom_model_key_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_associated :replies replied_topic.valid? @@ -530,7 +530,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_associated_finds_global_default_translation - I18n.backend.add_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_associated :replies replied_topic.valid? diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb index 8732927f48..5b8e04363e 100644 --- a/activesupport/lib/active_support/lang/en-US.rb +++ b/activesupport/lib/active_support/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.add_translations :'en-US', { +I18n.backend.set_translations :'en-US', { :support => { :array => { :sentence_connector => 'and' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 70ab0f3cc5..1af3435539 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 70ab0f3cc5921cc67e09741939a08b2582d707cb +Subproject commit 1af3435539b4a0729c13d21c5df037a635fe98c1 -- cgit v1.2.3 From c1e2506494107892a0962b8491cd234f77949c08 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 11:27:19 +0200 Subject: Changed process of storing translations from the client libraries to the backend: clients now can pass a block to backend#populate which can contain code to load and register translations. This makes sense for backends that persist their translations (e.g. to db) so the repeated loading and passing of translations throughout the server startup would be wasted resources. --- actionpack/lib/action_view.rb | 4 +- actionpack/lib/action_view/lang/en-US.rb | 2 +- .../test/template/number_helper_i18n_test.rb | 2 +- activerecord/lib/active_record.rb | 5 +- activerecord/lib/active_record/lang/en-US.rb | 2 +- activerecord/test/cases/validations_i18n_test.rb | 80 +++++++++++----------- activesupport/lib/active_support.rb | 5 +- activesupport/lib/active_support/lang/en-US.rb | 2 +- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- 9 files changed, 56 insertions(+), 48 deletions(-) diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 33d50a61c4..dff487377f 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -32,7 +32,9 @@ require 'action_view/base' require 'action_view/partials' require 'action_view/template_error' -require 'action_view/lang/en-US.rb' +I18n.backend.populate do + require 'action_view/lang/en-US.rb' +end ActionView::Base.class_eval do include ActionView::Partials diff --git a/actionpack/lib/action_view/lang/en-US.rb b/actionpack/lib/action_view/lang/en-US.rb index 70eb1b79de..6b5345ed90 100644 --- a/actionpack/lib/action_view/lang/en-US.rb +++ b/actionpack/lib/action_view/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.set_translations :'en-US', { +I18n.backend.store_translations :'en-US', { :date => { :formats => { :default => "%Y-%m-%d", diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index d002ad4a2f..b75af03378 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -7,7 +7,7 @@ class NumberHelperI18nTests < Test::Unit::TestCase def setup @request = mock @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} - I18n.backend.set_translations 'en-US', :currency => {:format => @defaults} + I18n.backend.store_translations 'en-US', :currency => {:format => @defaults} end def test_number_to_currency_given_a_locale_it_does_not_check_request_for_locale diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index b379bd26f8..71882833d4 100755 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -81,4 +81,7 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' -require 'active_record/lang/en-US.rb' +I18n.backend.populate do + require 'active_record/lang/en-US.rb' +end + diff --git a/activerecord/lib/active_record/lang/en-US.rb b/activerecord/lib/active_record/lang/en-US.rb index f307f40f1a..b31e13ed3a 100644 --- a/activerecord/lib/active_record/lang/en-US.rb +++ b/activerecord/lib/active_record/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.set_translations :'en-US', { +I18n.backend.store_translations :'en-US', { :active_record => { :error_messages => { :inclusion => "is not included in the list", diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 8f8574c242..de844bf5a6 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -6,7 +6,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def setup reset_callbacks Topic @topic = Topic.new - I18n.backend.set_translations('en-US', :active_record => {:error_messages => {:custom => nil}}) + I18n.backend.store_translations('en-US', :active_record => {:error_messages => {:custom => nil}}) end def teardown @@ -113,8 +113,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_confirmation_of_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @@ -123,7 +123,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_confirmation_of_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @@ -147,8 +147,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_acceptance_of_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? @@ -156,7 +156,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_acceptance_of_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? @@ -179,8 +179,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_presence_of_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} Topic.validates_presence_of :title @topic.valid? @@ -188,7 +188,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_presence_of_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} Topic.validates_presence_of :title @topic.valid? @@ -211,8 +211,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} Topic.validates_length_of :title, :within => 3..5 @topic.valid? @@ -220,7 +220,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} Topic.validates_length_of :title, :within => 3..5 @topic.valid? @@ -243,8 +243,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -252,7 +252,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -277,8 +277,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -286,7 +286,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_length_of_within_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} Topic.validates_length_of :title, :is => 5 @topic.valid? @@ -311,8 +311,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_format_of_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? @@ -320,7 +320,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_format_of_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? @@ -345,8 +345,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_inclusion_of_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? @@ -354,7 +354,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_inclusion_of_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? @@ -379,8 +379,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_exclusion_of_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @@ -389,7 +389,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_exclusion_of_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @@ -415,8 +415,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_only_integer_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -425,7 +425,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_only_integer_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @@ -451,8 +451,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_odd_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @@ -461,7 +461,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_odd_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @@ -487,8 +487,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_less_than_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @@ -497,7 +497,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_numericality_of_less_than_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @@ -521,8 +521,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_associated_finds_custom_model_key_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_associated :replies replied_topic.valid? @@ -530,7 +530,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end def test_validates_associated_finds_global_default_translation - I18n.backend.set_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} Topic.validates_associated :replies replied_topic.valid? diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index acdb3056d2..0de948dda9 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -55,7 +55,10 @@ require 'active_support/multibyte' require 'active_support/base64' require 'active_support/time_with_zone' -require 'active_support/lang/en-US.rb' + +I18n.backend.populate do + require 'active_support/lang/en-US.rb' +end Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') Dependencies = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Dependencies', 'ActiveSupport::Dependencies') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb index 5b8e04363e..aa06fe14bd 100644 --- a/activesupport/lib/active_support/lang/en-US.rb +++ b/activesupport/lib/active_support/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.set_translations :'en-US', { +I18n.backend.store_translations :'en-US', { :support => { :array => { :sentence_connector => 'and' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 1af3435539..8e43afa38a 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 1af3435539b4a0729c13d21c5df037a635fe98c1 +Subproject commit 8e43afa38aa007d1de6d6acf44d43143c403d13f -- cgit v1.2.3 From 428aa24d24032d382dc3d9ccf131e0c874043dbd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 11:35:02 +0200 Subject: Renamed lang/ to locale/ because that's what we seem to standarize on. Also, in future this place can be used for data/code that's not literally translations but conceptually belongs to the locale (like custom pluralization algorithms etc.). --- actionpack/lib/action_view.rb | 2 +- actionpack/lib/action_view/lang/en-US.rb | 93 ------------------------ actionpack/lib/action_view/locale/en-US.rb | 93 ++++++++++++++++++++++++ activerecord/lib/active_record.rb | 2 +- activerecord/lib/active_record/lang/en-US.rb | 25 ------- activerecord/lib/active_record/locale/en-US.rb | 25 +++++++ activerecord/test/cases/validations_i18n_test.rb | 2 +- activesupport/lib/active_support.rb | 2 +- activesupport/lib/active_support/lang/en-US.rb | 7 -- activesupport/lib/active_support/locale/en-US.rb | 7 ++ 10 files changed, 129 insertions(+), 129 deletions(-) delete mode 100644 actionpack/lib/action_view/lang/en-US.rb create mode 100644 actionpack/lib/action_view/locale/en-US.rb delete mode 100644 activerecord/lib/active_record/lang/en-US.rb create mode 100644 activerecord/lib/active_record/locale/en-US.rb delete mode 100644 activesupport/lib/active_support/lang/en-US.rb create mode 100644 activesupport/lib/active_support/locale/en-US.rb diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index dff487377f..067a871f79 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -33,7 +33,7 @@ require 'action_view/partials' require 'action_view/template_error' I18n.backend.populate do - require 'action_view/lang/en-US.rb' + require 'action_view/locale/en-US.rb' end ActionView::Base.class_eval do diff --git a/actionpack/lib/action_view/lang/en-US.rb b/actionpack/lib/action_view/lang/en-US.rb deleted file mode 100644 index 6b5345ed90..0000000000 --- a/actionpack/lib/action_view/lang/en-US.rb +++ /dev/null @@ -1,93 +0,0 @@ -I18n.backend.store_translations :'en-US', { - :date => { - :formats => { - :default => "%Y-%m-%d", - :short => "%b %d", - :long => "%B %d, %Y", - }, - :day_names => Date::DAYNAMES, - :abbr_day_names => Date::ABBR_DAYNAMES, - :month_names => Date::MONTHNAMES, - :abbr_month_names => Date::ABBR_MONTHNAMES, - :order => [:year, :month, :day] - }, - :time => { - :formats => { - :default => "%a, %d %b %Y %H:%M:%S %z", - :short => "%d %b %H:%M", - :long => "%B %d, %Y %H:%M", - }, - :am => 'am', - :pm => 'pm' - }, - :datetime => { - :distance_in_words => { - :half_a_minute => 'half a minute', - :less_than_x_seconds => ['less than 1 second', 'less than {{count}} seconds'], - :x_seconds => ['1 second', '{{count}} seconds'], - :less_than_x_minutes => ['less than a minute', 'less than {{count}} minutes'], - :x_minutes => ['1 minute', '{{count}} minutes'], - :about_x_hours => ['about 1 hour', 'about {{count}} hours'], - :x_days => ['1 day', '{{count}} days'], - :about_x_months => ['about 1 month', 'about {{count}} months'], - :x_months => ['1 month', '{{count}} months'], - :about_x_years => ['about 1 year', 'about {{count}} year'], - :over_x_years => ['over 1 year', 'over {{count}} years'] - } - }, - :currency => { - :format => { - :unit => '$', - :precision => 2, - :separator => '.', - :delimiter => ',', - :format => '%u%n', - } - }, - :countries => { - :names => ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", - "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", - "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", - "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", - "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", - "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", - "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", - "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", - "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", - "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", - "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", - "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", - "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", - "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", - "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", - "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", - "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", - "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", - "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", - "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", - "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", - "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", - "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", - "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", - "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", - "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", - "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", - "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", - "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", - "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", - "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", - "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", - "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", - "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", - "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", - "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", - "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", - "Yemen", "Zambia", "Zimbabwe"] - }, - :active_record => { - :error => { - :header_message => ["1 error prohibited this {{object_name}} from being saved", "{{count}} errors prohibited this {{object_name}} from being saved"], - :message => "There were problems with the following fields:" - } - } -} diff --git a/actionpack/lib/action_view/locale/en-US.rb b/actionpack/lib/action_view/locale/en-US.rb new file mode 100644 index 0000000000..6b5345ed90 --- /dev/null +++ b/actionpack/lib/action_view/locale/en-US.rb @@ -0,0 +1,93 @@ +I18n.backend.store_translations :'en-US', { + :date => { + :formats => { + :default => "%Y-%m-%d", + :short => "%b %d", + :long => "%B %d, %Y", + }, + :day_names => Date::DAYNAMES, + :abbr_day_names => Date::ABBR_DAYNAMES, + :month_names => Date::MONTHNAMES, + :abbr_month_names => Date::ABBR_MONTHNAMES, + :order => [:year, :month, :day] + }, + :time => { + :formats => { + :default => "%a, %d %b %Y %H:%M:%S %z", + :short => "%d %b %H:%M", + :long => "%B %d, %Y %H:%M", + }, + :am => 'am', + :pm => 'pm' + }, + :datetime => { + :distance_in_words => { + :half_a_minute => 'half a minute', + :less_than_x_seconds => ['less than 1 second', 'less than {{count}} seconds'], + :x_seconds => ['1 second', '{{count}} seconds'], + :less_than_x_minutes => ['less than a minute', 'less than {{count}} minutes'], + :x_minutes => ['1 minute', '{{count}} minutes'], + :about_x_hours => ['about 1 hour', 'about {{count}} hours'], + :x_days => ['1 day', '{{count}} days'], + :about_x_months => ['about 1 month', 'about {{count}} months'], + :x_months => ['1 month', '{{count}} months'], + :about_x_years => ['about 1 year', 'about {{count}} year'], + :over_x_years => ['over 1 year', 'over {{count}} years'] + } + }, + :currency => { + :format => { + :unit => '$', + :precision => 2, + :separator => '.', + :delimiter => ',', + :format => '%u%n', + } + }, + :countries => { + :names => ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", + "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", + "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", + "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", + "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", + "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", + "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", + "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", + "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", + "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", + "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", + "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", + "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", + "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", + "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", + "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", + "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", + "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", + "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", + "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", + "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", + "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", + "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", + "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", + "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", + "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", + "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", + "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", + "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", + "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", + "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", + "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", + "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", + "Yemen", "Zambia", "Zimbabwe"] + }, + :active_record => { + :error => { + :header_message => ["1 error prohibited this {{object_name}} from being saved", "{{count}} errors prohibited this {{object_name}} from being saved"], + :message => "There were problems with the following fields:" + } + } +} diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 71882833d4..17a7949959 100755 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -82,6 +82,6 @@ require 'active_record/connection_adapters/abstract_adapter' require 'active_record/schema_dumper' I18n.backend.populate do - require 'active_record/lang/en-US.rb' + require 'active_record/locale/en-US.rb' end diff --git a/activerecord/lib/active_record/lang/en-US.rb b/activerecord/lib/active_record/lang/en-US.rb deleted file mode 100644 index b31e13ed3a..0000000000 --- a/activerecord/lib/active_record/lang/en-US.rb +++ /dev/null @@ -1,25 +0,0 @@ -I18n.backend.store_translations :'en-US', { - :active_record => { - :error_messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :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)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :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" - } - } -} \ No newline at end of file diff --git a/activerecord/lib/active_record/locale/en-US.rb b/activerecord/lib/active_record/locale/en-US.rb new file mode 100644 index 0000000000..b31e13ed3a --- /dev/null +++ b/activerecord/lib/active_record/locale/en-US.rb @@ -0,0 +1,25 @@ +I18n.backend.store_translations :'en-US', { + :active_record => { + :error_messages => { + :inclusion => "is not included in the list", + :exclusion => "is reserved", + :invalid => "is invalid", + :confirmation => "doesn't match confirmation", + :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)", + :taken => "has already been taken", + :not_a_number => "is not a number", + :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" + } + } +} \ No newline at end of file diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index de844bf5a6..10e36b1512 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -11,7 +11,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def teardown reset_callbacks Topic - load 'active_record/lang/en-US.rb' + load 'active_record/locale/en-US.rb' end def unique_topic diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 0de948dda9..de50aafe16 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -57,7 +57,7 @@ require 'active_support/base64' require 'active_support/time_with_zone' I18n.backend.populate do - require 'active_support/lang/en-US.rb' + require 'active_support/locale/en-US.rb' end Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb deleted file mode 100644 index aa06fe14bd..0000000000 --- a/activesupport/lib/active_support/lang/en-US.rb +++ /dev/null @@ -1,7 +0,0 @@ -I18n.backend.store_translations :'en-US', { - :support => { - :array => { - :sentence_connector => 'and' - } - } -} \ No newline at end of file diff --git a/activesupport/lib/active_support/locale/en-US.rb b/activesupport/lib/active_support/locale/en-US.rb new file mode 100644 index 0000000000..aa06fe14bd --- /dev/null +++ b/activesupport/lib/active_support/locale/en-US.rb @@ -0,0 +1,7 @@ +I18n.backend.store_translations :'en-US', { + :support => { + :array => { + :sentence_connector => 'and' + } + } +} \ No newline at end of file -- cgit v1.2.3 From 8bfdabbd8b5137f91d8bcddc8c3d18961c8e316b Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 17:50:37 +0200 Subject: incorporate #translate usage with several default keys (use first default key that resolves to a translation). this might, depending on the backend implementation save some expensive lookups (like db lookups) --- activerecord/lib/active_record/validations.rb | 19 ++++- activerecord/test/cases/validations_i18n_test.rb | 86 +++++++++------------- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- 3 files changed, 52 insertions(+), 55 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index bcb204f1ba..49d3c59ca7 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -66,9 +66,11 @@ module ActiveRecord end def generate_message(attr, key, options = {}) - scope = [:active_record, :error_messages] - key.t(options.merge(:scope => scope + [:custom, @base.class.name.downcase, attr])) || - key.t(options.merge(:scope => scope)) + msgs = base_classes(@base.class).map{|klass| :"custom.#{klass.name.underscore}.#{attr}.#{key}"} + msgs << options[:default] if options[:default] + msgs << key + + I18n.t options.merge(:default => msgs, :scope => [:active_record, :error_messages]) end # Returns true if the specified +attribute+ has errors associated with it. @@ -217,6 +219,17 @@ module ActiveRecord full_messages.each { |msg| e.error(msg) } end end + + protected + + # TODO maybe this should be on ActiveRecord::Base, maybe #self_and_descendents_from_active_record + def base_classes(klass) + classes = [klass] + while klass != klass.base_class + classes << klass = klass.superclass + end + classes + end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 10e36b1512..37a7c1ce49 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -40,31 +40,15 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase global_scope = [:active_record, :error_messages] custom_scope = global_scope + [:custom, 'topic', :title] - I18n.expects(:translate).with(:invalid, :scope => custom_scope).returns 'translation' - I18n.expects(:translate).with(:invalid, :scope => global_scope).never - - @topic.errors.generate_message :title, :invalid + I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] + @topic.errors.generate_message :title, :invalid, :default => 'default from class def' end - def test_errors_generate_message_given_a_custom_message_translates_custom_model_attribute_key_with_custom_message_as_default + def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] - I18n.expects(:translate).with(:invalid, :scope => custom_scope, :default => 'default from class def').returns 'translation' - @topic.errors.generate_message :title, :invalid, :default => 'default from class def' - end - - def test_errors_generate_message_given_no_custom_message_falls_back_to_global_default_key_translation - global_scope = [:active_record, :error_messages] - custom_scope = global_scope + [:custom, 'topic', :title] - - I18n.stubs(:translate).with(:invalid, :scope => custom_scope).returns nil - I18n.expects(:translate).with(:invalid, :scope => global_scope) - @topic.errors.generate_message :title, :invalid - end - - def test_errors_add_given_no_message_it_translates_invalid - I18n.expects(:translate).with(:"active_record.error_messages.invalid") - @topic.errors.add :title + I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] + Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' end def test_errors_add_on_empty_generates_message @@ -115,7 +99,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_confirmation_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} - + Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @topic.valid? @@ -124,7 +108,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_confirmation_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:confirmation => 'global message'}} - + Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @topic.valid? @@ -133,13 +117,13 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_acceptance_of - + def test_validates_acceptance_of_generates_message Topic.validates_acceptance_of :title, :allow_nil => false @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil}) @topic.valid? end - + def test_validates_acceptance_of_generates_message_with_custom_default_message Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) @@ -149,7 +133,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_acceptance_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} - + Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -157,7 +141,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_acceptance_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:accepted => 'global message'}} - + Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -165,7 +149,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_presence_of - + def test_validates_presence_of_generates_message Topic.validates_presence_of :title @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) @@ -181,7 +165,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_presence_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} - + Topic.validates_presence_of :title @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -189,7 +173,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_presence_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} - + Topic.validates_presence_of :title @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -197,7 +181,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_length_of :within - + def test_validates_length_of_within_generates_message Topic.validates_length_of :title, :within => 3..5 @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) @@ -213,7 +197,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} - + Topic.validates_length_of :title, :within => 3..5 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -221,7 +205,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_length_of_within_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} - + Topic.validates_length_of :title, :within => 3..5 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -245,7 +229,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -253,7 +237,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_length_of_within_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -279,7 +263,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -287,7 +271,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_length_of_within_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:wrong_length => 'global message'}} - + Topic.validates_length_of :title, :is => 5 @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -313,7 +297,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_format_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} - + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -321,7 +305,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_format_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} - + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -347,7 +331,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_inclusion_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} - + Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? assert_equal 'custom message', @topic.errors.on(:title) @@ -355,7 +339,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_inclusion_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:inclusion => 'global message'}} - + Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? assert_equal 'global message', @topic.errors.on(:title) @@ -381,7 +365,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_exclusion_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} - + Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @topic.valid? @@ -390,7 +374,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_exclusion_of_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:exclusion => 'global message'}} - + Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @topic.valid? @@ -417,7 +401,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_numericality_of_only_integer_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} - + Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? @@ -426,7 +410,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_numericality_of_only_integer_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} - + Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? @@ -453,7 +437,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_numericality_of_odd_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} - + Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @topic.valid? @@ -462,7 +446,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_numericality_of_odd_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:odd => 'global message'}} - + Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @topic.valid? @@ -489,7 +473,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_numericality_of_less_than_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} - + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @topic.valid? @@ -498,7 +482,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_numericality_of_less_than_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:less_than => 'global message'}} - + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @topic.valid? @@ -523,7 +507,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_associated_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} - + Topic.validates_associated :replies replied_topic.valid? assert_equal 'custom message', replied_topic.errors.on(:replies) @@ -531,7 +515,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_validates_associated_finds_global_default_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:invalid => 'global message'}} - + Topic.validates_associated :replies replied_topic.valid? assert_equal 'global message', replied_topic.errors.on(:replies) diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 8e43afa38a..20c331666b 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 8e43afa38aa007d1de6d6acf44d43143c403d13f +Subproject commit 20c331666b3b6a21791d4cded53c3d8654fba714 -- cgit v1.2.3 From 55e2e2e8b4efbe6fdb0a921c19cd8be5650eab0a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 18:12:59 +0200 Subject: experimental DeprecatedConstantToMethodProxy --- activesupport/lib/active_support/deprecation.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index ebdaf86146..36933b007d 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -194,6 +194,23 @@ module ActiveSupport ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) end end + + class DeprecatedConstantToMethodProxy < DeprecationProxy #:nodoc: + def initialize(old_const, new_target, new_method) + @old_const = old_const + @new_target = new_target + @new_method = new_method + end + + private + def target + @new_target.__send__(@new_method) + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_target.inspect}.#{@new_method} instead.", callstack) + end + end end end -- cgit v1.2.3 From d897acfbb11aaa2d7f3138e1f9772546ecb6f981 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 22 Jun 2008 11:39:10 +0200 Subject: remove DeprecatedConstantToMethodProxy again --- activesupport/lib/active_support/deprecation.rb | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 36933b007d..ebdaf86146 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -194,23 +194,6 @@ module ActiveSupport ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) end end - - class DeprecatedConstantToMethodProxy < DeprecationProxy #:nodoc: - def initialize(old_const, new_target, new_method) - @old_const = old_const - @new_target = new_target - @new_method = new_method - end - - private - def target - @new_target.__send__(@new_method) - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_target.inspect}.#{@new_method} instead.", callstack) - end - end end end -- cgit v1.2.3 From 20d6630c1bb70f09e1f6a135bd3f9d690ad28250 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 22 Jun 2008 11:41:51 +0200 Subject: Replaced country_options_for_select with old, untranslated version and moved country-related helpers to a new FormCountryHelper helper module so that they can easily be moved to a plugin. Updated tests accordingly. --- .../lib/action_view/helpers/form_country_helper.rb | 86 +++ .../lib/action_view/helpers/form_options_helper.rb | 32 +- actionpack/lib/action_view/locale/en-US.rb | 40 -- .../test/template/form_country_helper_test.rb | 772 +++++++++++++++++++++ .../test/template/form_options_helper_i18n_test.rb | 26 - .../test/template/form_options_helper_test.rb | 766 +------------------- 6 files changed, 862 insertions(+), 860 deletions(-) create mode 100644 actionpack/lib/action_view/helpers/form_country_helper.rb create mode 100644 actionpack/test/template/form_country_helper_test.rb delete mode 100644 actionpack/test/template/form_options_helper_i18n_test.rb diff --git a/actionpack/lib/action_view/helpers/form_country_helper.rb b/actionpack/lib/action_view/helpers/form_country_helper.rb new file mode 100644 index 0000000000..b2d2b7741f --- /dev/null +++ b/actionpack/lib/action_view/helpers/form_country_helper.rb @@ -0,0 +1,86 @@ +require 'action_view/helpers/form_options_helper' + +module ActionView + module Helpers + module FormCountryHelper + # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags. + def country_select(object, method, priority_countries = nil, options = {}, html_options = {}) + InstanceTag.new(object, method, self, nil, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options) + end + + # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to + # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so + # that they will be listed above the rest of the (long) list. + # + # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. + def country_options_for_select(selected = nil, priority_countries = nil) + country_options = "" + + if priority_countries + country_options += options_for_select(priority_countries, selected) + country_options += "\n" + end + + return country_options + options_for_select(COUNTRIES, selected) + end + + private + + # All the countries included in the country_options output. + COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", + "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", + "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", + "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", + "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", + "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", + "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", + "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", + "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", + "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", + "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", + "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", + "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", + "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", + "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", + "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", + "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", + "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", + "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", + "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", + "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", + "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", + "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", + "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", + "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", + "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", + "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", + "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", + "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", + "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", + "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", + "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", + "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", + "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", + "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", + "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", + "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES") + + end + + class InstanceTag #:nodoc: + include FormCountryHelper + + def to_country_select_tag(priority_countries, options, html_options) + html_options = html_options.stringify_keys + add_default_name_and_id(html_options) + value = value(object) + content_tag("select", + add_options( + country_options_for_select(value, priority_countries), + options, value + ), html_options + ) + end + end + end +end \ No newline at end of file diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index a6b9e65a77..8ce092b015 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -133,11 +133,6 @@ module ActionView InstanceTag.new(object, method, self, nil, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options) end - # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags. - def country_select(object, method, priority_countries = nil, options = {}, html_options = {}) - InstanceTag.new(object, method, self, nil, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options) - end - # Return select and option tags for the given object and method, using # #time_zone_options_for_select to generate the list of option tags. # @@ -270,30 +265,6 @@ module ActionView options_for_select += '' end end - - # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to - # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so - # that they will be listed above the rest of the (long) list. - # - # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. - def country_options_for_select(*args) - options = args.extract_options! - - locale = options[:locale] - locale ||= request.locale if respond_to?(:request) - - selected, priority_countries = *args - countries = :'countries.names'.t options[:locale] - country_options = "" - - if priority_countries - # TODO priority_countries need to be translated? - country_options += options_for_select(priority_countries, selected) - country_options += "\n" - end - - return country_options + options_for_select(countries, selected) - end # Returns a string of option tags for pretty much any time zone in the @@ -349,8 +320,7 @@ module ActionView end # All the countries included in the country_options output. - # deprecated. please use :'countries.names'.t directly - COUNTRIES = :'countries.names'.t 'en-US' unless const_defined?("COUNTRIES") + COUNTRIES = ActiveSupport::Deprecation::DeprecatedConstantProxy.new 'COUNTRIES', 'ActionView::Helpers::FormCountryHelper::COUNTRIES' end class InstanceTag #:nodoc: diff --git a/actionpack/lib/action_view/locale/en-US.rb b/actionpack/lib/action_view/locale/en-US.rb index 6b5345ed90..20d668a9e1 100644 --- a/actionpack/lib/action_view/locale/en-US.rb +++ b/actionpack/lib/action_view/locale/en-US.rb @@ -44,46 +44,6 @@ I18n.backend.store_translations :'en-US', { :format => '%u%n', } }, - :countries => { - :names => ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", - "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria", - "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin", - "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil", - "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia", - "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China", - "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo", - "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", - "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", - "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)", - "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia", - "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", - "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea", - "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)", - "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq", - "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya", - "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan", - "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya", - "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of", - "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique", - "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of", - "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", - "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger", - "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", - "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines", - "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation", - "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia", - "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino", - "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore", - "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa", - "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname", - "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic", - "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste", - "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan", - "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom", - "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela", - "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara", - "Yemen", "Zambia", "Zimbabwe"] - }, :active_record => { :error => { :header_message => ["1 error prohibited this {{object_name}} from being saved", "{{count}} errors prohibited this {{object_name}} from being saved"], diff --git a/actionpack/test/template/form_country_helper_test.rb b/actionpack/test/template/form_country_helper_test.rb new file mode 100644 index 0000000000..224b2e21c2 --- /dev/null +++ b/actionpack/test/template/form_country_helper_test.rb @@ -0,0 +1,772 @@ +require 'abstract_unit' + +class FormCountryHelperTest < ActionView::TestCase + tests ActionView::Helpers::FormCountryHelper + + silence_warnings do + Post = Struct.new('Post', :title, :author_name, :body, :secret, :written_on, :category, :origin) + end + + def test_country_select + @post = Post.new + @post.origin = "Denmark" + expected_select = <<-COUNTRIES + +COUNTRIES + assert_dom_equal(expected_select[0..-2], country_select("post", "origin")) + end + + def test_country_select_with_priority_countries + @post = Post.new + @post.origin = "Denmark" + expected_select = <<-COUNTRIES + +COUNTRIES + assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"])) + end + + def test_country_select_with_selected_priority_country + @post = Post.new + @post.origin = "New Zealand" + expected_select = <<-COUNTRIES + +COUNTRIES + assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"])) + end +end \ No newline at end of file diff --git a/actionpack/test/template/form_options_helper_i18n_test.rb b/actionpack/test/template/form_options_helper_i18n_test.rb deleted file mode 100644 index c9fc0768bb..0000000000 --- a/actionpack/test/template/form_options_helper_i18n_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'abstract_unit' - -class FormOptionsHelperI18nTests < Test::Unit::TestCase - include ActionView::Helpers::FormOptionsHelper - attr_reader :request - - def setup - @request = mock - end - - def test_country_options_for_select_given_a_locale_it_does_not_check_request_for_locale - request.expects(:locale).never - country_options_for_select :locale => 'en-US' - end - - def test_country_options_for_select_given_no_locale_it_checks_request_for_locale - request.expects(:locale).returns 'en-US' - country_options_for_select - end - - def test_country_options_for_select_translates_country_names - countries = ActionView::Helpers::FormOptionsHelper::COUNTRIES - I18n.expects(:translate).with(:'countries.names', 'en-US').returns countries - country_options_for_select :locale => 'en-US' - end -end \ No newline at end of file diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 3f89a5e426..5ba81aac02 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -412,769 +412,6 @@ class FormOptionsHelperTest < ActionView::TestCase assert_dom_equal expected, collection_select("post", "author_name", @posts, "author_name", "author_name", { :include_blank => true, :name => 'post[author_name][]' }, :multiple => true) end - def test_country_select - @post = Post.new - @post.origin = "Denmark" - expected_select = <<-COUNTRIES - -COUNTRIES - assert_dom_equal(expected_select[0..-2], country_select("post", "origin")) - end - - def test_country_select_with_priority_countries - @post = Post.new - @post.origin = "Denmark" - expected_select = <<-COUNTRIES - -COUNTRIES - assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"])) - end - - def test_country_select_with_selected_priority_country - @post = Post.new - @post.origin = "New Zealand" - expected_select = <<-COUNTRIES - -COUNTRIES - assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"])) - end - def test_time_zone_select @firm = Firm.new("D") html = time_zone_select( "firm", "time_zone" ) @@ -1327,4 +564,7 @@ COUNTRIES html end + def test_countries_is_deprectated + assert_deprecated(/COUNTRIES/) { ActionView::Helpers::FormOptionsHelper::COUNTRIES.size } + end end -- cgit v1.2.3 From 67fce4671e8bcfe2aa670a89195b20837546183a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 22 Jun 2008 13:49:08 +0200 Subject: crap, an array never has a request, stupid. --- activesupport/lib/active_support/core_ext/array/conversions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 08e608d346..80d91a6cbd 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -11,7 +11,7 @@ module ActiveSupport #:nodoc: options.assert_valid_keys(:connector, :skip_last_comma, :locale) locale = options[:locale] - locale ||= request.locale if respond_to?(:request) + locale ||= self.locale if respond_to?(:locale) default = :'support.array.sentence_connector'.t(locale) options.reverse_merge! :connector => default, :skip_last_comma => false -- cgit v1.2.3 From 3533dc68120ed40a4ec44ed9900c9035108cfcf1 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 22 Jun 2008 13:49:38 +0200 Subject: check self.locale instead of request.locale in helpers --- actionpack/lib/action_view/helpers/active_record_helper.rb | 2 +- actionpack/lib/action_view/helpers/date_helper.rb | 4 ++-- actionpack/lib/action_view/helpers/number_helper.rb | 2 +- actionpack/test/template/active_record_helper_i18n_test.rb | 6 +++--- actionpack/test/template/date_helper_i18n_test.rb | 8 ++++---- actionpack/test/template/number_helper_i18n_test.rb | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 5ad9d5f76d..716e303a5d 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -160,7 +160,7 @@ module ActionView count = objects.inject(0) {|sum, object| sum + object.errors.count } locale = options[:locale] - locale ||= request.locale if respond_to?(:request) + locale ||= self.locale if respond_to?(:locale) unless count.zero? html = {} diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 0337be0744..dbb5d458bf 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -60,7 +60,7 @@ module ActionView # def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) locale = options[:locale] - locale ||= request.locale if respond_to?(:request) + locale ||= self.locale if respond_to?(:locale) from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) @@ -507,7 +507,7 @@ module ActionView # def select_month(date, options = {}, html_options = {}) locale = options[:locale] - locale ||= request.locale if respond_to?(:request) + locale ||= self.locale if respond_to?(:locale) val = date ? (date.kind_of?(Fixnum) ? date : date.month) : '' if options[:use_hidden] diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 9d98036f2d..dc56817c12 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -72,7 +72,7 @@ module ActionView options = options.symbolize_keys locale = options[:locale] - locale ||= request.locale if respond_to?(:request) + locale ||= self.locale if respond_to?(:locale) defaults = :'currency.format'.t(locale) || {} precision = options[:precision] || defaults[:precision] diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index 057fb9bd1a..3a2197ac93 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -5,22 +5,22 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase attr_reader :request def setup - @request = mock @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) stubs(:content_tag).returns 'content_tag' + stubs(:locale) I18n.stubs(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns "1 error prohibited this from being saved" I18n.stubs(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' end def test_error_messages_for_given_a_locale_it_does_not_check_request_for_locale - request.expects(:locale).never + expects(:locale).never @object.errors.stubs(:count).returns 0 error_messages_for(:object => @object, :locale => 'en-US') end def test_error_messages_for_given_no_locale_it_checks_request_for_locale - request.expects(:locale).returns 'en-US' + expects(:locale).returns 'en-US' @object.errors.stubs(:count).returns 0 error_messages_for(:object => @object) end diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index 9b7c03a400..f245ca1fc8 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -5,19 +5,19 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase attr_reader :request def setup - @request = mock + stubs(:locale) @from = Time.mktime(2004, 6, 6, 21, 45, 0) end # distance_of_time_in_words def test_distance_of_time_in_words_given_a_locale_it_does_not_check_request_for_locale - request.expects(:locale).never + expects(:locale).never distance_of_time_in_words @from, @from + 1.second, false, :locale => 'en-US' end def test_distance_of_time_in_words_given_no_locale_it_checks_request_for_locale - request.expects(:locale).returns 'en-US' + expects(:locale).returns 'en-US' distance_of_time_in_words @from, @from + 1.second end @@ -64,7 +64,7 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase attr_reader :request def setup - @request = mock + # stubs(:locale) I18n.stubs(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES end diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index b75af03378..5db60ece04 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -5,18 +5,18 @@ class NumberHelperI18nTests < Test::Unit::TestCase attr_reader :request def setup - @request = mock + stubs(:locale) @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} I18n.backend.store_translations 'en-US', :currency => {:format => @defaults} end def test_number_to_currency_given_a_locale_it_does_not_check_request_for_locale - request.expects(:locale).never + expects(:locale).never number_to_currency(1, :locale => 'en-US') end def test_number_to_currency_given_no_locale_it_checks_request_for_locale - request.expects(:locale).returns 'en-US' + expects(:locale).returns 'en-US' number_to_currency(1) end -- cgit v1.2.3 From 0dddba41fcfcd28de2ce1a88a23514fbde53afcf Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 23 Jun 2008 14:33:29 +0200 Subject: rather cosmetic improvements of test coverage --- .../lib/action_view/helpers/number_helper.rb | 2 +- activerecord/lib/active_record/validations.rb | 8 +-- activerecord/test/cases/validations_i18n_test.rb | 62 ++++++++++++++++++++-- 3 files changed, 64 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index dc56817c12..4373d063bb 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -70,7 +70,7 @@ module ActionView # # => 1234567890,50 £ def number_to_currency(number, options = {}) options = options.symbolize_keys - + locale = options[:locale] locale ||= self.locale if respond_to?(:locale) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 49d3c59ca7..5bbd10394c 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -21,8 +21,8 @@ module ActiveRecord class << self def default_error_messages - # ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use :'active_record.error_messages'.t.") - :'active_record.error_messages'.t + ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use 'active_record.error_messages'.t.") + 'active_record.error_messages'.t end end @@ -163,7 +163,7 @@ module ActiveRecord @errors.each_key do |attr| @errors[attr].each do |message| next unless message - + if attr == "base" full_messages << message else @@ -872,7 +872,7 @@ module ActiveRecord end raw_value = raw_value.to_i else - begin + begin raw_value = Kernel.Float(raw_value.to_s) rescue ArgumentError, TypeError message = record.errors.generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 37a7c1ce49..158ff69e57 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -34,6 +34,12 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end end + def test_default_error_messages_is_deprecated + assert_deprecated('ActiveRecord::Errors.default_error_messages') do + ActiveRecord::Errors.default_error_messages + end + end + # ActiveRecord::Errors def test_errors_generate_message_translates_custom_model_attribute_key @@ -182,18 +188,32 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase # validates_length_of :within - def test_validates_length_of_within_generates_message + def test_validates_length_of_within_generates_message_with_title_too_short Topic.validates_length_of :title, :within => 3..5 @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) @topic.valid? end - def test_validates_length_of_within_generates_message_with_custom_default_message + def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) @topic.valid? end + def test_validates_length_of_within_generates_message_with_title_too_long + Topic.validates_length_of :title, :within => 3..5 + @topic.title = 'this title is too long' + @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message + Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' + @topic.title = 'this title is too long' + @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) + @topic.valid? + end + def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:too_short => 'global message'}} @@ -382,7 +402,43 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end - # validates_numericality_of :only_integer + # validates_numericality_of without :only_integer + + def test_validates_numericality_of_generates_message + Topic.validates_numericality_of :title + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :message => 'custom' + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) + @topic.valid? + end + + def test_validates_numericality_of_finds_custom_model_key_translation + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + + Topic.validates_numericality_of :title + @topic.title = 'a' + @topic.valid? + assert_equal 'custom message', @topic.errors.on(:title) + end + + def test_validates_numericality_of_finds_global_default_translation + I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:not_a_number => 'global message'}} + + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + @topic.valid? + assert_equal 'global message', @topic.errors.on(:title) + end + + + # validates_numericality_of with :only_integer def test_validates_numericality_of_only_integer_generates_message Topic.validates_numericality_of :title, :only_integer => true -- cgit v1.2.3 From 2ee9f2a0303cba95b2d8073fc7e22ec75229a8ee Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 23 Jun 2008 14:34:01 +0200 Subject: remove generic translate helpers --- actionpack/lib/action_view/helpers/i18n_helper.rb | 19 ------------- actionpack/test/template/i18n_helper_test.rb | 33 ----------------------- 2 files changed, 52 deletions(-) delete mode 100644 actionpack/lib/action_view/helpers/i18n_helper.rb delete mode 100644 actionpack/test/template/i18n_helper_test.rb diff --git a/actionpack/lib/action_view/helpers/i18n_helper.rb b/actionpack/lib/action_view/helpers/i18n_helper.rb deleted file mode 100644 index 1b1d1f301d..0000000000 --- a/actionpack/lib/action_view/helpers/i18n_helper.rb +++ /dev/null @@ -1,19 +0,0 @@ -module ActionView - module Helpers - module I18nHelper - def translate(*args) - # inserts the locale or current request locale to the argument list if no locale - # has been passed or the locale has been passed as part of the options hash - options = args.extract_options! - if args.size != 2 - locale = options.delete :locale - locale ||= request.locale if respond_to? :request - args << locale if locale - end - args << options unless options.empty? - I18n.translate *args - end - alias :t :translate - end - end -end \ No newline at end of file diff --git a/actionpack/test/template/i18n_helper_test.rb b/actionpack/test/template/i18n_helper_test.rb deleted file mode 100644 index 598731568c..0000000000 --- a/actionpack/test/template/i18n_helper_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'abstract_unit' -require 'action_view/helpers/i18n_helper' - -class I18nHelperTests < Test::Unit::TestCase - include ActionView::Helpers::I18nHelper - - attr_reader :request - def setup - @request = stub :locale => 'en-US' - I18n.stubs(:translate).with(:'foo.bar', 'en-US').returns 'Foo Bar' - end - - def test_translate_given_a_locale_argument_it_does_not_check_request_for_locale - request.expects(:locale).never - assert_equal 'Foo Bar', translate(:'foo.bar', :locale => 'en-US') - end - - def test_translate_given_a_locale_option_it_does_not_check_request_for_locale - request.expects(:locale).never - I18n.expects(:translate).with(:'foo.bar', 'en-US').returns 'Foo Bar' - assert_equal 'Foo Bar', translate(:'foo.bar', :locale => 'en-US') - end - - def test_translate_given_no_locale_it_checks_request_for_locale - request.expects(:locale).returns 'en-US' - assert_equal 'Foo Bar', translate(:'foo.bar') - end - - def test_translate_delegates_to_i18n_translate - I18n.expects(:translate).with(:'foo.bar', 'en-US').returns 'Foo Bar' - assert_equal 'Foo Bar', translate(:'foo.bar') - end -end \ No newline at end of file -- cgit v1.2.3 From c178a87b4326edd491922136c0a55bf4b889473d Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 23 Jun 2008 14:37:50 +0200 Subject: remove call to self.locale from helpers --- actionpack/lib/action_view/helpers/active_record_helper.rb | 5 +---- actionpack/lib/action_view/helpers/date_helper.rb | 5 +---- actionpack/lib/action_view/helpers/number_helper.rb | 9 +++------ 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 716e303a5d..4ff16cd70c 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -159,9 +159,6 @@ module ActionView end count = objects.inject(0) {|sum, object| sum + object.errors.count } - locale = options[:locale] - locale ||= self.locale if respond_to?(:locale) - unless count.zero? html = {} [:id, :class].each do |key| @@ -174,7 +171,7 @@ module ActionView end options[:object_name] ||= params.first - I18n.with_options :locale => locale, :scope => [:active_record, :error] do |locale| + I18n.with_options :locale => options[:locale], :scope => [:active_record, :error] do |locale| header_message = if options.include?(:header_message) options[:header_message] else diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index dbb5d458bf..6ac4171fd5 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -59,15 +59,12 @@ module ActionView # distance_of_time_in_words(Time.now, Time.now) # => less than a minute # def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false, options = {}) - locale = options[:locale] - locale ||= self.locale if respond_to?(:locale) - from_time = from_time.to_time if from_time.respond_to?(:to_time) to_time = to_time.to_time if to_time.respond_to?(:to_time) distance_in_minutes = (((to_time - from_time).abs)/60).round distance_in_seconds = ((to_time - from_time).abs).round - I18n.with_options :locale => locale, :scope => :'datetime.distance_in_words' do |locale| + I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale| case distance_in_minutes when 0..1 return distance_in_minutes == 0 ? diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 4373d063bb..3e0d5b1db4 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -69,12 +69,9 @@ module ActionView # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "", :format => "%n %u") # # => 1234567890,50 £ def number_to_currency(number, options = {}) - options = options.symbolize_keys - - locale = options[:locale] - locale ||= self.locale if respond_to?(:locale) - - defaults = :'currency.format'.t(locale) || {} + options = options.symbolize_keys + defaults = :'currency.format'.t(options[:locale]) || {} + precision = options[:precision] || defaults[:precision] unit = options[:unit] || defaults[:unit] separator = options[:separator] || defaults[:separator] -- cgit v1.2.3 From ac66865ea3d0eb0de8e19fef49293feb9e61281b Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 23 Jun 2008 14:49:02 +0200 Subject: update tests according to removal of self.locale from helpers --- actionpack/test/template/active_record_helper_i18n_test.rb | 13 ------------- actionpack/test/template/date_helper_i18n_test.rb | 12 ------------ actionpack/test/template/number_helper_i18n_test.rb | 11 ----------- 3 files changed, 36 deletions(-) diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index 3a2197ac93..d1b92c7e4d 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -7,24 +7,11 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase def setup @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) stubs(:content_tag).returns 'content_tag' - stubs(:locale) I18n.stubs(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns "1 error prohibited this from being saved" I18n.stubs(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' end - def test_error_messages_for_given_a_locale_it_does_not_check_request_for_locale - expects(:locale).never - @object.errors.stubs(:count).returns 0 - error_messages_for(:object => @object, :locale => 'en-US') - end - - def test_error_messages_for_given_no_locale_it_checks_request_for_locale - expects(:locale).returns 'en-US' - @object.errors.stubs(:count).returns 0 - error_messages_for(:object => @object) - end - def test_error_messages_for_given_a_header_message_option_it_does_not_translate_header_message I18n.expects(:translate).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').never error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en-US') diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index f245ca1fc8..3e1eed61fd 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -5,22 +5,11 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase attr_reader :request def setup - stubs(:locale) @from = Time.mktime(2004, 6, 6, 21, 45, 0) end # distance_of_time_in_words - def test_distance_of_time_in_words_given_a_locale_it_does_not_check_request_for_locale - expects(:locale).never - distance_of_time_in_words @from, @from + 1.second, false, :locale => 'en-US' - end - - def test_distance_of_time_in_words_given_no_locale_it_checks_request_for_locale - expects(:locale).returns 'en-US' - distance_of_time_in_words @from, @from + 1.second - end - def test_distance_of_time_in_words_calls_i18n { # with include_seconds [2.seconds, true] => [:'less_than_x_seconds', 5], @@ -64,7 +53,6 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase attr_reader :request def setup - # stubs(:locale) I18n.stubs(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES end diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index 5db60ece04..bee9ceaa71 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -5,21 +5,10 @@ class NumberHelperI18nTests < Test::Unit::TestCase attr_reader :request def setup - stubs(:locale) @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} I18n.backend.store_translations 'en-US', :currency => {:format => @defaults} end - def test_number_to_currency_given_a_locale_it_does_not_check_request_for_locale - expects(:locale).never - number_to_currency(1, :locale => 'en-US') - end - - def test_number_to_currency_given_no_locale_it_checks_request_for_locale - expects(:locale).returns 'en-US' - number_to_currency(1) - end - def test_number_to_currency_translates_currency_formats I18n.expects(:translate).with(:'currency.format', 'en-US').returns @defaults number_to_currency(1, :locale => 'en-US') -- cgit v1.2.3 From 77177441d1bd8f62c5b6a990ddee155061df661c Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 23 Jun 2008 14:49:47 +0200 Subject: including rcov shell scripts for reference --- actionpack/test/i18n_coverage | 9 +++++++++ activerecord/test/i18n_coverage | 6 ++++++ 2 files changed, 15 insertions(+) create mode 100755 actionpack/test/i18n_coverage create mode 100755 activerecord/test/i18n_coverage diff --git a/actionpack/test/i18n_coverage b/actionpack/test/i18n_coverage new file mode 100755 index 0000000000..57b54e9d47 --- /dev/null +++ b/actionpack/test/i18n_coverage @@ -0,0 +1,9 @@ +rcov -x abstract_unit.rb \ +-i action_view/helpers/number_helper.rb,action_view/helpers/date_helper.rb,action_view/helpers/active_record_helper.rb \ +template/number_helper_i18n_test.rb \ +template/date_helper_i18n_test.rb \ +template/active_record_helper_i18n_test.rb \ + +# template/number_helper_test.rb \ +# template/date_helper_test.rb \ +# template/active_record_helper_test.rb \ No newline at end of file diff --git a/activerecord/test/i18n_coverage b/activerecord/test/i18n_coverage new file mode 100755 index 0000000000..1589a6c06f --- /dev/null +++ b/activerecord/test/i18n_coverage @@ -0,0 +1,6 @@ +rcov -I connections/native_mysql \ +-x cases/helper,config,connection,models \ +-i active_record/validations.rb \ +cases/validations_i18n_test.rb \ + +# cases/validations_test.rb \ No newline at end of file -- cgit v1.2.3 From 8461526f346b8d8387ba3b74221fbeefef3aefeb Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Mon, 23 Jun 2008 14:55:07 +0200 Subject: silence deprecation warning during validations test --- activerecord/test/cases/validations_test.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 7b71647d25..ad27ac951c 100755 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -853,7 +853,9 @@ class ValidationsTest < ActiveRecord::TestCase end def test_validates_length_with_globally_modified_error_message - ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d' + ActiveSupport::Deprecation.silence do + ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d' + end Topic.validates_length_of :title, :minimum => 10 t = Topic.create(:title => 'too short') assert !t.valid? -- cgit v1.2.3 From 66c2508ebbca06e551255a76cc47a1608f091992 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Fri, 27 Jun 2008 15:00:55 +0200 Subject: Make sure mocha is available --- .../template/active_record_helper_i18n_test.rb | 48 +- actionpack/test/template/date_helper_i18n_test.rb | 134 ++--- .../test/template/number_helper_i18n_test.rb | 16 +- activerecord/test/cases/validations_i18n_test.rb | 576 +++++++++++---------- 4 files changed, 413 insertions(+), 361 deletions(-) diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index d1b92c7e4d..d78b0e4c0c 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -4,31 +4,33 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase include ActionView::Helpers::ActiveRecordHelper attr_reader :request - def setup - @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) - stubs(:content_tag).returns 'content_tag' - - I18n.stubs(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns "1 error prohibited this from being saved" - I18n.stubs(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' - end + uses_mocha 'active_record_helper_i18n_test' do + def setup + @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) + stubs(:content_tag).returns 'content_tag' - def test_error_messages_for_given_a_header_message_option_it_does_not_translate_header_message - I18n.expects(:translate).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').never - error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en-US') - end + I18n.stubs(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns "1 error prohibited this from being saved" + I18n.stubs(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' + end - def test_error_messages_for_given_no_header_message_option_it_translates_header_message - I18n.expects(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns 'header message' - error_messages_for(:object => @object, :locale => 'en-US') - end - - def test_error_messages_for_given_a_message_option_it_does_not_translate_message - I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).never - error_messages_for(:object => @object, :message => 'message', :locale => 'en-US') - end + def test_error_messages_for_given_a_header_message_option_it_does_not_translate_header_message + I18n.expects(:translate).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').never + error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en-US') + end + + def test_error_messages_for_given_no_header_message_option_it_translates_header_message + I18n.expects(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns 'header message' + error_messages_for(:object => @object, :locale => 'en-US') + end + + def test_error_messages_for_given_a_message_option_it_does_not_translate_message + I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).never + error_messages_for(:object => @object, :message => 'message', :locale => 'en-US') + end - def test_error_messages_for_given_no_message_option_it_translates_message - I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' - error_messages_for(:object => @object, :locale => 'en-US') + def test_error_messages_for_given_no_message_option_it_translates_message + I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' + error_messages_for(:object => @object, :locale => 'en-US') + end end end \ No newline at end of file diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index 3e1eed61fd..aeb06c55ea 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -8,43 +8,45 @@ class DateHelperDistanceOfTimeInWordsI18nTests < Test::Unit::TestCase @from = Time.mktime(2004, 6, 6, 21, 45, 0) end - # distance_of_time_in_words + uses_mocha 'date_helper_distance_of_time_in_words_i18n_test' do + # distance_of_time_in_words - def test_distance_of_time_in_words_calls_i18n - { # with include_seconds - [2.seconds, true] => [:'less_than_x_seconds', 5], - [9.seconds, true] => [:'less_than_x_seconds', 10], - [19.seconds, true] => [:'less_than_x_seconds', 20], - [30.seconds, true] => [:'half_a_minute', nil], - [59.seconds, true] => [:'less_than_x_minutes', 1], - [60.seconds, true] => [:'x_minutes', 1], - - # without include_seconds - [29.seconds, false] => [:'less_than_x_minutes', 1], - [60.seconds, false] => [:'x_minutes', 1], - [44.minutes, false] => [:'x_minutes', 44], - [61.minutes, false] => [:'about_x_hours', 1], - [24.hours, false] => [:'x_days', 1], - [30.days, false] => [:'about_x_months', 1], - [60.days, false] => [:'x_months', 2], - [1.year, false] => [:'about_x_years', 1], - [3.years, false] => [:'over_x_years', 3] - - }.each do |passed, expected| - assert_distance_of_time_in_words_translates_key passed, expected + def test_distance_of_time_in_words_calls_i18n + { # with include_seconds + [2.seconds, true] => [:'less_than_x_seconds', 5], + [9.seconds, true] => [:'less_than_x_seconds', 10], + [19.seconds, true] => [:'less_than_x_seconds', 20], + [30.seconds, true] => [:'half_a_minute', nil], + [59.seconds, true] => [:'less_than_x_minutes', 1], + [60.seconds, true] => [:'x_minutes', 1], + + # without include_seconds + [29.seconds, false] => [:'less_than_x_minutes', 1], + [60.seconds, false] => [:'x_minutes', 1], + [44.minutes, false] => [:'x_minutes', 44], + [61.minutes, false] => [:'about_x_hours', 1], + [24.hours, false] => [:'x_days', 1], + [30.days, false] => [:'about_x_months', 1], + [60.days, false] => [:'x_months', 2], + [1.year, false] => [:'about_x_years', 1], + [3.years, false] => [:'over_x_years', 3] + + }.each do |passed, expected| + assert_distance_of_time_in_words_translates_key passed, expected + end end - end - - def assert_distance_of_time_in_words_translates_key(passed, expected) - diff, include_seconds = *passed - key, count = *expected - to = @from + diff - options = {:locale => 'en-US', :scope => :'datetime.distance_in_words'} - options[:count] = count if count - - I18n.expects(:t).with(key, options) - distance_of_time_in_words(@from, to, include_seconds, :locale => 'en-US') + def assert_distance_of_time_in_words_translates_key(passed, expected) + diff, include_seconds = *passed + key, count = *expected + to = @from + diff + + options = {:locale => 'en-US', :scope => :'datetime.distance_in_words'} + options[:count] = count if count + + I18n.expects(:t).with(key, options) + distance_of_time_in_words(@from, to, include_seconds, :locale => 'en-US') + end end end @@ -52,36 +54,38 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase include ActionView::Helpers::DateHelper attr_reader :request - def setup - I18n.stubs(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES - end - - # select_month - - def test_select_month_given_use_month_names_option_does_not_translate_monthnames - I18n.expects(:translate).never - select_month(8, :locale => 'en-US', :use_month_names => Date::MONTHNAMES) - end - - def test_select_month_translates_monthnames - I18n.expects(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES - select_month(8, :locale => 'en-US') - end - - def test_select_month_given_use_short_month_option_translates_abbr_monthnames - I18n.expects(:translate).with(:'date.abbr_month_names', 'en-US').returns Date::ABBR_MONTHNAMES - select_month(8, :locale => 'en-US', :use_short_month => true) - end - - # date_or_time_select - - def test_date_or_time_select_given_an_order_options_does_not_translate_order - I18n.expects(:translate).never - datetime_select('post', 'updated_at', :order => [:year, :month, :day], :locale => 'en-US') - end - - def test_date_or_time_select_given_no_order_options_translates_order - I18n.expects(:translate).with(:'date.order', 'en-US').returns [:year, :month, :day] - datetime_select('post', 'updated_at', :locale => 'en-US') + uses_mocha 'date_helper_select_tags_i18n_tests' do + def setup + I18n.stubs(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES + end + + # select_month + + def test_select_month_given_use_month_names_option_does_not_translate_monthnames + I18n.expects(:translate).never + select_month(8, :locale => 'en-US', :use_month_names => Date::MONTHNAMES) + end + + def test_select_month_translates_monthnames + I18n.expects(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES + select_month(8, :locale => 'en-US') + end + + def test_select_month_given_use_short_month_option_translates_abbr_monthnames + I18n.expects(:translate).with(:'date.abbr_month_names', 'en-US').returns Date::ABBR_MONTHNAMES + select_month(8, :locale => 'en-US', :use_short_month => true) + end + + # date_or_time_select + + def test_date_or_time_select_given_an_order_options_does_not_translate_order + I18n.expects(:translate).never + datetime_select('post', 'updated_at', :order => [:year, :month, :day], :locale => 'en-US') + end + + def test_date_or_time_select_given_no_order_options_translates_order + I18n.expects(:translate).with(:'date.order', 'en-US').returns [:year, :month, :day] + datetime_select('post', 'updated_at', :locale => 'en-US') + end end end \ No newline at end of file diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index bee9ceaa71..be40ddbc88 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -4,13 +4,15 @@ class NumberHelperI18nTests < Test::Unit::TestCase include ActionView::Helpers::NumberHelper attr_reader :request - def setup - @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} - I18n.backend.store_translations 'en-US', :currency => {:format => @defaults} - end + uses_mocha 'number_helper_i18n_tests' do + def setup + @defaults = {:separator => ".", :unit => "$", :format => "%u%n", :delimiter => ",", :precision => 2} + I18n.backend.store_translations 'en-US', :currency => {:format => @defaults} + end - def test_number_to_currency_translates_currency_formats - I18n.expects(:translate).with(:'currency.format', 'en-US').returns @defaults - number_to_currency(1, :locale => 'en-US') + def test_number_to_currency_translates_currency_formats + I18n.expects(:translate).with(:'currency.format', 'en-US').returns @defaults + number_to_currency(1, :locale => 'en-US') + end end end \ No newline at end of file diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 158ff69e57..53e90f4d53 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -41,66 +41,307 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end # ActiveRecord::Errors + uses_mocha 'ActiveRecord::Errors' do + def test_errors_generate_message_translates_custom_model_attribute_key + global_scope = [:active_record, :error_messages] + custom_scope = global_scope + [:custom, 'topic', :title] + + I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] + @topic.errors.generate_message :title, :invalid, :default => 'default from class def' + end + + def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti + custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] + + I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] + Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' + end + + def test_errors_add_on_empty_generates_message + @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) + @topic.errors.add_on_empty :title + end + + def test_errors_add_on_empty_generates_message_with_custom_default_message + @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) + @topic.errors.add_on_empty :title, 'custom' + end + + def test_errors_add_on_blank_generates_message + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) + @topic.errors.add_on_blank :title + end + + def test_errors_add_on_blank_generates_message_with_custom_default_message + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) + @topic.errors.add_on_blank :title, 'custom' + end + + def test_errors_full_messages_translates_human_attribute_name_for_model_attributes + @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } + I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US').returns('Title') + @topic.errors.full_messages :locale => 'en-US' + end + end - def test_errors_generate_message_translates_custom_model_attribute_key - global_scope = [:active_record, :error_messages] - custom_scope = global_scope + [:custom, 'topic', :title] + # ActiveRecord::Validations + uses_mocha 'ActiveRecord::Validations' do + # validates_confirmation_of w/ mocha + + def test_validates_confirmation_of_generates_message + Topic.validates_confirmation_of :title + @topic.title_confirmation = 'foo' + @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil}) + @topic.valid? + end + + def test_validates_confirmation_of_generates_message_with_custom_default_message + Topic.validates_confirmation_of :title, :message => 'custom' + @topic.title_confirmation = 'foo' + @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'}) + @topic.valid? + end - I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] - @topic.errors.generate_message :title, :invalid, :default => 'default from class def' - end - - def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti - custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] + # validates_acceptance_of w/ mocha + + def test_validates_acceptance_of_generates_message + Topic.validates_acceptance_of :title, :allow_nil => false + @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil}) + @topic.valid? + end + + def test_validates_acceptance_of_generates_message_with_custom_default_message + Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false + @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) + @topic.valid? + end - I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] - Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' - end - - def test_errors_add_on_empty_generates_message - @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) - @topic.errors.add_on_empty :title - end - - def test_errors_add_on_empty_generates_message_with_custom_default_message - @topic.errors.expects(:generate_message).with(:title, :empty, {:default => 'custom'}) - @topic.errors.add_on_empty :title, 'custom' - end - - def test_errors_add_on_blank_generates_message - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) - @topic.errors.add_on_blank :title - end - - def test_errors_add_on_blank_generates_message_with_custom_default_message - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) - @topic.errors.add_on_blank :title, 'custom' - end - - def test_errors_full_messages_translates_human_attribute_name_for_model_attributes - @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } - I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US').returns('Title') - @topic.errors.full_messages :locale => 'en-US' - end - - - # ActiveRecord::Validations - - # validates_confirmation_of - - def test_validates_confirmation_of_generates_message - Topic.validates_confirmation_of :title - @topic.title_confirmation = 'foo' - @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => nil}) - @topic.valid? + # validates_presence_of w/ mocha + + def test_validates_presence_of_generates_message + Topic.validates_presence_of :title + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) + @topic.valid? + end + + def test_validates_presence_of_generates_message_with_custom_default_message + Topic.validates_presence_of :title, :message => 'custom' + @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_short + Topic.validates_length_of :title, :within => 3..5 + @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message + Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' + @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_long + Topic.validates_length_of :title, :within => 3..5 + @topic.title = 'this title is too long' + @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message + Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' + @topic.title = 'this title is too long' + @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) + @topic.valid? + end + + # validates_length_of :within w/ mocha + + def test_validates_length_of_within_generates_message_with_title_too_short + Topic.validates_length_of :title, :within => 3..5 + @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message + Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' + @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_long + Topic.validates_length_of :title, :within => 3..5 + @topic.title = 'this title is too long' + @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message + Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' + @topic.title = 'this title is too long' + @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) + @topic.valid? + end + + # validates_length_of :is w/ mocha + + def test_validates_length_of_is_generates_message + Topic.validates_length_of :title, :is => 5 + @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil}) + @topic.valid? + end + + def test_validates_length_of_is_generates_message_with_custom_default_message + Topic.validates_length_of :title, :is => 5, :message => 'custom' + @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'}) + @topic.valid? + end + + # validates_uniqueness_of w/ mocha + + def test_validates_uniqueness_of_generates_message + Topic.validates_uniqueness_of :title + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil}) + @topic.valid? + end + + def test_validates_uniqueness_of_generates_message_with_custom_default_message + Topic.validates_uniqueness_of :title, :message => 'custom' + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom'}) + @topic.valid? + end + + # validates_format_of w/ mocha + + def test_validates_format_of_generates_message + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ + @topic.title = '72x' + @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil}) + @topic.valid? + end + + def test_validates_format_of_generates_message_with_custom_default_message + Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom' + @topic.title = '72x' + @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'}) + @topic.valid? + end + + # validates_inclusion_of w/ mocha + + def test_validates_inclusion_of_generates_message + Topic.validates_inclusion_of :title, :in => %w(a b c) + @topic.title = 'z' + @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil}) + @topic.valid? + end + + def test_validates_inclusion_of_generates_message_with_custom_default_message + Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom' + @topic.title = 'z' + @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'}) + @topic.valid? + end + + # validates_exclusion_of w/ mocha + + def test_validates_exclusion_of_generates_message + Topic.validates_exclusion_of :title, :in => %w(a b c) + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil}) + @topic.valid? + end + + def test_validates_exclusion_of_generates_message_with_custom_default_message + Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom' + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'}) + @topic.valid? + end + + # validates_numericality_of without :only_integer w/ mocha + + def test_validates_numericality_of_generates_message + Topic.validates_numericality_of :title + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :message => 'custom' + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) + @topic.valid? + end + + # validates_numericality_of with :only_integer w/ mocha + + def test_validates_numericality_of_only_integer_generates_message + Topic.validates_numericality_of :title, :only_integer => true + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom' + @topic.title = 'a' + @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) + @topic.valid? + end + + # validates_numericality_of :odd w/ mocha + + def test_validates_numericality_of_odd_generates_message + Topic.validates_numericality_of :title, :only_integer => true, :odd => true + @topic.title = 0 + @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_odd_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom' + @topic.title = 0 + @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'}) + @topic.valid? + end + + # validates_numericality_of :less_than w/ mocha + + def test_validates_numericality_of_less_than_generates_message + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 + @topic.title = 1 + @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil}) + @topic.valid? + end + + def test_validates_numericality_of_odd_generates_message_with_custom_default_message + Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' + @topic.title = 1 + @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) + @topic.valid? + end + + # validates_associated w/ mocha + + def test_validates_associated_generates_message + Topic.validates_associated :replies + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) + replied_topic.valid? + end + + def test_validates_associated_generates_message_with_custom_default_message + Topic.validates_associated :replies + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) + replied_topic.valid? + end end - def test_validates_confirmation_of_generates_message_with_custom_default_message - Topic.validates_confirmation_of :title, :message => 'custom' - @topic.title_confirmation = 'foo' - @topic.errors.expects(:generate_message).with(:title, :confirmation, {:default => 'custom'}) - @topic.valid? - end + # validates_confirmation_of w/o mocha def test_validates_confirmation_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:confirmation => 'custom message'}}}}} @@ -121,20 +362,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_acceptance_of - - def test_validates_acceptance_of_generates_message - Topic.validates_acceptance_of :title, :allow_nil => false - @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => nil}) - @topic.valid? - end - - def test_validates_acceptance_of_generates_message_with_custom_default_message - Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false - @topic.errors.expects(:generate_message).with(:title, :accepted, {:default => 'custom'}) - @topic.valid? - end + # validates_acceptance_of w/o mocha def test_validates_acceptance_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:accepted => 'custom message'}}}}} @@ -153,21 +381,8 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_presence_of - - def test_validates_presence_of_generates_message - Topic.validates_presence_of :title - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => nil}) - @topic.valid? - end - - def test_validates_presence_of_generates_message_with_custom_default_message - Topic.validates_presence_of :title, :message => 'custom' - @topic.errors.expects(:generate_message).with(:title, :blank, {:default => 'custom'}) - @topic.valid? - end - + # validates_presence_of w/o mocha + def test_validates_presence_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:blank => 'custom message'}}}}} I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:blank => 'global message'}} @@ -185,34 +400,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_length_of :within - - def test_validates_length_of_within_generates_message_with_title_too_short - Topic.validates_length_of :title, :within => 3..5 - @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) - @topic.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message - Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom' - @topic.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) - @topic.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long - Topic.validates_length_of :title, :within => 3..5 - @topic.title = 'this title is too long' - @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) - @topic.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message - Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom' - @topic.title = 'this title is too long' - @topic.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) - @topic.valid? - end + # validates_length_of :within w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:too_short => 'custom message'}}}}} @@ -231,20 +419,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_length_of :is - - def test_validates_length_of_is_generates_message - Topic.validates_length_of :title, :is => 5 - @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => nil}) - @topic.valid? - end - - def test_validates_length_of_is_generates_message_with_custom_default_message - Topic.validates_length_of :title, :is => 5, :message => 'custom' - @topic.errors.expects(:generate_message).with(:title, :wrong_length, {:count => 5, :default => 'custom'}) - @topic.valid? - end + # validates_length_of :is w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} @@ -263,22 +438,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_uniqueness_of - - def test_validates_uniqueness_of_generates_message - Topic.validates_uniqueness_of :title - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil}) - @topic.valid? - end - - def test_validates_uniqueness_of_generates_message_with_custom_default_message - Topic.validates_uniqueness_of :title, :message => 'custom' - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom'}) - @topic.valid? - end + # validates_uniqueness_of w/o mocha def test_validates_length_of_within_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:wrong_length => 'custom message'}}}}} @@ -298,21 +458,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end - # validates_format_of - - def test_validates_format_of_generates_message - Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ - @topic.title = '72x' - @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => nil}) - @topic.valid? - end - - def test_validates_format_of_generates_message_with_custom_default_message - Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom' - @topic.title = '72x' - @topic.errors.expects(:generate_message).with(:title, :invalid, {:value => '72x', :default => 'custom'}) - @topic.valid? - end + # validates_format_of w/o mocha def test_validates_format_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:invalid => 'custom message'}}}}} @@ -331,22 +477,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_inclusion_of - - def test_validates_inclusion_of_generates_message - Topic.validates_inclusion_of :title, :in => %w(a b c) - @topic.title = 'z' - @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => nil}) - @topic.valid? - end - - def test_validates_inclusion_of_generates_message_with_custom_default_message - Topic.validates_inclusion_of :title, :in => %w(a b c), :message => 'custom' - @topic.title = 'z' - @topic.errors.expects(:generate_message).with(:title, :inclusion, {:value => 'z', :default => 'custom'}) - @topic.valid? - end + # validates_inclusion_of w/o mocha def test_validates_inclusion_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:inclusion => 'custom message'}}}}} @@ -365,22 +496,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_exclusion_of - - def test_validates_exclusion_of_generates_message - Topic.validates_exclusion_of :title, :in => %w(a b c) - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => nil}) - @topic.valid? - end - - def test_validates_exclusion_of_generates_message_with_custom_default_message - Topic.validates_exclusion_of :title, :in => %w(a b c), :message => 'custom' - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :exclusion, {:value => 'a', :default => 'custom'}) - @topic.valid? - end + # validates_exclusion_of w/o mocha def test_validates_exclusion_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:exclusion => 'custom message'}}}}} @@ -401,22 +517,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_numericality_of without :only_integer - - def test_validates_numericality_of_generates_message - Topic.validates_numericality_of :title - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) - @topic.valid? - end - - def test_validates_numericality_of_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :message => 'custom' - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) - @topic.valid? - end + # validates_numericality_of without :only_integer w/o mocha def test_validates_numericality_of_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} @@ -437,22 +538,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_numericality_of with :only_integer - - def test_validates_numericality_of_only_integer_generates_message - Topic.validates_numericality_of :title, :only_integer => true - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => nil}) - @topic.valid? - end - - def test_validates_numericality_of_only_integer_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom' - @topic.title = 'a' - @topic.errors.expects(:generate_message).with(:title, :not_a_number, {:value => 'a', :default => 'custom'}) - @topic.valid? - end + # validates_numericality_of with :only_integer w/o mocha def test_validates_numericality_of_only_integer_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:not_a_number => 'custom message'}}}}} @@ -473,22 +559,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_numericality_of :odd - - def test_validates_numericality_of_odd_generates_message - Topic.validates_numericality_of :title, :only_integer => true, :odd => true - @topic.title = 0 - @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => nil}) - @topic.valid? - end - - def test_validates_numericality_of_odd_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom' - @topic.title = 0 - @topic.errors.expects(:generate_message).with(:title, :odd, {:value => 0, :default => 'custom'}) - @topic.valid? - end + # validates_numericality_of :odd w/o mocha def test_validates_numericality_of_odd_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:odd => 'custom message'}}}}} @@ -509,22 +580,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase assert_equal 'global message', @topic.errors.on(:title) end - - # validates_numericality_of :less_than - - def test_validates_numericality_of_less_than_generates_message - Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 - @topic.title = 1 - @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => nil}) - @topic.valid? - end - - def test_validates_numericality_of_odd_generates_message_with_custom_default_message - Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' - @topic.title = 1 - @topic.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) - @topic.valid? - end + # validates_numericality_of :less_than w/o mocha def test_validates_numericality_of_less_than_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:title => {:less_than => 'custom message'}}}}} @@ -546,19 +602,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase end - # validates_associated - - def test_validates_associated_generates_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? - end - - def test_validates_associated_generates_message_with_custom_default_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? - end + # validates_associated w/o mocha def test_validates_associated_finds_custom_model_key_translation I18n.backend.store_translations 'en-US', :active_record => {:error_messages => {:custom => {:topic => {:replies => {:invalid => 'custom message'}}}}} -- cgit v1.2.3 From 6982acb0793fb6e59f52cab4062344a88e3691ce Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Tue, 1 Jul 2008 16:52:48 +0200 Subject: Experimental I18n charset support for ActionMailer --- actionmailer/lib/action_mailer.rb | 4 + actionmailer/lib/action_mailer/base.rb | 14 ++- actionmailer/lib/action_mailer/locale/en-US.rb | 3 + actionmailer/test/i18n_test.rb | 133 +++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 actionmailer/lib/action_mailer/locale/en-US.rb create mode 100644 actionmailer/test/i18n_test.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 2e324d4637..806edf1f9e 100755 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -49,4 +49,8 @@ ActionMailer::Base.class_eval do helper MailHelper end +I18n.backend.populate do + require 'action_mailer/locale/en-US.rb' +end + silence_warnings { TMail::Encoder.const_set("MAX_LINE_LEN", 200) } diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 1518e23dfe..e787c1b8da 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -307,10 +307,6 @@ module ActionMailer #:nodoc: # Specify the CC addresses for the message. adv_attr_accessor :cc - # Specify the charset to use for the message. This defaults to the - # +default_charset+ specified for ActionMailer::Base. - adv_attr_accessor :charset - # Specify the content type for the message. This defaults to text/plain # in most cases, but can be automatically set in some situations. adv_attr_accessor :content_type @@ -348,6 +344,15 @@ module ActionMailer #:nodoc: # have multiple mailer methods share the same template. adv_attr_accessor :template + # Specify the charset to use for the message. + # It performs a lookup, on the specified charset, then on the charset from + # the current locale, and, finally, on the +default_charset+ specified + # for ActionMailer::Base. + def charset(charset = nil) + @charset ||= charset || :'charset'.t || @@default_charset + end + attr_writer :charset + # Override the mailer name, which defaults to an inflected version of the # mailer's class name. If you want to use a template in a non-standard # location, you can use this to specify that location. @@ -517,7 +522,6 @@ module ActionMailer #:nodoc: # mailer. Subclasses may override this method to provide different # defaults. def initialize_defaults(method_name) - @charset ||= @@default_charset.dup @content_type ||= @@default_content_type.dup @implicit_parts_order ||= @@default_implicit_parts_order.dup @template ||= method_name diff --git a/actionmailer/lib/action_mailer/locale/en-US.rb b/actionmailer/lib/action_mailer/locale/en-US.rb new file mode 100644 index 0000000000..369f2d1a1c --- /dev/null +++ b/actionmailer/lib/action_mailer/locale/en-US.rb @@ -0,0 +1,3 @@ +I18n.backend.store_translations :'en-US', { + :charset => 'utf-8' +} \ No newline at end of file diff --git a/actionmailer/test/i18n_test.rb b/actionmailer/test/i18n_test.rb new file mode 100644 index 0000000000..92b128bce6 --- /dev/null +++ b/actionmailer/test/i18n_test.rb @@ -0,0 +1,133 @@ +require 'abstract_unit' + +class I18nMailer < ActionMailer::Base + def use_locale_charset(recipient) + recipients recipient + subject "using locale charset" + from "tester@example.com" + body "x" + end + + def use_explicit_charset(recipient) + recipients recipient + subject "using explicit charset" + from "tester@example.com" + body "x" + charset "iso-8859-2" + end + + def multiparted(recipient) + recipients recipient + subject "Multiparted" + from "tester@example.com" + body "x" + + part "text/html" do |p| + p.body = "multiparted iso-8859-1 html" + end + + part :content_type => "text/plain", + :body => "multiparted utf-8 text", + :charset => 'utf-8' + end + + def rxml_template(recipient) + recipients recipient + subject "rendering rxml template" + from "tester@example.com" + end + + def initialize_defaults(method_name) + super + mailer_name "test_mailer" + end +end + +I18n.backend.store_translations :'en-GB', { } +I18n.backend.store_translations :'de-DE', { + :charset => 'iso-8859-1' +} + +class I18nTest < Test::Unit::TestCase + def setup + @charset = 'utf-8' + @recipient = 'test@localhost' + end + + def test_should_use_locale_charset + assert_equal @charset, mail.charset + end + + def test_should_use_default_charset_if_no_current_locale + uses_locale nil do + assert_equal @charset, mail.charset + end + end + + def test_mail_headers_should_contains_current_charset + uses_locale 'de-DE' do + assert_match /iso-8859-1/, mail.header['content-type'].body + end + end + + def test_should_use_charset_from_current_locale + uses_locale 'de-DE' do + assert_equal 'iso-8859-1', mail.charset + end + end + + def test_should_raise_exception_if_current_locale_doesnt_specify_a_charset + assert_raise I18n::MissingTranslationData do + uses_locale 'en-GB' do + mail + end + end + end + + def test_should_use_explicit_charset + assert_equal 'iso-8859-2', mail('use_explicit_charset').charset + end + + def test_mail_parts_charsets + uses_locale 'de-DE' do + charsets = mail('multiparted').parts.map(&:charset) + assert_equal 'iso-8859-1', charsets[0] + assert_equal 'iso-8859-1', charsets[1] + assert_equal 'utf-8', charsets[2] + end + end + + def test_mail_parts_headers + uses_locale 'de-DE' do + content_types = mail('multiparted').parts.map(&:header).map do |header| + header['content-type'].body + end + assert_match /iso-8859-1/, content_types[0] + assert_match /iso-8859-1/, content_types[1] + assert_match /utf-8/, content_types[2] + end + end + + # TODO: this case depends on XML Builder, + # should we pass Builder::XmlMarkup.new :encoding => charset_from_i18n ? + def _ignore_test_rxml_template_should_use_current_charset + uses_locale 'de-DE' do + assert_equal "\n", + mail('rxml_template').body.strip + end + end + + private + def mail(method = 'use_locale_charset') + I18nMailer.__send__('create_' + method, @recipient) + end + + def uses_locale(locale, &block) + begin + I18n.locale = locale + yield + ensure + I18n.locale = I18n.default_locale + end + end +end -- cgit v1.2.3 From 7403c825a05af320e20f1b7e20b0c565081ede89 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Wed, 2 Jul 2008 17:51:34 +0200 Subject: Fixed Date and Time localization for ActiveSupport --- actionpack/lib/action_view/locale/en-US.rb | 21 ------- activesupport/lib/active_support/locale/en-US.rb | 21 +++++++ activesupport/test/core_ext/i18n_test.rb | 75 ++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 21 deletions(-) create mode 100644 activesupport/test/core_ext/i18n_test.rb diff --git a/actionpack/lib/action_view/locale/en-US.rb b/actionpack/lib/action_view/locale/en-US.rb index 20d668a9e1..3adb199681 100644 --- a/actionpack/lib/action_view/locale/en-US.rb +++ b/actionpack/lib/action_view/locale/en-US.rb @@ -1,25 +1,4 @@ I18n.backend.store_translations :'en-US', { - :date => { - :formats => { - :default => "%Y-%m-%d", - :short => "%b %d", - :long => "%B %d, %Y", - }, - :day_names => Date::DAYNAMES, - :abbr_day_names => Date::ABBR_DAYNAMES, - :month_names => Date::MONTHNAMES, - :abbr_month_names => Date::ABBR_MONTHNAMES, - :order => [:year, :month, :day] - }, - :time => { - :formats => { - :default => "%a, %d %b %Y %H:%M:%S %z", - :short => "%d %b %H:%M", - :long => "%B %d, %Y %H:%M", - }, - :am => 'am', - :pm => 'pm' - }, :datetime => { :distance_in_words => { :half_a_minute => 'half a minute', diff --git a/activesupport/lib/active_support/locale/en-US.rb b/activesupport/lib/active_support/locale/en-US.rb index aa06fe14bd..51324a90bf 100644 --- a/activesupport/lib/active_support/locale/en-US.rb +++ b/activesupport/lib/active_support/locale/en-US.rb @@ -3,5 +3,26 @@ I18n.backend.store_translations :'en-US', { :array => { :sentence_connector => 'and' } + }, + :date => { + :formats => { + :default => "%Y-%m-%d", + :short => "%b %d", + :long => "%B %d, %Y", + }, + :day_names => Date::DAYNAMES, + :abbr_day_names => Date::ABBR_DAYNAMES, + :month_names => Date::MONTHNAMES, + :abbr_month_names => Date::ABBR_MONTHNAMES, + :order => [:year, :month, :day] + }, + :time => { + :formats => { + :default => "%a, %d %b %Y %H:%M:%S %z", + :short => "%d %b %H:%M", + :long => "%B %d, %Y %H:%M", + }, + :am => 'am', + :pm => 'pm' } } \ No newline at end of file diff --git a/activesupport/test/core_ext/i18n_test.rb b/activesupport/test/core_ext/i18n_test.rb new file mode 100644 index 0000000000..a67b6a5d8c --- /dev/null +++ b/activesupport/test/core_ext/i18n_test.rb @@ -0,0 +1,75 @@ +require 'abstract_unit' + +class I18nTest < Test::Unit::TestCase + def setup + @date = Date.parse("2008-7-2") + @time = Time.utc(2008, 7, 2, 16, 47, 1) + end + + uses_mocha 'I18nTimeZoneTest' do + def test_time_zone_localization_with_default_format + Time.zone.stubs(:now).returns Time.local(2000) + assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", Time.zone.now.l + end + end + + def test_date_localization_should_use_default_format + assert_equal "2008-07-02", @date.l + end + + def test_date_localization_with_default_format + assert_equal "2008-07-02", @date.l(nil, :default) + end + + def test_date_localization_with_short_format + assert_equal "Jul 02", @date.l(nil, :short) + end + + def test_date_localization_with_long_format + assert_equal "July 02, 2008", @date.l(nil, :long) + end + + def test_time_localization_should_use_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l + end + + def test_time_localization_with_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l(nil, :default) + end + + def test_time_localization_with_short_format + assert_equal "02 Jul 16:47", @time.l(nil, :short) + end + + def test_time_localization_with_long_format + assert_equal "July 02, 2008 16:47", @time.l(nil, :long) + end + + def test_day_names + assert_equal Date::DAYNAMES, :'date.day_names'.t + end + + def test_abbr_day_names + assert_equal Date::ABBR_DAYNAMES, :'date.abbr_day_names'.t + end + + def test_month_names + assert_equal Date::MONTHNAMES, :'date.month_names'.t + end + + def test_abbr_month_names + assert_equal Date::ABBR_MONTHNAMES, :'date.abbr_month_names'.t + end + + def test_date_order + assert_equal [:year, :month, :day], :'date.order'.t + end + + def test_time_am + assert_equal 'am', :'time.am'.t + end + + def test_time_pm + assert_equal 'pm', :'time.pm'.t + end +end -- cgit v1.2.3 From 8f74ba96c47e77e18ce363c8e7cd2fc9196faf7a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 2 Jul 2008 19:21:07 +0200 Subject: remove core extensions in favor of I18n#translate and I18n#localize --- actionmailer/lib/action_mailer/base.rb | 2 +- actionpack/lib/action_view/helpers/date_helper.rb | 5 +- .../lib/action_view/helpers/number_helper.rb | 2 +- activerecord/lib/active_record/validations.rb | 6 +- activerecord/test/cases/validations_i18n_test.rb | 2 +- .../active_support/core_ext/array/conversions.rb | 2 +- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- activesupport/test/core_ext/i18n_test.rb | 75 ---------------------- activesupport/test/i18n_test.rb | 75 ++++++++++++++++++++++ 9 files changed, 86 insertions(+), 85 deletions(-) delete mode 100644 activesupport/test/core_ext/i18n_test.rb create mode 100644 activesupport/test/i18n_test.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e787c1b8da..f7da90d10f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -349,7 +349,7 @@ module ActionMailer #:nodoc: # the current locale, and, finally, on the +default_charset+ specified # for ActionMailer::Base. def charset(charset = nil) - @charset ||= charset || :'charset'.t || @@default_charset + @charset ||= charset || I18n.translate(:charset) || @@default_charset end attr_writer :charset diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 6ac4171fd5..d306c7a742 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -512,7 +512,8 @@ module ActionView else month_options = [] month_names = options[:use_month_names] || begin - (options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names').t locale + key = options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names' + I18n.translate key, locale end month_names.unshift(nil) if month_names.size < 13 @@ -632,7 +633,7 @@ module ActionView position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 } - order = options[:order] ||= :'date.order'.t(locale) + order = options[:order] ||= I18n.translate(:'date.order', locale) # Discard explicit and implicit by not being included in the :order discard = {} diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 3e0d5b1db4..981589437d 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -70,7 +70,7 @@ module ActionView # # => 1234567890,50 £ def number_to_currency(number, options = {}) options = options.symbolize_keys - defaults = :'currency.format'.t(options[:locale]) || {} + defaults = I18n.translate(:'currency.format', options[:locale]) || {} precision = options[:precision] || defaults[:precision] unit = options[:unit] || defaults[:unit] diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 5bbd10394c..5245f65869 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -22,7 +22,7 @@ module ActiveRecord class << self def default_error_messages ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use 'active_record.error_messages'.t.") - 'active_record.error_messages'.t + I18n.translate 'active_record.error_messages' end end @@ -43,7 +43,7 @@ module ActiveRecord # error can be added to the same +attribute+ in which case an array will be returned on a call to on(attribute). # If no +msg+ is supplied, "invalid" is assumed. def add(attribute, message = nil) - message ||= :"active_record.error_messages.invalid".t + message ||= I18n.translate :"active_record.error_messages.invalid" @errors[attribute.to_s] ||= [] @errors[attribute.to_s] << message end @@ -168,7 +168,7 @@ module ActiveRecord full_messages << message else key = :"active_record.human_attribute_names.#{@base.class.name.underscore.to_sym}.#{attr}" - attr_name = key.t(locale) || @base.class.human_attribute_name(attr) + attr_name = I18n.translate(key, locale, :raise => true) rescue @base.class.human_attribute_name(attr) full_messages << attr_name + " " + message end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 53e90f4d53..840fcd81d0 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -79,7 +79,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } - I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US').returns('Title') + I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US', :raise => true).returns('Title') @topic.errors.full_messages :locale => 'en-US' end end diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 80d91a6cbd..80bf1404de 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -13,7 +13,7 @@ module ActiveSupport #:nodoc: locale = options[:locale] locale ||= self.locale if respond_to?(:locale) - default = :'support.array.sentence_connector'.t(locale) + default = I18n.translate(:'support.array.sentence_connector', locale) options.reverse_merge! :connector => default, :skip_last_comma => false options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == '' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 20c331666b..970bc7ab5f 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 20c331666b3b6a21791d4cded53c3d8654fba714 +Subproject commit 970bc7ab5faa94e41ee4a56bc8913c144c1cdd19 diff --git a/activesupport/test/core_ext/i18n_test.rb b/activesupport/test/core_ext/i18n_test.rb deleted file mode 100644 index a67b6a5d8c..0000000000 --- a/activesupport/test/core_ext/i18n_test.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'abstract_unit' - -class I18nTest < Test::Unit::TestCase - def setup - @date = Date.parse("2008-7-2") - @time = Time.utc(2008, 7, 2, 16, 47, 1) - end - - uses_mocha 'I18nTimeZoneTest' do - def test_time_zone_localization_with_default_format - Time.zone.stubs(:now).returns Time.local(2000) - assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", Time.zone.now.l - end - end - - def test_date_localization_should_use_default_format - assert_equal "2008-07-02", @date.l - end - - def test_date_localization_with_default_format - assert_equal "2008-07-02", @date.l(nil, :default) - end - - def test_date_localization_with_short_format - assert_equal "Jul 02", @date.l(nil, :short) - end - - def test_date_localization_with_long_format - assert_equal "July 02, 2008", @date.l(nil, :long) - end - - def test_time_localization_should_use_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l - end - - def test_time_localization_with_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l(nil, :default) - end - - def test_time_localization_with_short_format - assert_equal "02 Jul 16:47", @time.l(nil, :short) - end - - def test_time_localization_with_long_format - assert_equal "July 02, 2008 16:47", @time.l(nil, :long) - end - - def test_day_names - assert_equal Date::DAYNAMES, :'date.day_names'.t - end - - def test_abbr_day_names - assert_equal Date::ABBR_DAYNAMES, :'date.abbr_day_names'.t - end - - def test_month_names - assert_equal Date::MONTHNAMES, :'date.month_names'.t - end - - def test_abbr_month_names - assert_equal Date::ABBR_MONTHNAMES, :'date.abbr_month_names'.t - end - - def test_date_order - assert_equal [:year, :month, :day], :'date.order'.t - end - - def test_time_am - assert_equal 'am', :'time.am'.t - end - - def test_time_pm - assert_equal 'pm', :'time.pm'.t - end -end diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb new file mode 100644 index 0000000000..e6d90f09c1 --- /dev/null +++ b/activesupport/test/i18n_test.rb @@ -0,0 +1,75 @@ +require 'abstract_unit' + +class I18nTest < Test::Unit::TestCase + def setup + @date = Date.parse("2008-7-2") + @time = Time.utc(2008, 7, 2, 16, 47, 1) + end + + uses_mocha 'I18nTimeZoneTest' do + def test_time_zone_localization_with_default_format + Time.zone.stubs(:now).returns Time.local(2000) + assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", I18n.localize(Time.zone.now) + end + end + + def test_date_localization_should_use_default_format + assert_equal "2008-07-02", I18n.localize(@date) + end + + def test_date_localization_with_default_format + assert_equal "2008-07-02", I18n.localize(@date, nil, :default) + end + + def test_date_localization_with_short_format + assert_equal "Jul 02", I18n.localize(@date, nil, :short) + end + + def test_date_localization_with_long_format + assert_equal "July 02, 2008", I18n.localize(@date, nil, :long) + end + + def test_time_localization_should_use_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time) + end + + def test_time_localization_with_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, nil, :default) + end + + def test_time_localization_with_short_format + assert_equal "02 Jul 16:47", I18n.localize(@time, nil, :short) + end + + def test_time_localization_with_long_format + assert_equal "July 02, 2008 16:47", I18n.localize(@time, nil, :long) + end + + def test_day_names + assert_equal Date::DAYNAMES, I18n.translate(:'date.day_names') + end + + def test_abbr_day_names + assert_equal Date::ABBR_DAYNAMES, I18n.translate(:'date.abbr_day_names') + end + + def test_month_names + assert_equal Date::MONTHNAMES, I18n.translate(:'date.month_names') + end + + def test_abbr_month_names + assert_equal Date::ABBR_MONTHNAMES, I18n.translate(:'date.abbr_month_names') + end + + def test_date_order + assert_equal [:year, :month, :day], I18n.translate(:'date.order') + end + + def test_time_am + assert_equal 'am', I18n.translate(:'time.am') + end + + def test_time_pm + assert_equal 'pm', I18n.translate(:'time.pm') + end +end -- cgit v1.2.3 From d41e4c1c3d6e6259f1cfc0cdbd4fc30fee0f866a Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Thu, 3 Jul 2008 11:50:18 +0200 Subject: Make sure ActionMailer use default charset if no defined by current locale --- actionmailer/lib/action_mailer/base.rb | 4 ++-- actionmailer/test/i18n_test.rb | 26 ++++++++++++-------------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index f7da90d10f..a1a7f55d2b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -346,10 +346,10 @@ module ActionMailer #:nodoc: # Specify the charset to use for the message. # It performs a lookup, on the specified charset, then on the charset from - # the current locale, and, finally, on the +default_charset+ specified + # the current locale, and, in the end, on the +default_charset+ specified # for ActionMailer::Base. def charset(charset = nil) - @charset ||= charset || I18n.translate(:charset) || @@default_charset + @charset ||= charset || I18n.translate(:charset, :default => @@default_charset) end attr_writer :charset diff --git a/actionmailer/test/i18n_test.rb b/actionmailer/test/i18n_test.rb index 92b128bce6..a775128519 100644 --- a/actionmailer/test/i18n_test.rb +++ b/actionmailer/test/i18n_test.rb @@ -59,37 +59,35 @@ class I18nTest < Test::Unit::TestCase end def test_should_use_default_charset_if_no_current_locale - uses_locale nil do + with_locale nil do assert_equal @charset, mail.charset end end def test_mail_headers_should_contains_current_charset - uses_locale 'de-DE' do + with_locale 'de-DE' do assert_match /iso-8859-1/, mail.header['content-type'].body end end def test_should_use_charset_from_current_locale - uses_locale 'de-DE' do + with_locale 'de-DE' do assert_equal 'iso-8859-1', mail.charset end end - - def test_should_raise_exception_if_current_locale_doesnt_specify_a_charset - assert_raise I18n::MissingTranslationData do - uses_locale 'en-GB' do - mail - end + + def test_should_use_default_charset_if_missing_for_current_locale + with_locale 'en-GB' do + assert_equal @charset, mail.charset end end - + def test_should_use_explicit_charset assert_equal 'iso-8859-2', mail('use_explicit_charset').charset end def test_mail_parts_charsets - uses_locale 'de-DE' do + with_locale 'de-DE' do charsets = mail('multiparted').parts.map(&:charset) assert_equal 'iso-8859-1', charsets[0] assert_equal 'iso-8859-1', charsets[1] @@ -98,7 +96,7 @@ class I18nTest < Test::Unit::TestCase end def test_mail_parts_headers - uses_locale 'de-DE' do + with_locale 'de-DE' do content_types = mail('multiparted').parts.map(&:header).map do |header| header['content-type'].body end @@ -111,7 +109,7 @@ class I18nTest < Test::Unit::TestCase # TODO: this case depends on XML Builder, # should we pass Builder::XmlMarkup.new :encoding => charset_from_i18n ? def _ignore_test_rxml_template_should_use_current_charset - uses_locale 'de-DE' do + with_locale 'de-DE' do assert_equal "\n", mail('rxml_template').body.strip end @@ -122,7 +120,7 @@ class I18nTest < Test::Unit::TestCase I18nMailer.__send__('create_' + method, @recipient) end - def uses_locale(locale, &block) + def with_locale(locale, &block) begin I18n.locale = locale yield -- cgit v1.2.3 From e1a7f83fca862fd7472ef6b80f8b6a8d33849a8e Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Fri, 4 Jul 2008 22:22:20 +0200 Subject: use :default for human_attribute_name --- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_i18n_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 5245f65869..8ba09b3992 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -168,7 +168,7 @@ module ActiveRecord full_messages << message else key = :"active_record.human_attribute_names.#{@base.class.name.underscore.to_sym}.#{attr}" - attr_name = I18n.translate(key, locale, :raise => true) rescue @base.class.human_attribute_name(attr) + attr_name = I18n.translate(key, locale, :default => @base.class.human_attribute_name(attr)) full_messages << attr_name + " " + message end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 840fcd81d0..5be518c547 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -79,7 +79,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } - I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US', :raise => true).returns('Title') + I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US', :default => 'Title').returns('Title') @topic.errors.full_messages :locale => 'en-US' end end -- cgit v1.2.3 From 8305d6759abe2b8511ccee35fd0998e6fb0dffd0 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Fri, 4 Jul 2008 22:26:53 +0200 Subject: Reverting changes to ActionMailer Revert "Make sure ActionMailer use default charset if no defined by current locale" This reverts commit d41e4c1c3d6e6259f1cfc0cdbd4fc30fee0f866a. --- actionmailer/lib/action_mailer/base.rb | 4 ++-- actionmailer/test/i18n_test.rb | 26 ++++++++++++++------------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index a1a7f55d2b..f7da90d10f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -346,10 +346,10 @@ module ActionMailer #:nodoc: # Specify the charset to use for the message. # It performs a lookup, on the specified charset, then on the charset from - # the current locale, and, in the end, on the +default_charset+ specified + # the current locale, and, finally, on the +default_charset+ specified # for ActionMailer::Base. def charset(charset = nil) - @charset ||= charset || I18n.translate(:charset, :default => @@default_charset) + @charset ||= charset || I18n.translate(:charset) || @@default_charset end attr_writer :charset diff --git a/actionmailer/test/i18n_test.rb b/actionmailer/test/i18n_test.rb index a775128519..92b128bce6 100644 --- a/actionmailer/test/i18n_test.rb +++ b/actionmailer/test/i18n_test.rb @@ -59,35 +59,37 @@ class I18nTest < Test::Unit::TestCase end def test_should_use_default_charset_if_no_current_locale - with_locale nil do + uses_locale nil do assert_equal @charset, mail.charset end end def test_mail_headers_should_contains_current_charset - with_locale 'de-DE' do + uses_locale 'de-DE' do assert_match /iso-8859-1/, mail.header['content-type'].body end end def test_should_use_charset_from_current_locale - with_locale 'de-DE' do + uses_locale 'de-DE' do assert_equal 'iso-8859-1', mail.charset end end - - def test_should_use_default_charset_if_missing_for_current_locale - with_locale 'en-GB' do - assert_equal @charset, mail.charset + + def test_should_raise_exception_if_current_locale_doesnt_specify_a_charset + assert_raise I18n::MissingTranslationData do + uses_locale 'en-GB' do + mail + end end end - + def test_should_use_explicit_charset assert_equal 'iso-8859-2', mail('use_explicit_charset').charset end def test_mail_parts_charsets - with_locale 'de-DE' do + uses_locale 'de-DE' do charsets = mail('multiparted').parts.map(&:charset) assert_equal 'iso-8859-1', charsets[0] assert_equal 'iso-8859-1', charsets[1] @@ -96,7 +98,7 @@ class I18nTest < Test::Unit::TestCase end def test_mail_parts_headers - with_locale 'de-DE' do + uses_locale 'de-DE' do content_types = mail('multiparted').parts.map(&:header).map do |header| header['content-type'].body end @@ -109,7 +111,7 @@ class I18nTest < Test::Unit::TestCase # TODO: this case depends on XML Builder, # should we pass Builder::XmlMarkup.new :encoding => charset_from_i18n ? def _ignore_test_rxml_template_should_use_current_charset - with_locale 'de-DE' do + uses_locale 'de-DE' do assert_equal "\n", mail('rxml_template').body.strip end @@ -120,7 +122,7 @@ class I18nTest < Test::Unit::TestCase I18nMailer.__send__('create_' + method, @recipient) end - def with_locale(locale, &block) + def uses_locale(locale, &block) begin I18n.locale = locale yield -- cgit v1.2.3 From 34bd1e95c7a08c9266fc99281417dad4a19cdf73 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Fri, 4 Jul 2008 22:34:32 +0200 Subject: Reverting changes to ActionMailer Revert "Experimental I18n charset support for ActionMailer" This reverts commit 6982acb0793fb6e59f52cab4062344a88e3691ce. Conflicts: actionmailer/lib/action_mailer/base.rb --- actionmailer/lib/action_mailer.rb | 4 - actionmailer/lib/action_mailer/base.rb | 9 -- actionmailer/lib/action_mailer/locale/en-US.rb | 3 - actionmailer/test/i18n_test.rb | 133 ------------------------- 4 files changed, 149 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/locale/en-US.rb delete mode 100644 actionmailer/test/i18n_test.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 806edf1f9e..2e324d4637 100755 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -49,8 +49,4 @@ ActionMailer::Base.class_eval do helper MailHelper end -I18n.backend.populate do - require 'action_mailer/locale/en-US.rb' -end - silence_warnings { TMail::Encoder.const_set("MAX_LINE_LEN", 200) } diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index f7da90d10f..b1f23583a6 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -344,15 +344,6 @@ module ActionMailer #:nodoc: # have multiple mailer methods share the same template. adv_attr_accessor :template - # Specify the charset to use for the message. - # It performs a lookup, on the specified charset, then on the charset from - # the current locale, and, finally, on the +default_charset+ specified - # for ActionMailer::Base. - def charset(charset = nil) - @charset ||= charset || I18n.translate(:charset) || @@default_charset - end - attr_writer :charset - # Override the mailer name, which defaults to an inflected version of the # mailer's class name. If you want to use a template in a non-standard # location, you can use this to specify that location. diff --git a/actionmailer/lib/action_mailer/locale/en-US.rb b/actionmailer/lib/action_mailer/locale/en-US.rb deleted file mode 100644 index 369f2d1a1c..0000000000 --- a/actionmailer/lib/action_mailer/locale/en-US.rb +++ /dev/null @@ -1,3 +0,0 @@ -I18n.backend.store_translations :'en-US', { - :charset => 'utf-8' -} \ No newline at end of file diff --git a/actionmailer/test/i18n_test.rb b/actionmailer/test/i18n_test.rb deleted file mode 100644 index 92b128bce6..0000000000 --- a/actionmailer/test/i18n_test.rb +++ /dev/null @@ -1,133 +0,0 @@ -require 'abstract_unit' - -class I18nMailer < ActionMailer::Base - def use_locale_charset(recipient) - recipients recipient - subject "using locale charset" - from "tester@example.com" - body "x" - end - - def use_explicit_charset(recipient) - recipients recipient - subject "using explicit charset" - from "tester@example.com" - body "x" - charset "iso-8859-2" - end - - def multiparted(recipient) - recipients recipient - subject "Multiparted" - from "tester@example.com" - body "x" - - part "text/html" do |p| - p.body = "multiparted iso-8859-1 html" - end - - part :content_type => "text/plain", - :body => "multiparted utf-8 text", - :charset => 'utf-8' - end - - def rxml_template(recipient) - recipients recipient - subject "rendering rxml template" - from "tester@example.com" - end - - def initialize_defaults(method_name) - super - mailer_name "test_mailer" - end -end - -I18n.backend.store_translations :'en-GB', { } -I18n.backend.store_translations :'de-DE', { - :charset => 'iso-8859-1' -} - -class I18nTest < Test::Unit::TestCase - def setup - @charset = 'utf-8' - @recipient = 'test@localhost' - end - - def test_should_use_locale_charset - assert_equal @charset, mail.charset - end - - def test_should_use_default_charset_if_no_current_locale - uses_locale nil do - assert_equal @charset, mail.charset - end - end - - def test_mail_headers_should_contains_current_charset - uses_locale 'de-DE' do - assert_match /iso-8859-1/, mail.header['content-type'].body - end - end - - def test_should_use_charset_from_current_locale - uses_locale 'de-DE' do - assert_equal 'iso-8859-1', mail.charset - end - end - - def test_should_raise_exception_if_current_locale_doesnt_specify_a_charset - assert_raise I18n::MissingTranslationData do - uses_locale 'en-GB' do - mail - end - end - end - - def test_should_use_explicit_charset - assert_equal 'iso-8859-2', mail('use_explicit_charset').charset - end - - def test_mail_parts_charsets - uses_locale 'de-DE' do - charsets = mail('multiparted').parts.map(&:charset) - assert_equal 'iso-8859-1', charsets[0] - assert_equal 'iso-8859-1', charsets[1] - assert_equal 'utf-8', charsets[2] - end - end - - def test_mail_parts_headers - uses_locale 'de-DE' do - content_types = mail('multiparted').parts.map(&:header).map do |header| - header['content-type'].body - end - assert_match /iso-8859-1/, content_types[0] - assert_match /iso-8859-1/, content_types[1] - assert_match /utf-8/, content_types[2] - end - end - - # TODO: this case depends on XML Builder, - # should we pass Builder::XmlMarkup.new :encoding => charset_from_i18n ? - def _ignore_test_rxml_template_should_use_current_charset - uses_locale 'de-DE' do - assert_equal "\n", - mail('rxml_template').body.strip - end - end - - private - def mail(method = 'use_locale_charset') - I18nMailer.__send__('create_' + method, @recipient) - end - - def uses_locale(locale, &block) - begin - I18n.locale = locale - yield - ensure - I18n.locale = I18n.default_locale - end - end -end -- cgit v1.2.3 From a865d19516a716762b46ca29212bac668dd7c4a0 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Fri, 4 Jul 2008 22:45:53 +0200 Subject: reverting changes to ActionMailer --- actionmailer/lib/action_mailer/base.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index b1f23583a6..1518e23dfe 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -307,6 +307,10 @@ module ActionMailer #:nodoc: # Specify the CC addresses for the message. adv_attr_accessor :cc + # Specify the charset to use for the message. This defaults to the + # +default_charset+ specified for ActionMailer::Base. + adv_attr_accessor :charset + # Specify the content type for the message. This defaults to text/plain # in most cases, but can be automatically set in some situations. adv_attr_accessor :content_type @@ -513,6 +517,7 @@ module ActionMailer #:nodoc: # mailer. Subclasses may override this method to provide different # defaults. def initialize_defaults(method_name) + @charset ||= @@default_charset.dup @content_type ||= @@default_content_type.dup @implicit_parts_order ||= @@default_implicit_parts_order.dup @template ||= method_name -- cgit v1.2.3 From c9ed2c9bd24b9f4cdfcb692151f87ba900469e71 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 6 Jul 2008 19:00:55 +0200 Subject: add a translation helper --- .../lib/action_view/helpers/translation_helper.rb | 16 +++++++++ .../test/template/translation_helper_test.rb | 42 ++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 actionpack/lib/action_view/helpers/translation_helper.rb create mode 100644 actionpack/test/template/translation_helper_test.rb diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb new file mode 100644 index 0000000000..0bfe6bf771 --- /dev/null +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -0,0 +1,16 @@ +require 'action_view/helpers/tag_helper' + +module ActionView + module Helpers + module TranslationHelper + def translate(*args) + key, locale, options = I18n.send :process_translate_arguments, *args + I18n.translate key, locale, options.merge(:raise => true) + + rescue I18n::MissingTranslationData => e + keys = I18n.send :normalize_translation_keys, locale, key, options[:scope] + content_tag('span', keys.join(', '), :class => 'translation_missing') + end + end + end +end \ No newline at end of file diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb new file mode 100644 index 0000000000..e97bcdb731 --- /dev/null +++ b/actionpack/test/template/translation_helper_test.rb @@ -0,0 +1,42 @@ +require 'abstract_unit' + +class TranslationHelperTest < Test::Unit::TestCase + include ActionView::Helpers::TagHelper + include ActionView::Helpers::TranslationHelper + + attr_reader :request + uses_mocha 'translation_helper_test' do + def setup + end + + def test_delegates_to_i18n_setting_the_raise_option + I18n.expects(:translate).with(:foo, 'en-US', :raise => true) + translate :foo, 'en-US' + end + + def test_returns_missing_translation_message_wrapped_into_span + expected = 'en-US, foo' + assert_equal expected, translate(:foo) + end + + # def test_error_messages_for_given_a_header_message_option_it_does_not_translate_header_message + # I18n.expects(:translate).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').never + # error_messages_for(:object => @object, :header_message => 'header message', :locale => 'en-US') + # end + # + # def test_error_messages_for_given_no_header_message_option_it_translates_header_message + # I18n.expects(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns 'header message' + # error_messages_for(:object => @object, :locale => 'en-US') + # end + # + # def test_error_messages_for_given_a_message_option_it_does_not_translate_message + # I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).never + # error_messages_for(:object => @object, :message => 'message', :locale => 'en-US') + # end + # + # def test_error_messages_for_given_no_message_option_it_translates_message + # I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' + # error_messages_for(:object => @object, :locale => 'en-US') + # end + end +end \ No newline at end of file -- cgit v1.2.3 From 84816ae981a8598e5e401eb1b9b805de840fefc9 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 6 Jul 2008 21:20:02 +0200 Subject: align with changes in i18n --- actionpack/lib/action_view/helpers/date_helper.rb | 5 ++--- actionpack/lib/action_view/helpers/number_helper.rb | 2 +- actionpack/lib/action_view/helpers/translation_helper.rb | 6 +++--- actionpack/test/template/date_helper_i18n_test.rb | 8 ++++---- actionpack/test/template/number_helper_i18n_test.rb | 2 +- activerecord/lib/active_record/validations.rb | 5 ++--- activerecord/test/cases/validations_i18n_test.rb | 6 +++--- .../lib/active_support/core_ext/array/conversions.rb | 5 +---- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- activesupport/test/i18n_test.rb | 12 ++++++------ 10 files changed, 24 insertions(+), 29 deletions(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index d306c7a742..61fff42d5a 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -504,7 +504,6 @@ module ActionView # def select_month(date, options = {}, html_options = {}) locale = options[:locale] - locale ||= self.locale if respond_to?(:locale) val = date ? (date.kind_of?(Fixnum) ? date : date.month) : '' if options[:use_hidden] @@ -513,7 +512,7 @@ module ActionView month_options = [] month_names = options[:use_month_names] || begin key = options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names' - I18n.translate key, locale + I18n.translate key, :locale => locale end month_names.unshift(nil) if month_names.size < 13 @@ -633,7 +632,7 @@ module ActionView position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 } - order = options[:order] ||= I18n.translate(:'date.order', locale) + order = options[:order] ||= I18n.translate(:'date.order', :locale => locale) # Discard explicit and implicit by not being included in the :order discard = {} diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 981589437d..6bb8263794 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -70,7 +70,7 @@ module ActionView # # => 1234567890,50 £ def number_to_currency(number, options = {}) options = options.symbolize_keys - defaults = I18n.translate(:'currency.format', options[:locale]) || {} + defaults = I18n.translate(:'currency.format', :locale => options[:locale]) || {} precision = options[:precision] || defaults[:precision] unit = options[:unit] || defaults[:unit] diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 0bfe6bf771..c13c2dfc04 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -4,11 +4,11 @@ module ActionView module Helpers module TranslationHelper def translate(*args) - key, locale, options = I18n.send :process_translate_arguments, *args - I18n.translate key, locale, options.merge(:raise => true) + args << args.extract_options!.merge(:raise => true) + I18n.translate *args rescue I18n::MissingTranslationData => e - keys = I18n.send :normalize_translation_keys, locale, key, options[:scope] + keys = I18n.send :normalize_translation_keys, e.locale, e.key, e.options[:scope] content_tag('span', keys.join(', '), :class => 'translation_missing') end end diff --git a/actionpack/test/template/date_helper_i18n_test.rb b/actionpack/test/template/date_helper_i18n_test.rb index aeb06c55ea..aca3593921 100644 --- a/actionpack/test/template/date_helper_i18n_test.rb +++ b/actionpack/test/template/date_helper_i18n_test.rb @@ -56,7 +56,7 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase uses_mocha 'date_helper_select_tags_i18n_tests' do def setup - I18n.stubs(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES + I18n.stubs(:translate).with(:'date.month_names', :locale => 'en-US').returns Date::MONTHNAMES end # select_month @@ -67,12 +67,12 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase end def test_select_month_translates_monthnames - I18n.expects(:translate).with(:'date.month_names', 'en-US').returns Date::MONTHNAMES + I18n.expects(:translate).with(:'date.month_names', :locale => 'en-US').returns Date::MONTHNAMES select_month(8, :locale => 'en-US') end def test_select_month_given_use_short_month_option_translates_abbr_monthnames - I18n.expects(:translate).with(:'date.abbr_month_names', 'en-US').returns Date::ABBR_MONTHNAMES + I18n.expects(:translate).with(:'date.abbr_month_names', :locale => 'en-US').returns Date::ABBR_MONTHNAMES select_month(8, :locale => 'en-US', :use_short_month => true) end @@ -84,7 +84,7 @@ class DateHelperSelectTagsI18nTests < Test::Unit::TestCase end def test_date_or_time_select_given_no_order_options_translates_order - I18n.expects(:translate).with(:'date.order', 'en-US').returns [:year, :month, :day] + I18n.expects(:translate).with(:'date.order', :locale => 'en-US').returns [:year, :month, :day] datetime_select('post', 'updated_at', :locale => 'en-US') end end diff --git a/actionpack/test/template/number_helper_i18n_test.rb b/actionpack/test/template/number_helper_i18n_test.rb index be40ddbc88..50c20c3627 100644 --- a/actionpack/test/template/number_helper_i18n_test.rb +++ b/actionpack/test/template/number_helper_i18n_test.rb @@ -11,7 +11,7 @@ class NumberHelperI18nTests < Test::Unit::TestCase end def test_number_to_currency_translates_currency_formats - I18n.expects(:translate).with(:'currency.format', 'en-US').returns @defaults + I18n.expects(:translate).with(:'currency.format', :locale => 'en-US').returns @defaults number_to_currency(1, :locale => 'en-US') end end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 8ba09b3992..a328c4d927 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -70,7 +70,7 @@ module ActiveRecord msgs << options[:default] if options[:default] msgs << key - I18n.t options.merge(:default => msgs, :scope => [:active_record, :error_messages]) + I18n.t nil, options.merge(:default => msgs, :scope => [:active_record, :error_messages]) end # Returns true if the specified +attribute+ has errors associated with it. @@ -158,7 +158,6 @@ module ActiveRecord # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"] def full_messages(options = {}) full_messages = [] - locale = options[:locale] @errors.each_key do |attr| @errors[attr].each do |message| @@ -168,7 +167,7 @@ module ActiveRecord full_messages << message else key = :"active_record.human_attribute_names.#{@base.class.name.underscore.to_sym}.#{attr}" - attr_name = I18n.translate(key, locale, :default => @base.class.human_attribute_name(attr)) + attr_name = I18n.translate(key, :locale => options[:locale], :default => @base.class.human_attribute_name(attr)) full_messages << attr_name + " " + message end end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations_i18n_test.rb index 5be518c547..86834fe920 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations_i18n_test.rb @@ -46,14 +46,14 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase global_scope = [:active_record, :error_messages] custom_scope = global_scope + [:custom, 'topic', :title] - I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] + I18n.expects(:t).with nil, :scope => [:active_record, :error_messages], :default => [:"custom.topic.title.invalid", 'default from class def', :invalid] @topic.errors.generate_message :title, :invalid, :default => 'default from class def' end def test_errors_generate_message_translates_custom_model_attribute_keys_with_sti custom_scope = [:active_record, :error_messages, :custom, 'topic', :title] - I18n.expects(:t).with :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] + I18n.expects(:t).with nil, :scope => [:active_record, :error_messages], :default => [:"custom.reply.title.invalid", :"custom.topic.title.invalid", 'default from class def', :invalid] Reply.new.errors.generate_message :title, :invalid, :default => 'default from class def' end @@ -79,7 +79,7 @@ class ActiveRecordValidationsI18nTests < Test::Unit::TestCase def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @topic.errors.instance_variable_set :@errors, { 'title' => 'empty' } - I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", 'en-US', :default => 'Title').returns('Title') + I18n.expects(:translate).with(:"active_record.human_attribute_names.topic.title", :locale => 'en-US', :default => 'Title').returns('Title') @topic.errors.full_messages :locale => 'en-US' end end diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 80bf1404de..59dc96754f 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -10,10 +10,7 @@ module ActiveSupport #:nodoc: def to_sentence(options = {}) options.assert_valid_keys(:connector, :skip_last_comma, :locale) - locale = options[:locale] - locale ||= self.locale if respond_to?(:locale) - - default = I18n.translate(:'support.array.sentence_connector', locale) + default = I18n.translate(:'support.array.sentence_connector', :locale => options[:locale]) options.reverse_merge! :connector => default, :skip_last_comma => false options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == '' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 970bc7ab5f..46aad28993 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 970bc7ab5faa94e41ee4a56bc8913c144c1cdd19 +Subproject commit 46aad289935eaf059c429acb5f3bfa0946f2d99f diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb index e6d90f09c1..17074b6cc6 100644 --- a/activesupport/test/i18n_test.rb +++ b/activesupport/test/i18n_test.rb @@ -18,15 +18,15 @@ class I18nTest < Test::Unit::TestCase end def test_date_localization_with_default_format - assert_equal "2008-07-02", I18n.localize(@date, nil, :default) + assert_equal "2008-07-02", I18n.localize(@date, :format => :default) end def test_date_localization_with_short_format - assert_equal "Jul 02", I18n.localize(@date, nil, :short) + assert_equal "Jul 02", I18n.localize(@date, :format => :short) end def test_date_localization_with_long_format - assert_equal "July 02, 2008", I18n.localize(@date, nil, :long) + assert_equal "July 02, 2008", I18n.localize(@date, :format => :long) end def test_time_localization_should_use_default_format @@ -34,15 +34,15 @@ class I18nTest < Test::Unit::TestCase end def test_time_localization_with_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, nil, :default) + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, :format => :default) end def test_time_localization_with_short_format - assert_equal "02 Jul 16:47", I18n.localize(@time, nil, :short) + assert_equal "02 Jul 16:47", I18n.localize(@time, :format => :short) end def test_time_localization_with_long_format - assert_equal "July 02, 2008 16:47", I18n.localize(@time, nil, :long) + assert_equal "July 02, 2008 16:47", I18n.localize(@time, :format => :long) end def test_day_names -- cgit v1.2.3 From 826c3db42105518b3a88cf56e348b48c1660f850 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Mon, 7 Jul 2008 22:46:16 +0200 Subject: Updated ActiveRecord::Errors#default_error_messages deprecation warning according to i18n changes --- activerecord/lib/active_record/validations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index a328c4d927..83d55f23ea 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -21,7 +21,7 @@ module ActiveRecord class << self def default_error_messages - ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use 'active_record.error_messages'.t.") + ActiveSupport::Deprecation.warn("ActiveRecord::Errors.default_error_messages has been deprecated. Please use I18n.translate('active_record.error_messages').") I18n.translate 'active_record.error_messages' end end -- cgit v1.2.3 From 2949918b4cea26435d1f0a076fe884c8113b40fa Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Tue, 8 Jul 2008 11:53:19 +0200 Subject: Make sure object name is translated in #error_messages_for --- actionpack/lib/action_view/helpers/active_record_helper.rb | 1 + actionpack/test/template/active_record_helper_i18n_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 4ff16cd70c..a2fee53fb6 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -176,6 +176,7 @@ module ActionView options[:header_message] else object_name = options[:object_name].to_s.gsub('_', ' ') + object_name = I18n.t(object_name, :default => object_name) || '' locale.t :header_message, :count => count, :object_name => object_name end message = options.include?(:message) ? options[:message] : locale.t(:message) diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index d78b0e4c0c..d35e79b94a 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -7,6 +7,7 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase uses_mocha 'active_record_helper_i18n_test' do def setup @object = stub :errors => stub(:count => 1, :full_messages => ['full_messages']) + @object_name = 'book' stubs(:content_tag).returns 'content_tag' I18n.stubs(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns "1 error prohibited this from being saved" @@ -20,17 +21,26 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase def test_error_messages_for_given_no_header_message_option_it_translates_header_message I18n.expects(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns 'header message' + I18n.expects(:t).with('', :default => '').once error_messages_for(:object => @object, :locale => 'en-US') end def test_error_messages_for_given_a_message_option_it_does_not_translate_message I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).never + I18n.expects(:t).with('', :default => '').once error_messages_for(:object => @object, :message => 'message', :locale => 'en-US') end def test_error_messages_for_given_no_message_option_it_translates_message I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' + I18n.expects(:t).with('', :default => '').once error_messages_for(:object => @object, :locale => 'en-US') end + + def test_error_messages_for_given_object_name_it_translates_object_name + I18n.expects(:t).with(:header_message, :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => @object_name).returns "1 error prohibited this #{@object_name} from being saved" + I18n.expects(:t).with(@object_name, :default => @object_name).once.returns @object_name + error_messages_for(:object => @object, :locale => 'en-US', :object_name => @object_name) + end end end \ No newline at end of file -- cgit v1.2.3 From cf5d6ab9a849d19ac683180cc7b603ca94b13ed7 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Tue, 8 Jul 2008 12:37:49 +0200 Subject: Added localize helper method --- actionpack/lib/action_view/helpers/translation_helper.rb | 4 ++++ actionpack/test/template/translation_helper_test.rb | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index c13c2dfc04..e1010ccf5f 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -11,6 +11,10 @@ module ActionView keys = I18n.send :normalize_translation_keys, e.locale, e.key, e.options[:scope] content_tag('span', keys.join(', '), :class => 'translation_missing') end + + def localize(*args) + I18n.l *args + end end end end \ No newline at end of file diff --git a/actionpack/test/template/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb index e97bcdb731..2263d48a65 100644 --- a/actionpack/test/template/translation_helper_test.rb +++ b/actionpack/test/template/translation_helper_test.rb @@ -39,4 +39,10 @@ class TranslationHelperTest < Test::Unit::TestCase # error_messages_for(:object => @object, :locale => 'en-US') # end end + + def test_delegates_localize_to_i18n + @time = Time.utc(2008, 7, 8, 12, 18, 38) + assert_equal "Tue, 08 Jul 2008 12:18:38 +0100", localize(@time) + assert_equal "08 Jul 12:18", localize(@time, :format => :short) + end end \ No newline at end of file -- cgit v1.2.3 From dc77359c16abc0f693e3847e677c0cad62d0df50 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Tue, 8 Jul 2008 17:41:18 +0200 Subject: Removed unnecessary or condition in #error_messages_for --- actionpack/lib/action_view/helpers/active_record_helper.rb | 2 +- actionpack/test/template/active_record_helper_i18n_test.rb | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index a2fee53fb6..aa978a33bd 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -176,7 +176,7 @@ module ActionView options[:header_message] else object_name = options[:object_name].to_s.gsub('_', ' ') - object_name = I18n.t(object_name, :default => object_name) || '' + object_name = I18n.t(object_name, :default => object_name) locale.t :header_message, :count => count, :object_name => object_name end message = options.include?(:message) ? options[:message] : locale.t(:message) diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index d35e79b94a..feec64aa30 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -21,19 +21,19 @@ class ActiveRecordHelperI18nTest < Test::Unit::TestCase def test_error_messages_for_given_no_header_message_option_it_translates_header_message I18n.expects(:t).with(:'header_message', :locale => 'en-US', :scope => [:active_record, :error], :count => 1, :object_name => '').returns 'header message' - I18n.expects(:t).with('', :default => '').once + I18n.expects(:t).with('', :default => '').once.returns '' error_messages_for(:object => @object, :locale => 'en-US') end def test_error_messages_for_given_a_message_option_it_does_not_translate_message I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).never - I18n.expects(:t).with('', :default => '').once + I18n.expects(:t).with('', :default => '').once.returns '' error_messages_for(:object => @object, :message => 'message', :locale => 'en-US') end def test_error_messages_for_given_no_message_option_it_translates_message I18n.expects(:t).with(:'message', :locale => 'en-US', :scope => [:active_record, :error]).returns 'There were problems with the following fields:' - I18n.expects(:t).with('', :default => '').once + I18n.expects(:t).with('', :default => '').once.returns '' error_messages_for(:object => @object, :locale => 'en-US') end -- cgit v1.2.3 From e0fef66149092dd3d2988fff64f0ce8765735687 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 13 Jul 2008 13:26:48 -0500 Subject: Made ActionView::Base#first_render a little more private. And added _last_render to track the most recent render. Will fix #609 as a side effect. [#609 state:resolved] --- .../action_controller/assertions/response_assertions.rb | 4 ++-- actionpack/lib/action_controller/test_process.rb | 15 ++++----------- actionpack/lib/action_view/base.rb | 6 +++--- actionpack/lib/action_view/helpers/cache_helper.rb | 3 +-- actionpack/lib/action_view/renderable.rb | 4 ++-- actionpack/test/controller/action_pack_assertions_test.rb | 6 +++--- actionpack/test/controller/caching_test.rb | 8 ++++++++ .../functional_caching/inline_fragment_cached.html.erb | 2 ++ 8 files changed, 25 insertions(+), 23 deletions(-) create mode 100644 actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index cb36405700..a98c70d66f 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -87,11 +87,11 @@ module ActionController # def assert_template(expected = nil, message=nil) clean_backtrace do - rendered = expected ? @response.rendered_file(!expected.include?('/')) : @response.rendered_file + rendered = @response.rendered_template.to_s msg = build_message(message, "expecting but rendering with ", expected, rendered) assert_block(msg) do if expected.nil? - !@response.rendered_with_file? + @response.rendered_template ? true : false else rendered.match(expected) end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index 52884a93f4..a6e0c98936 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -205,17 +205,10 @@ module ActionController #:nodoc: p.match(redirect_url) != nil end - # Returns the template path of the file which was used to - # render this response (or nil) - def rendered_file(with_controller = false) - if template.first_render - template.first_render.to_s - end - end - - # Was this template rendered by a file? - def rendered_with_file? - !rendered_file.nil? + # Returns the template of the file which was used to + # render this response (or nil) + def rendered_template + template._first_render end # A shortcut to the flash. Returns an empyt hash if no session flash exists. diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 989e92a890..9f244d7250 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -159,11 +159,11 @@ module ActionView #:nodoc: class Base include ERB::Util - attr_accessor :base_path, :assigns, :template_extension, :first_render + attr_accessor :base_path, :assigns, :template_extension attr_accessor :controller + attr_accessor :_first_render, :_last_render attr_writer :template_format - attr_accessor :current_render_extension attr_accessor :output_buffer @@ -313,7 +313,7 @@ module ActionView #:nodoc: template elsif template = self.view_paths[template_file_name] template - elsif first_render && template = self.view_paths["#{template_file_name}.#{first_render.extension}"] + elsif _first_render && template = self.view_paths["#{template_file_name}.#{_first_render.extension}"] template elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"] @template_format = :html diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index 930c397785..2cdbae6e40 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -32,8 +32,7 @@ module ActionView # Topics listed alphabetically # <% end %> def cache(name = {}, options = nil, &block) - handler = Template.handler_class_for_extension(current_render_extension.to_sym) - handler.new(@controller).cache_fragment(block, name, options) + _last_render.handler.new(@controller).cache_fragment(block, name, options) end end end diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index 2c4302146f..ebb0f1b674 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -18,9 +18,9 @@ module ActionView end def render(view, local_assigns = {}) - view.first_render ||= self + view._first_render ||= self + view._last_render = self view.send(:evaluate_assigns) - view.current_render_extension = extension compile(local_assigns) if handler.compilable? handler.new(view).render(self, local_assigns) end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 610e196362..56ba36cee5 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -328,11 +328,11 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase # check if we were rendered by a file-based template? def test_rendered_action process :nothing - assert !@response.rendered_with_file? + assert_nil @response.rendered_template process :hello_world - assert @response.rendered_with_file? - assert 'hello_world', @response.rendered_file + assert @response.rendered_template + assert 'hello_world', @response.rendered_template.to_s end # check the redirection location diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 2e98837a35..c30f7be700 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -664,6 +664,14 @@ CACHED assert_match "Fragment caching in a partial", @store.read('views/test.host/functional_caching/html_fragment_cached_with_partial') end + def test_render_inline_before_fragment_caching + get :inline_fragment_cached + assert_response :success + assert_match /Some inline content/, @response.body + assert_match /Some cached content/, @response.body + assert_match "Some cached content", @store.read('views/test.host/functional_caching/inline_fragment_cached') + end + def test_fragment_caching_in_rjs_partials xhr :get, :js_fragment_cached_with_partial assert_response :success diff --git a/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb b/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb new file mode 100644 index 0000000000..87309b8ccb --- /dev/null +++ b/actionpack/test/fixtures/functional_caching/inline_fragment_cached.html.erb @@ -0,0 +1,2 @@ +<%= render :inline => 'Some inline content' %> +<% cache do %>Some cached content<% end %> -- cgit v1.2.3 From 26bc867151a8f302b4c6122e6375c3ea2088a037 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 13 Jul 2008 14:00:40 -0500 Subject: Small tweak to e0fef66 --- actionpack/lib/action_controller/assertions/response_assertions.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index a98c70d66f..765225ae24 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -87,13 +87,13 @@ module ActionController # def assert_template(expected = nil, message=nil) clean_backtrace do - rendered = @response.rendered_template.to_s + rendered = @response.rendered_template msg = build_message(message, "expecting but rendering with ", expected, rendered) assert_block(msg) do if expected.nil? - @response.rendered_template ? true : false + @response.rendered_template.nil? else - rendered.match(expected) + rendered.to_s.match(expected) end end end -- cgit v1.2.3 From 68fe898189a27e4e3c4c2fe005c99975d40e1dd7 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 13 Jul 2008 14:05:21 -0500 Subject: Check first render format and extension. Fixes failing ActionMailer test. --- actionpack/lib/action_view/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 9f244d7250..04e8d3a358 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -313,7 +313,7 @@ module ActionView #:nodoc: template elsif template = self.view_paths[template_file_name] template - elsif _first_render && template = self.view_paths["#{template_file_name}.#{_first_render.extension}"] + elsif _first_render && template = self.view_paths["#{template_file_name}.#{_first_render.format_and_extension}"] template elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"] @template_format = :html -- cgit v1.2.3 From 0d241f4434bafa2107cd6c3f3ab77c05f5d5ec71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tapaj=C3=B3s?= Date: Sun, 13 Jul 2008 14:19:03 -0500 Subject: Use full path in database tasks so commands will work outside of Rails root [#612 state:resolved] Signed-off-by: Joshua Peek --- railties/lib/tasks/databases.rake | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 75fba8b45a..22b8459ce4 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -215,14 +215,14 @@ namespace :db do desc "Create a db/schema.rb file that can be portably used against any DB supported by AR" task :dump => :environment do require 'active_record/schema_dumper' - File.open(ENV['SCHEMA'] || "db/schema.rb", "w") do |file| + File.open(ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb", "w") do |file| ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file) end end desc "Load a schema.rb file into the database" task :load => :environment do - file = ENV['SCHEMA'] || "db/schema.rb" + file = ENV['SCHEMA'] || "#{RAILS_ROOT}/db/schema.rb" load(file) end end @@ -234,7 +234,7 @@ namespace :db do case abcs[RAILS_ENV]["adapter"] when "mysql", "oci", "oracle" ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) - File.open("db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } when "postgresql" ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"] ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"] @@ -252,13 +252,13 @@ namespace :db do when "firebird" set_firebird_env(abcs[RAILS_ENV]) db_string = firebird_db_string(abcs[RAILS_ENV]) - sh "isql -a #{db_string} > db/#{RAILS_ENV}_structure.sql" + sh "isql -a #{db_string} > #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql" else raise "Task not supported by '#{abcs["test"]["adapter"]}'" end if ActiveRecord::Base.connection.supports_migrations? - File.open("db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } + File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } end end end @@ -281,28 +281,28 @@ namespace :db do when "mysql" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') - IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| + IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end when "postgresql" ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] - `psql -U "#{abcs["test"]["username"]}" -f db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}` + `psql -U "#{abcs["test"]["username"]}" -f #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}` when "sqlite", "sqlite3" dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] - `#{abcs["test"]["adapter"]} #{dbfile} < db/#{RAILS_ENV}_structure.sql` + `#{abcs["test"]["adapter"]} #{dbfile} < #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql` when "sqlserver" `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` when "oci", "oracle" ActiveRecord::Base.establish_connection(:test) - IO.readlines("db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| + IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end when "firebird" set_firebird_env(abcs["test"]) db_string = firebird_db_string(abcs["test"]) - sh "isql -i db/#{RAILS_ENV}_structure.sql #{db_string}" + sh "isql -i #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{db_string}" else raise "Task not supported by '#{abcs["test"]["adapter"]}'" end -- cgit v1.2.3 From 95812d5eafc3b63ce5eeb0748a5d0132f5108b64 Mon Sep 17 00:00:00 2001 From: rsl Date: Mon, 14 Jul 2008 00:55:57 +0100 Subject: Ensure :index works with fields_for select methods. [#518 state:resolved] Signed-off-by: Pratik Naik --- .../lib/action_view/helpers/form_options_helper.rb | 8 +- .../test/template/form_options_helper_test.rb | 929 ++++++++++++++++++++- 2 files changed, 911 insertions(+), 26 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index ab9e174621..87d49397c6 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -445,19 +445,19 @@ module ActionView class FormBuilder def select(method, choices, options = {}, html_options = {}) - @template.select(@object_name, method, choices, options.merge(:object => @object), html_options) + @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options)) end def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}) - @template.collection_select(@object_name, method, collection, value_method, text_method, options.merge(:object => @object), html_options) + @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)) end def country_select(method, priority_countries = nil, options = {}, html_options = {}) - @template.country_select(@object_name, method, priority_countries, options.merge(:object => @object), html_options) + @template.country_select(@object_name, method, priority_countries, objectify_options(options), @default_options.merge(html_options)) end def time_zone_select(method, priority_zones = nil, options = {}, html_options = {}) - @template.time_zone_select(@object_name, method, priority_zones, options.merge(:object => @object), html_options) + @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options)) end end end diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 2496931f4b..9dd43d7b4f 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -231,6 +231,35 @@ uses_mocha "FormOptionsHelperTest" do ) end + def test_select_under_fields_for_with_index + @post = Post.new + @post.category = "" + + fields_for :post, @post, :index => 108 do |f| + concat f.select(:category, %w( abe hest)) + end + + assert_dom_equal( + "", + output_buffer + ) + end + + def test_select_under_fields_for_with_auto_index + @post = Post.new + @post.category = "" + def @post.to_param; 108; end + + fields_for "post[]", @post do |f| + concat f.select(:category, %w( abe hest)) + end + + assert_dom_equal( + "", + output_buffer + ) + end + def test_select_with_blank @post = Post.new @post.category = "" @@ -351,6 +380,47 @@ uses_mocha "FormOptionsHelperTest" do ) end + def test_collection_select_under_fields_for_with_index + @posts = [ + Post.new(" went home", "", "To a little house", "shh!"), + Post.new("Babe went home", "Babe", "To a little house", "shh!"), + Post.new("Cabe went home", "Cabe", "To a little house", "shh!") + ] + + @post = Post.new + @post.author_name = "Babe" + + fields_for :post, @post, :index => 815 do |f| + concat f.collection_select(:author_name, @posts, :author_name, :author_name) + end + + assert_dom_equal( + "", + output_buffer + ) + end + + def test_collection_select_under_fields_for_with_auto_index + @posts = [ + Post.new(" went home", "", "To a little house", "shh!"), + Post.new("Babe went home", "Babe", "To a little house", "shh!"), + Post.new("Cabe went home", "Cabe", "To a little house", "shh!") + ] + + @post = Post.new + @post.author_name = "Babe" + def @post.to_param; 815; end + + fields_for "post[]", @post do |f| + concat f.collection_select(:author_name, @posts, :author_name, :author_name) + end + + assert_dom_equal( + "", + output_buffer + ) + end + def test_collection_select_with_blank_and_style @posts = [ Post.new(" went home", "", "To a little house", "shh!"), @@ -1165,28 +1235,843 @@ uses_mocha "FormOptionsHelperTest" do assert_dom_equal(expected_select[0..-2], country_select("post", "origin", ["New Zealand", "Nicaragua"])) end - def test_time_zone_select - @firm = Firm.new("D") - html = time_zone_select( "firm", "time_zone" ) - assert_dom_equal "", - html - end - - def test_time_zone_select_under_fields_for - @firm = Firm.new("D") - - fields_for :firm, @firm do |f| - concat f.time_zone_select(:time_zone) - end - - assert_dom_equal( - " + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + COUNTRIES + + fields_for :post, @post do |f| + concat f.country_select("origin") + end + + assert_dom_equal(expected_select[0..-2], output_buffer) + end + + def test_country_select_under_fields_for_with_index + @post = Post.new + @post.origin = "United States" + expected_select = <<-COUNTRIES + + COUNTRIES + + fields_for :post, @post, :index => 325 do |f| + concat f.country_select("origin") + end + + assert_dom_equal(expected_select[0..-2], output_buffer) + end + + def test_country_select_under_fields_for_with_auto_index + @post = Post.new + @post.origin = "Iraq" + def @post.to_param; 325; end + + expected_select = <<-COUNTRIES + + COUNTRIES + + fields_for "post[]", @post do |f| + concat f.country_select("origin") + end + + assert_dom_equal(expected_select[0..-2], output_buffer) + end + + def test_time_zone_select + @firm = Firm.new("D") + html = time_zone_select( "firm", "time_zone" ) + assert_dom_equal "", + html + end + + def test_time_zone_select_under_fields_for + @firm = Firm.new("D") + + fields_for :firm, @firm do |f| + concat f.time_zone_select(:time_zone) + end + + assert_dom_equal( + "", + output_buffer + ) + end + + def test_time_zone_select_under_fields_for_with_index + @firm = Firm.new("D") + + fields_for :firm, @firm, :index => 305 do |f| + concat f.time_zone_select(:time_zone) + end + + assert_dom_equal( + "", + output_buffer + ) + end + + def test_time_zone_select_under_fields_for_with_auto_index + @firm = Firm.new("D") + def @firm.to_param; 305; end + + fields_for "firm[]", @firm do |f| + concat f.time_zone_select(:time_zone) + end + + assert_dom_equal( + " # # # <% form_remote_tag :url => '/posts' do -%> @@ -323,19 +323,19 @@ module ActionView options[:form] = true options[:html] ||= {} - options[:html][:onsubmit] = - (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") + + options[:html][:onsubmit] = + (options[:html][:onsubmit] ? options[:html][:onsubmit] + "; " : "") + "#{remote_function(options)}; return false;" form_tag(options[:html].delete(:action) || url_for(options[:url]), options[:html], &block) end - # Creates a form that will submit using XMLHttpRequest in the background - # instead of the regular reloading POST arrangement and a scope around a + # Creates a form that will submit using XMLHttpRequest in the background + # instead of the regular reloading POST arrangement and a scope around a # specific resource that is used as a base for questioning about - # values for the fields. + # values for the fields. # - # === Resource + # === Resource # # Example: # <% remote_form_for(@post) do |f| %> @@ -348,7 +348,7 @@ module ActionView # ... # <% end %> # - # === Nested Resource + # === Nested Resource # # Example: # <% remote_form_for([@post, @comment]) do |f| %> @@ -387,23 +387,23 @@ module ActionView concat('') end alias_method :form_remote_for, :remote_form_for - + # Returns a button input tag with the element name of +name+ and a value (i.e., display text) of +value+ # that will submit form using XMLHttpRequest in the background instead of a regular POST request that - # reloads the page. + # reloads the page. # # # Create a button that submits to the create action - # # - # # Generates: # <%= button_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %> # # # Submit to the remote action update and update the DIV succeed or fail based # # on the success or failure of the request # # - # # Generates: # <%= button_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, # :update => { :success => "succeed", :failure => "fail" } @@ -423,7 +423,7 @@ module ActionView tag("input", options[:html], false) end alias_method :submit_to_remote, :button_to_remote - + # Returns 'eval(request.responseText)' which is the JavaScript function # that +form_remote_tag+ can call in :complete to evaluate a multiple # update return document using +update_element_function+ calls. @@ -433,11 +433,11 @@ module ActionView # Returns the JavaScript needed for a remote function. # Takes the same arguments as link_to_remote. - # + # # Example: - # # Generates: { :action => :update_options }) %>"> # # @@ -455,7 +455,7 @@ module ActionView update << "'#{options[:update]}'" end - function = update.empty? ? + function = update.empty? ? "new Ajax.Request(" : "new Ajax.Updater(#{update}, " @@ -476,9 +476,9 @@ module ActionView # callback when its contents have changed. The default callback is an # Ajax call. By default the value of the observed field is sent as a # parameter with the Ajax call. - # + # # Example: - # # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest', + # # Generates: new Form.Element.Observer('suggest', 0.25, function(element, value) {new Ajax.Updater('suggest', # # '/testing/find_suggestion', {asynchronous:true, evalScripts:true, parameters:'q=' + value})}) # <%= observe_field :suggest, :url => { :action => :find_suggestion }, # :frequency => 0.25, @@ -500,14 +500,14 @@ module ActionView # new Form.Element.Observer('glass', 1, function(element, value) {alert('Element changed')}) # The element parameter is the DOM element being observed, and the value is its value at the # time the observer is triggered. - # + # # Additional options are: # :frequency:: The frequency (in seconds) at which changes to # this field will be detected. Not setting this # option at all or to a value equal to or less than # zero will use event based observation instead of # time based observation. - # :update:: Specifies the DOM ID of the element whose + # :update:: Specifies the DOM ID of the element whose # innerHTML should be updated with the # XMLHttpRequest response text. # :with:: A JavaScript expression specifying the parameters @@ -518,7 +518,7 @@ module ActionView # variable +value+. # # Examples - # + # # :with => "'my_custom_key=' + value" # :with => "'person[name]=' + prompt('New name')" # :with => "Form.Element.serialize('other-field')" @@ -544,7 +544,7 @@ module ActionView # observe_field 'book_title', # :url => 'http://example.com/books/edit/1', # :with => 'title' - # + # # # Sends params: {:book_title => 'Title of the book'} when the focus leaves # # the input field. # observe_field 'book_title', @@ -558,7 +558,7 @@ module ActionView build_observer('Form.Element.EventObserver', field_id, options) end end - + # Observes the form with the DOM ID specified by +form_id+ and calls a # callback when its contents have changed. The default callback is an # Ajax call. By default all fields of the observed field are sent as @@ -574,16 +574,17 @@ module ActionView build_observer('Form.EventObserver', form_id, options) end end - - # All the methods were moved to GeneratorMethods so that + + # All the methods were moved to GeneratorMethods so that # #include_helpers_from_context has nothing to overwrite. class JavaScriptGenerator #:nodoc: def initialize(context, &block) #:nodoc: @context, @lines = context, [] + @context.output_buffer = @lines if @context include_helpers_from_context @context.instance_exec(self, &block) end - + private def include_helpers_from_context @context.extended_by.each do |mod| @@ -591,17 +592,17 @@ module ActionView end extend GeneratorMethods end - - # JavaScriptGenerator generates blocks of JavaScript code that allow you - # to change the content and presentation of multiple DOM elements. Use + + # JavaScriptGenerator generates blocks of JavaScript code that allow you + # to change the content and presentation of multiple DOM elements. Use # this in your Ajax response bodies, either in a \n\n\n\n), javascript_include_tag(:defaults)) -- cgit v1.2.3 From 1e0f94a77c717dd06a86edda97de5a4c4ad919a8 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 17 Jul 2008 14:45:14 -0700 Subject: Introduce simple internationalization support --- actionpack/test/i18n_coverage | 9 --------- activerecord/test/i18n_coverage | 6 ------ railties/CHANGELOG | 2 ++ 3 files changed, 2 insertions(+), 15 deletions(-) delete mode 100755 actionpack/test/i18n_coverage delete mode 100755 activerecord/test/i18n_coverage diff --git a/actionpack/test/i18n_coverage b/actionpack/test/i18n_coverage deleted file mode 100755 index 57b54e9d47..0000000000 --- a/actionpack/test/i18n_coverage +++ /dev/null @@ -1,9 +0,0 @@ -rcov -x abstract_unit.rb \ --i action_view/helpers/number_helper.rb,action_view/helpers/date_helper.rb,action_view/helpers/active_record_helper.rb \ -template/number_helper_i18n_test.rb \ -template/date_helper_i18n_test.rb \ -template/active_record_helper_i18n_test.rb \ - -# template/number_helper_test.rb \ -# template/date_helper_test.rb \ -# template/active_record_helper_test.rb \ No newline at end of file diff --git a/activerecord/test/i18n_coverage b/activerecord/test/i18n_coverage deleted file mode 100755 index 1589a6c06f..0000000000 --- a/activerecord/test/i18n_coverage +++ /dev/null @@ -1,6 +0,0 @@ -rcov -I connections/native_mysql \ --x cases/helper,config,connection,models \ --i active_record/validations.rb \ -cases/validations_i18n_test.rb \ - -# cases/validations_test.rb \ No newline at end of file diff --git a/railties/CHANGELOG b/railties/CHANGELOG index b5c5aba460..9c5e5b59c6 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Introduce simple internationalization support. [Ruby i18n team] + * Make script/plugin install -r option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example: script/plugin install git://github.com/mislav/will_paginate.git -r agnostic # Installs 'agnostic' branch -- cgit v1.2.3 From a1fcbd971d681e44de5ea33e6a8470ff8b8144c0 Mon Sep 17 00:00:00 2001 From: Joachim Garth Date: Fri, 27 Jun 2008 20:03:51 +0200 Subject: Make sure association preloading works with full STI class name [#465 state:Resolved] Signed-off-by: Pratik Naik --- .../lib/active_record/association_preload.rb | 2 +- .../eager_load_includes_full_sti_class_test.rb | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 64888f9110..c7594809b7 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -252,7 +252,7 @@ module ActiveRecord table_name = reflection.klass.quoted_table_name if interface = reflection.options[:as] - conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.name.demodulize}'" + conditions = "#{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_id"} IN (?) and #{reflection.klass.quoted_table_name}.#{connection.quote_column_name "#{interface}_type"} = '#{self.base_class.sti_name}'" else foreign_key = reflection.primary_key_name conditions = "#{reflection.klass.quoted_table_name}.#{foreign_key} IN (?)" diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb new file mode 100644 index 0000000000..7c470616a5 --- /dev/null +++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb @@ -0,0 +1,36 @@ +require 'cases/helper' +require 'models/post' +require 'models/tagging' + +module Namespaced + class Post < ActiveRecord::Base + set_table_name 'posts' + has_one :tagging, :as => :taggable, :class_name => 'Tagging' + end +end + +class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase + + def setup + generate_test_objects + end + + def generate_test_objects + post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 ) + tagging = Tagging.create( :taggable => post ) + end + + def test_class_names + old = ActiveRecord::Base.store_full_sti_class + + ActiveRecord::Base.store_full_sti_class = false + post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging ) + assert_nil post.tagging + + ActiveRecord::Base.store_full_sti_class = true + post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging ) + assert_equal 'Tagging', post.tagging.class.name + ensure + ActiveRecord::Base.store_full_sti_class = old + end +end -- cgit v1.2.3 From 57a2780f14447152ece1b1301fc6c25b2ec43da5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 16 Jul 2008 04:32:15 -0700 Subject: etag! and last_modified! conditional GET helpers --- actionpack/CHANGELOG | 4 + actionpack/lib/action_controller/base.rb | 15 ++- actionpack/lib/action_controller/response.rb | 40 +++++-- actionpack/test/controller/render_test.rb | 152 +++++++++++++++++---------- 4 files changed, 150 insertions(+), 61 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index da9fdbfd9d..f6432fe0be 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,9 @@ *Edge* +* Conditional GET utility methods. [Jeremy Kemper] + * etag!([:admin, post, current_user]) sets the ETag response header and returns head(:not_modified) if it matches the If-None-Match request header. + * last_modified!(post.updated_at) sets Last-Modified and returns head(:not_modified) if it's no later than If-Modified-Since. + * All 2xx requests are considered successful [Josh Peek] * Fixed that AssetTagHelper#compute_public_path shouldn't cache the asset_host along with the source or per-request proc's won't run [DHH] diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index c56812c2d9..50727c67c4 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -519,6 +519,8 @@ module ActionController #:nodoc: public # Extracts the action_name from the request parameters and performs that action. def process(request, response, method = :perform_action, *arguments) #:nodoc: + response.request = request + initialize_template_class(response) assign_shortcuts(request, response) initialize_current_url @@ -529,8 +531,6 @@ module ActionController #:nodoc: send(method, *arguments) assign_default_content_type_and_charset - - response.request = request response.prepare! unless component_request? response ensure @@ -968,6 +968,17 @@ module ActionController #:nodoc: render :nothing => true, :status => status end + # Sets the Last-Modified response header. Returns 304 Not Modified if the + # If-Modified-Since request header is <= last modified. + def last_modified!(utc_time) + head(:not_modified) if response.last_modified!(utc_time) + end + + # Sets the ETag response header. Returns 304 Not Modified if the + # If-None-Match request header matches. + def etag!(etag) + head(:not_modified) if response.etag!(etag) + end # Clears the rendered results, allowing for another render to be performed. def erase_render_results #:nodoc: diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb index 1d9f6676ba..9955532844 100755 --- a/actionpack/lib/action_controller/response.rb +++ b/actionpack/lib/action_controller/response.rb @@ -41,20 +41,48 @@ module ActionController set_content_length! end + # Sets the Last-Modified response header. Returns whether it's older than + # the If-Modified-Since request header. + def last_modified!(utc_time) + headers['Last-Modified'] ||= utc_time.httpdate + if request && since = request.headers['HTTP_IF_MODIFIED_SINCE'] + utc_time <= Time.rfc2822(since) + end + end + + # Sets the ETag response header. Returns whether it matches the + # If-None-Match request header. + def etag!(tag) + headers['ETag'] ||= %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(tag))}") + if request && request.headers['HTTP_IF_NONE_MATCH'] == headers['ETag'] + true + end + end private def handle_conditional_get! - if body.is_a?(String) && (headers['Status'] ? headers['Status'][0..2] == '200' : true) && !body.empty? - self.headers['ETag'] ||= %("#{Digest::MD5.hexdigest(body)}") - self.headers['Cache-Control'] = 'private, max-age=0, must-revalidate' if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control'] + if nonempty_ok_response? + set_conditional_cache_control! - if request.headers['HTTP_IF_NONE_MATCH'] == headers['ETag'] - self.headers['Status'] = '304 Not Modified' + if etag!(body) + headers['Status'] = '304 Not Modified' self.body = '' end end end + def nonempty_ok_response? + status = headers['Status'] + ok = !status || status[0..2] == '200' + ok && body.is_a?(String) && !body.empty? + end + + def set_conditional_cache_control! + if headers['Cache-Control'] == DEFAULT_HEADERS['Cache-Control'] + headers['Cache-Control'] = 'private, max-age=0, must-revalidate' + end + end + def convert_content_type! if content_type = headers.delete("Content-Type") self.headers["type"] = content_type @@ -73,4 +101,4 @@ module ActionController self.headers["Content-Length"] = body.size unless body.respond_to?(:call) end end -end \ No newline at end of file +end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 9a94db4b00..041c54c7fd 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -8,14 +8,18 @@ module Fun end end - -# FIXME: crashes Ruby 1.9 class TestController < ActionController::Base layout :determine_layout def hello_world end + def conditional_hello + etag! [:foo, 123] + last_modified! Time.now.utc.beginning_of_day + render :action => 'hello_world' unless performed? + end + def render_hello_world render :template => "test/hello_world" end @@ -408,6 +412,72 @@ class RenderTest < Test::Unit::TestCase assert_equal "Goodbye, Local David", @response.body end + def test_should_render_formatted_template + get :formatted_html_erb + assert_equal 'formatted html erb', @response.body + end + + def test_should_render_formatted_xml_erb_template + get :formatted_xml_erb, :format => :xml + assert_equal 'passed formatted xml erb', @response.body + end + + def test_should_render_formatted_html_erb_template + get :formatted_xml_erb + assert_equal 'passed formatted html erb', @response.body + end + + def test_should_render_formatted_html_erb_template_with_faulty_accepts_header + @request.env["HTTP_ACCEPT"] = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*" + get :formatted_xml_erb + assert_equal 'passed formatted html erb', @response.body + end + + def test_should_render_html_formatted_partial + get :partial + assert_equal 'partial html', @response.body + end + + def test_should_render_html_partial_with_dot + get :partial_dot_html + assert_equal 'partial html', @response.body + end + + def test_should_render_html_formatted_partial_with_rjs + xhr :get, :partial_as_rjs + assert_equal %(Element.replace("foo", "partial html");), @response.body + end + + def test_should_render_html_formatted_partial_with_rjs_and_js_format + xhr :get, :respond_to_partial_as_rjs + assert_equal %(Element.replace("foo", "partial html");), @response.body + end + + def test_should_render_js_partial + xhr :get, :partial, :format => 'js' + assert_equal 'partial js', @response.body + end + + def test_should_render_with_alternate_default_render + xhr :get, :render_alternate_default + assert_equal %(Element.replace("foo", "partial html");), @response.body + end + + def test_should_render_xml_but_keep_custom_content_type + get :render_xml_with_custom_content_type + assert_equal "application/atomsvc+xml", @response.content_type + end +end + +class EtagRenderTest < Test::Unit::TestCase + def setup + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @controller = TestController.new + + @request.host = "www.nextangle.com" + end + def test_render_200_should_set_etag get :render_hello_world_from_variable assert_equal etag_for("hello david"), @response.headers['ETag'] @@ -460,64 +530,40 @@ class RenderTest < Test::Unit::TestCase assert_equal etag_for("\n\n

Hello

\n

This is grand!

\n\n
\n"), @response.headers['ETag'] end - def test_should_render_formatted_template - get :formatted_html_erb - assert_equal 'formatted html erb', @response.body - end - - def test_should_render_formatted_xml_erb_template - get :formatted_xml_erb, :format => :xml - assert_equal 'passed formatted xml erb', @response.body - end - - def test_should_render_formatted_html_erb_template - get :formatted_xml_erb - assert_equal 'passed formatted html erb', @response.body - end - - def test_should_render_formatted_html_erb_template_with_faulty_accepts_header - @request.env["HTTP_ACCEPT"] = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, appliction/x-shockwave-flash, */*" - get :formatted_xml_erb - assert_equal 'passed formatted html erb', @response.body - end - - def test_should_render_html_formatted_partial - get :partial - assert_equal 'partial html', @response.body - end - - def test_should_render_html_partial_with_dot - get :partial_dot_html - assert_equal 'partial html', @response.body - end + protected + def etag_for(text) + %("#{Digest::MD5.hexdigest(text)}") + end +end - def test_should_render_html_formatted_partial_with_rjs - xhr :get, :partial_as_rjs - assert_equal %(Element.replace("foo", "partial html");), @response.body - end +class LastModifiedRenderTest < Test::Unit::TestCase + def setup + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + @controller = TestController.new - def test_should_render_html_formatted_partial_with_rjs_and_js_format - xhr :get, :respond_to_partial_as_rjs - assert_equal %(Element.replace("foo", "partial html");), @response.body + @request.host = "www.nextangle.com" + @last_modified = Time.now.utc.beginning_of_day.httpdate end - def test_should_render_js_partial - xhr :get, :partial, :format => 'js' - assert_equal 'partial js', @response.body + def test_responds_with_last_modified + get :conditional_hello + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_should_render_with_alternate_default_render - xhr :get, :render_alternate_default - assert_equal %(Element.replace("foo", "partial html");), @response.body + def test_request_not_modified + @request.headers["HTTP_IF_MODIFIED_SINCE"] = @last_modified + get :conditional_hello + assert_equal "304 Not Modified", @response.headers['Status'] + assert @response.body.blank?, @response.body + assert_equal @last_modified, @response.headers['Last-Modified'] end - def test_should_render_xml_but_keep_custom_content_type - get :render_xml_with_custom_content_type - assert_equal "application/atomsvc+xml", @response.content_type + def test_request_modified + @request.headers["HTTP_IF_MODIFIED_SINCE"] = 'Thu, 16 Jul 2008 00:00:00 GMT' + get :conditional_hello + assert_equal "200 OK", @response.headers['Status'] + assert !@response.body.blank? + assert_equal @last_modified, @response.headers['Last-Modified'] end - - protected - def etag_for(text) - %("#{Digest::MD5.hexdigest(text)}") - end end -- cgit v1.2.3 From 7430c4168fad07b480dbf80c8ac75ba7db8c634f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 16 Jul 2008 22:27:04 -0700 Subject: Decrease default benchmark runs from 10 to 4 --- activesupport/lib/active_support/testing/performance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 5f2027eb3b..71d6f4d9c6 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -11,7 +11,7 @@ module ActiveSupport DEFAULTS = if benchmark = ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, - :runs => 10, + :runs => 4, :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time], :output => 'tmp/performance' } else -- cgit v1.2.3 From e1f23da53cef20a60e4bf458d959fe2bfe7d52ea Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 11:18:16 -0500 Subject: Allow memoized methods to be reloaded and allow multiple symbols --- activesupport/lib/active_support/memoizable.rb | 32 +++++++++++---------- activesupport/test/memoizable_test.rb | 39 +++++++++++++++++--------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index d06250171a..c41feef4c7 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -5,28 +5,30 @@ module ActiveSupport end module ClassMethods - def memoize(symbol) - original_method = "_unmemoized_#{symbol}" - memoized_ivar = "@_memoized_#{symbol}" - raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method) + def memoize(*symbols) + symbols.each do |symbol| + original_method = "unmemoized_#{symbol}" + memoized_ivar = "@#{symbol}" + raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method) - alias_method original_method, symbol - class_eval <<-EOS, __FILE__, __LINE__ - def #{symbol} - if defined? #{memoized_ivar} - #{memoized_ivar} - else - #{memoized_ivar} = #{original_method} + alias_method original_method, symbol + class_eval <<-EOS, __FILE__, __LINE__ + def #{symbol}(reload = false) + if !reload && defined? #{memoized_ivar} + #{memoized_ivar} + else + #{memoized_ivar} = #{original_method}.freeze + end end - end - EOS + EOS + end end end def freeze methods.each do |method| - if m = method.to_s.match(/\A_unmemoized_(.*)/) - send(m[1]).freeze + if m = method.to_s.match(/\Aunmemoized_(.*)/) + send(m[1]) end end super diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index fc24a2942d..b649b31455 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -8,12 +8,16 @@ uses_mocha 'Memoizable' do def name fetch_name_from_floppy end - memoize :name def age nil end - memoize :age + + def random + rand(0) + end + + memoize :name, :age, :random private def fetch_name_from_floppy @@ -21,25 +25,34 @@ uses_mocha 'Memoizable' do end end + def setup + @person = Person.new + end + def test_memoization - person = Person.new - assert_equal "Josh", person.name + assert_equal "Josh", @person.name + + @person.expects(:fetch_name_from_floppy).never + 2.times { assert_equal "Josh", @person.name } + end - person.expects(:fetch_name_from_floppy).never - 2.times { assert_equal "Josh", person.name } + def test_reloadable + random = @person.random + assert_equal random, @person.random + assert_not_equal random, @person.random(:reload) end def test_memoized_methods_are_frozen - person = Person.new - person.freeze - assert_equal "Josh", person.name - assert_equal true, person.name.frozen? + assert_equal true, @person.name.frozen? + + @person.freeze + assert_equal "Josh", @person.name + assert_equal true, @person.name.frozen? end def test_memoization_frozen_with_nil_value - person = Person.new - person.freeze - assert_equal nil, person.age + @person.freeze + assert_equal nil, @person.age end def test_double_memoization -- cgit v1.2.3 From ef6f6625c91ea789a033799f649e4388e4a71045 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 15:32:28 -0500 Subject: Changed ActiveSupport::Memoizable API to extend since it mainly adds the memoize class method --- actionpack/lib/action_view/renderable.rb | 4 +- actionpack/lib/action_view/renderable_partial.rb | 2 +- actionpack/lib/action_view/template.rb | 2 +- activesupport/lib/active_support/memoizable.rb | 55 ++++++++++++------------ activesupport/test/memoizable_test.rb | 42 +++++++++++++++--- 5 files changed, 67 insertions(+), 38 deletions(-) diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index 46193670f3..5090d0160a 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -3,12 +3,12 @@ module ActionView # NOTE: The template that this mixin is beening include into is frozen # So you can not set or modify any instance variables + extend ActiveSupport::Memoizable + def self.included(base) @@mutex = Mutex.new end - include ActiveSupport::Memoizable - def filename 'compiled-template' end diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb index fdb1a5e6a7..342850f0f0 100644 --- a/actionpack/lib/action_view/renderable_partial.rb +++ b/actionpack/lib/action_view/renderable_partial.rb @@ -3,7 +3,7 @@ module ActionView # NOTE: The template that this mixin is beening include into is frozen # So you can not set or modify any instance variables - include ActiveSupport::Memoizable + extend ActiveSupport::Memoizable def variable_name name.sub(/\A_/, '').to_sym diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 304aec3a4c..eba42518d7 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,7 +1,7 @@ module ActionView #:nodoc: class Template extend TemplateHandlers - include ActiveSupport::Memoizable + extend ActiveSupport::Memoizable include Renderable attr_accessor :filename, :load_path, :base_path, :name, :format, :extension diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index c41feef4c7..59fecbecb1 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,37 +1,38 @@ module ActiveSupport - module Memoizable - def self.included(base) #:nodoc: - base.extend(ClassMethods) - end - - module ClassMethods - def memoize(*symbols) - symbols.each do |symbol| - original_method = "unmemoized_#{symbol}" - memoized_ivar = "@#{symbol}" - raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method) - - alias_method original_method, symbol - class_eval <<-EOS, __FILE__, __LINE__ - def #{symbol}(reload = false) - if !reload && defined? #{memoized_ivar} - #{memoized_ivar} - else - #{memoized_ivar} = #{original_method}.freeze - end + module Memoizable #:nodoc: + def self.extended(obj) + klass = obj.respond_to?(:class_eval) ? obj : obj.metaclass + klass.class_eval do + def freeze + methods.each do |method| + if m = method.to_s.match(/^unmemoized_(.*)/) + send(m[1]) end - EOS + end + super end end end - def freeze - methods.each do |method| - if m = method.to_s.match(/\Aunmemoized_(.*)/) - send(m[1]) - end + def memoize(*symbols) + symbols.each do |symbol| + original_method = "unmemoized_#{symbol}" + memoized_ivar = "@#{symbol}" + + klass = respond_to?(:class_eval) ? self : self.metaclass + raise "Already memoized #{symbol}" if klass.instance_methods.map(&:to_s).include?(original_method) + + klass.class_eval <<-EOS, __FILE__, __LINE__ + alias_method :#{original_method}, :#{symbol} + def #{symbol}(reload = false) + if !reload && defined? #{memoized_ivar} + #{memoized_ivar} + else + #{memoized_ivar} = #{original_method}.freeze + end + end + EOS end - super end end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index b649b31455..79769631ad 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -3,21 +3,24 @@ require 'abstract_unit' uses_mocha 'Memoizable' do class MemoizableTest < Test::Unit::TestCase class Person - include ActiveSupport::Memoizable + extend ActiveSupport::Memoizable def name fetch_name_from_floppy end + memoize :name + def age nil end - def random - rand(0) + def counter + @counter ||= 0 + @counter += 1 end - memoize :name, :age, :random + memoize :age, :counter private def fetch_name_from_floppy @@ -37,9 +40,9 @@ uses_mocha 'Memoizable' do end def test_reloadable - random = @person.random - assert_equal random, @person.random - assert_not_equal random, @person.random(:reload) + counter = @person.counter + assert_equal 1, @person.counter + assert_equal 2, @person.counter(:reload) end def test_memoized_methods_are_frozen @@ -58,5 +61,30 @@ uses_mocha 'Memoizable' do def test_double_memoization assert_raise(RuntimeError) { Person.memoize :name } end + + class Company + def name + lookup_name + end + + def lookup_name + "37signals" + end + end + + def test_object_memoization + company = Company.new + company.extend ActiveSupport::Memoizable + company.memoize :name + + assert_equal "37signals", company.name + # Mocha doesn't play well with frozen objects + company.metaclass.instance_eval { define_method(:lookup_name) { b00m } } + assert_equal "37signals", company.name + + assert_equal true, company.name.frozen? + company.freeze + assert_equal true, company.name.frozen? + end end end -- cgit v1.2.3 From d2ccb852d4e1f6f1b01e43f32213053ae3bef408 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 16:00:20 -0500 Subject: Removed lagacy TemplateHandler#render API. Left in a legacy TemplateHandler and Compilable stub so plugins will not have to change anything. --- actionpack/lib/action_view/base.rb | 4 +-- actionpack/lib/action_view/renderable.rb | 6 ++--- actionpack/lib/action_view/template_handler.rb | 23 ++++++----------- actionpack/lib/action_view/template_handlers.rb | 1 - .../action_view/template_handlers/compilable.rb | 20 --------------- actionpack/test/controller/layout_test.rb | 12 ++------- actionpack/test/template/render_test.rb | 30 ++++------------------ 7 files changed, 20 insertions(+), 76 deletions(-) delete mode 100644 actionpack/lib/action_view/template_handlers/compilable.rb diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index a6872b1a47..ae6b284854 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -379,8 +379,8 @@ module ActionView #:nodoc: @assigns.each { |key, value| instance_variable_set("@#{key}", value) } end - def execute(template, local_assigns = {}) - send(template.method(local_assigns), local_assigns) do |*names| + def execute(method, local_assigns = {}) + send(method, local_assigns) do |*names| instance_variable_get "@content_for_#{names.first || 'layout'}" end end diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index 5090d0160a..4f865cbced 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -19,7 +19,7 @@ module ActionView memoize :handler def compiled_source - handler.new(nil).compile(self) if handler.compilable? + handler.call(self) end memoize :compiled_source @@ -27,8 +27,8 @@ module ActionView view._first_render ||= self view._last_render = self view.send(:evaluate_assigns) - compile(local_assigns) if handler.compilable? - handler.new(view).render(self, local_assigns) + compile(local_assigns) + view.send(:execute, method(local_assigns), local_assigns) end def method(local_assigns) diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb index e2dd305f93..d7e7c9b199 100644 --- a/actionpack/lib/action_view/template_handler.rb +++ b/actionpack/lib/action_view/template_handler.rb @@ -1,21 +1,14 @@ -module ActionView - class TemplateHandler - def self.compilable? - false - end - - def initialize(view) - @view = view - end +# Legacy TemplateHandler stub - def render(template, local_assigns = {}) - end - - def compile(template) +module ActionView + module TemplateHandlers + module Compilable end + end - def compilable? - self.class.compilable? + class TemplateHandler + def self.call(template) + new.compile(template) end end end diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template_handlers.rb index 1471e99e01..6c8aa4c2a7 100644 --- a/actionpack/lib/action_view/template_handlers.rb +++ b/actionpack/lib/action_view/template_handlers.rb @@ -1,5 +1,4 @@ require 'action_view/template_handler' -require 'action_view/template_handlers/compilable' require 'action_view/template_handlers/builder' require 'action_view/template_handlers/erb' require 'action_view/template_handlers/rjs' diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb deleted file mode 100644 index a0ebaefeef..0000000000 --- a/actionpack/lib/action_view/template_handlers/compilable.rb +++ /dev/null @@ -1,20 +0,0 @@ -module ActionView - module TemplateHandlers - module Compilable - def self.included(base) - base.extend ClassMethod - end - - module ClassMethod - # If a handler is mixin this module, set compilable to true - def compilable? - true - end - end - - def render(template, local_assigns = {}) - @view.send(:execute, template, local_assigns) - end - end - end -end diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 92b6aa4f2f..72c01a9102 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -31,16 +31,8 @@ end class MultipleExtensions < LayoutTest end -class MabView < ActionView::TemplateHandler - def initialize(view) - end - - def render(template, local_assigns) - template.source - end -end - -ActionView::Template::register_template_handler :mab, MabView +ActionView::Template::register_template_handler :mab, + lambda { |template| template.source.inspect } class LayoutAutoDiscoveryTest < Test::Unit::TestCase def setup diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index cc5b4900dc..b1af043099 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -94,38 +94,18 @@ class ViewRenderTest < Test::Unit::TestCase assert_equal "Hello, World!", @view.render(:inline => "Hello, World!", :type => :foo) end - class CustomHandler < ActionView::TemplateHandler - def render(template, local_assigns) - [template.source, local_assigns].inspect - end - end - - def test_render_inline_with_custom_type - ActionView::Template.register_template_handler :foo, CustomHandler - assert_equal '["Hello, World!", {}]', @view.render(:inline => "Hello, World!", :type => :foo) - end - - def test_render_inline_with_locals_and_custom_type - ActionView::Template.register_template_handler :foo, CustomHandler - assert_equal '["Hello, <%= name %>!", {:name=>"Josh"}]', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) - end - - class CompilableCustomHandler < ActionView::TemplateHandler - include ActionView::TemplateHandlers::Compilable - - def compile(template) - "@output_buffer = ''\n" + - "@output_buffer << 'source: #{template.source.inspect}'\n" - end + CustomHandler = lambda do |template| + "@output_buffer = ''\n" + + "@output_buffer << 'source: #{template.source.inspect}'\n" end def test_render_inline_with_compilable_custom_type - ActionView::Template.register_template_handler :foo, CompilableCustomHandler + ActionView::Template.register_template_handler :foo, CustomHandler assert_equal 'source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo) end def test_render_inline_with_locals_and_compilable_custom_type - ActionView::Template.register_template_handler :foo, CompilableCustomHandler + ActionView::Template.register_template_handler :foo, CustomHandler assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) end end -- cgit v1.2.3 From c3d1fda555c4bd5f8821d830c685ae5d0e7e52d0 Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Fri, 18 Jul 2008 20:14:12 -0500 Subject: Set the response content type to that of found template if not explicitly set elsewhere [#444 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_view/base.rb | 6 ++++++ actionpack/lib/action_view/renderable.rb | 5 ++++- actionpack/lib/action_view/template.rb | 5 +++++ actionpack/lib/action_view/template_handlers/builder.rb | 3 +-- actionpack/test/controller/render_test.rb | 15 ++++++++++----- .../test/fixtures/test/implicit_content_type.atom.builder | 2 ++ 6 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 actionpack/test/fixtures/test/implicit_content_type.atom.builder diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index ae6b284854..fe51af62e6 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -379,6 +379,12 @@ module ActionView #:nodoc: @assigns.each { |key, value| instance_variable_set("@#{key}", value) } end + def set_controller_content_type(content_type) + if controller.respond_to?(:response) + controller.response.content_type ||= content_type + end + end + def execute(method, local_assigns = {}) send(method, local_assigns) do |*names| instance_variable_get "@content_for_#{names.first || 'layout'}" diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index 4f865cbced..2b825ac4e9 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -24,10 +24,13 @@ module ActionView memoize :compiled_source def render(view, local_assigns = {}) + compile(local_assigns) + view._first_render ||= self view._last_render = self + view.send(:evaluate_assigns) - compile(local_assigns) + view.send(:set_controller_content_type, mime_type) if respond_to?(:mime_type) view.send(:execute, method(local_assigns), local_assigns) end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index eba42518d7..1f528dd900 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -22,6 +22,11 @@ module ActionView #:nodoc: end memoize :format_and_extension + def mime_type + Mime::Type.lookup_by_extension(format) if format + end + memoize :mime_type + def path [base_path, [name, format, extension].compact.join('.')].compact.join('/') end diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template_handlers/builder.rb index 335ec1abb4..7d24a5c423 100644 --- a/actionpack/lib/action_view/template_handlers/builder.rb +++ b/actionpack/lib/action_view/template_handlers/builder.rb @@ -6,8 +6,7 @@ module ActionView include Compilable def compile(template) - # ActionMailer does not have a response - "controller.respond_to?(:response) && controller.response.content_type ||= Mime::XML;" + + "set_controller_content_type(Mime::XML);" + "xml = ::Builder::XmlMarkup.new(:indent => 2);" + "self.output_buffer = xml.target!;" + template.source + diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 041c54c7fd..76832f5713 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -197,11 +197,11 @@ class TestController < ActionController::Base def render_alternate_default # For this test, the method "default_render" is overridden: - @alternate_default_render = lambda { - render :update do |page| - page.replace :foo, :partial => 'partial' - end - } + @alternate_default_render = lambda do + render :update do |page| + page.replace :foo, :partial => 'partial' + end + end end def rescue_action(e) raise end @@ -467,6 +467,11 @@ class RenderTest < Test::Unit::TestCase get :render_xml_with_custom_content_type assert_equal "application/atomsvc+xml", @response.content_type end + + def test_should_use_implicit_content_type + get :implicit_content_type, :format => 'atom' + assert_equal Mime::ATOM, @response.content_type + end end class EtagRenderTest < Test::Unit::TestCase diff --git a/actionpack/test/fixtures/test/implicit_content_type.atom.builder b/actionpack/test/fixtures/test/implicit_content_type.atom.builder new file mode 100644 index 0000000000..2fcb32d247 --- /dev/null +++ b/actionpack/test/fixtures/test/implicit_content_type.atom.builder @@ -0,0 +1,2 @@ +xml.atom do +end -- cgit v1.2.3 From d39485078ec56e25a96e97d44b53498d8a1c7426 Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Fri, 18 Jul 2008 20:19:03 -0500 Subject: Raise ArgumentError if an invalid method is specified as part of a route's conditions. Also raise an error if HEAD is specified as the method, as rails routes all HEAD requests through the equivalent GET, though doesn't return the response body [#182 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_controller/resources.rb | 9 +++++---- actionpack/lib/action_controller/routing/builder.rb | 15 +++++++++++++++ actionpack/test/controller/resources_test.rb | 20 ++++++++++++++++++++ actionpack/test/controller/routing_test.rb | 16 ++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index b11aa5625b..0614b9a4d9 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -307,13 +307,13 @@ module ActionController # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_' # # You may also use :name_prefix to override the generic named routes in a nested resource: - # + # # map.resources :articles do |article| # article.resources :comments, :name_prefix => nil - # end - # + # end + # # This will yield named resources like so: - # + # # comments_url(@article) # comment_url(@article, @comment) # @@ -559,6 +559,7 @@ module ActionController def action_options_for(action, resource, method = nil) default_options = { :action => action.to_s } require_id = !resource.kind_of?(SingletonResource) + case default_options[:action] when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements) when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements) diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index b8323847fd..912999d845 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -76,6 +76,8 @@ module ActionController defaults = (options.delete(:defaults) || {}).dup conditions = (options.delete(:conditions) || {}).dup + validate_route_conditions(conditions) + path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact options.each do |key, value| hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements @@ -198,6 +200,19 @@ module ActionController route end + + private + def validate_route_conditions(conditions) + if method = conditions[:method] + if method == :head + raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers" + end + + unless HTTP_METHODS.include?(method.to_sym) + raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}" + end + end + end end end end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 0f7924649a..e153b0cc98 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -516,6 +516,26 @@ class ResourcesTest < Test::Unit::TestCase end end + def test_should_not_allow_invalid_head_method_for_member_routes + with_routing do |set| + set.draw do |map| + assert_raises(ArgumentError) do + map.resources :messages, :member => {:something => :head} + end + end + end + end + + def test_should_not_allow_invalid_http_methods_for_member_routes + with_routing do |set| + set.draw do |map| + assert_raises(ArgumentError) do + map.resources :messages, :member => {:something => :invalid} + end + end + end + end + def test_resource_action_separator with_routing do |set| set.draw do |map| diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index c5ccb71582..079189d7b3 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1801,6 +1801,22 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do end end + def test_route_requirements_with_invalid_http_method_is_invalid + assert_raises ArgumentError do + set.draw do |map| + map.connect 'valid/route', :controller => 'pages', :action => 'show', :conditions => {:method => :invalid} + end + end + end + + def test_route_requirements_with_head_method_condition_is_invalid + assert_raises ArgumentError do + set.draw do |map| + map.connect 'valid/route', :controller => 'pages', :action => 'show', :conditions => {:method => :head} + end + end + end + def test_non_path_route_requirements_match_all set.draw do |map| map.connect 'page/37s', :controller => 'pages', :action => 'show', :name => /(jamis|david)/ -- cgit v1.2.3 From c609be45966316bb107e0bad2b0935ac4a0d0c41 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 23:30:36 -0500 Subject: Ruby 1.9: Ensure Memoizable#freeze is only overriden once to avoid an endless loop --- activesupport/lib/active_support/memoizable.rb | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 59fecbecb1..f7cd73d39c 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,19 +1,5 @@ module ActiveSupport module Memoizable #:nodoc: - def self.extended(obj) - klass = obj.respond_to?(:class_eval) ? obj : obj.metaclass - klass.class_eval do - def freeze - methods.each do |method| - if m = method.to_s.match(/^unmemoized_(.*)/) - send(m[1]) - end - end - super - end - end - end - def memoize(*symbols) symbols.each do |symbol| original_method = "unmemoized_#{symbol}" @@ -23,6 +9,18 @@ module ActiveSupport raise "Already memoized #{symbol}" if klass.instance_methods.map(&:to_s).include?(original_method) klass.class_eval <<-EOS, __FILE__, __LINE__ + unless instance_methods.map(&:to_s).include?("freeze_without_memoizable") + alias_method :freeze_without_memoizable, :freeze + def freeze + methods.each do |method| + if m = method.to_s.match(/^unmemoized_(.*)/) + send(m[1]) + end + end + freeze_without_memoizable + end + end + alias_method :#{original_method}, :#{symbol} def #{symbol}(reload = false) if !reload && defined? #{memoized_ivar} -- cgit v1.2.3 From 108ed4a5660d4ebdc52563250463c762e3f27988 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 00:24:02 -0500 Subject: Ruby 1.9: Strip encoding from ERB source since you can not change character encoding during a method --- actionpack/lib/action_view/template_handlers/erb.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index 2f2febaa52..3def949f1e 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -48,8 +48,11 @@ module ActionView self.erb_trim_mode = '-' def compile(template) - src = ::ERB.new(template.source, nil, erb_trim_mode, '@output_buffer').src - "__in_erb_template=true;#{src}" + src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src + + # Ruby 1.9 prepends an encoding to the source. However this is + # useless because you can only set an encoding on the first line + RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src end end end -- cgit v1.2.3 From cab168ac9bbe24e5842fb7677d3fac820ddbc18c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 00:46:02 -0500 Subject: Ruby 1.9: Fixed regexp warning by replacing nested repeat operator + and ? with '*' --- actionpack/lib/action_view/helpers/text_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 3e3452b615..9342b38680 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -468,7 +468,7 @@ module ActionView [-\w]+ # subdomain or domain (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port - (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path + (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$])))*)* # path (?:\?[\w\+@%&=.;-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) -- cgit v1.2.3 From f2059393481ceb632abc7a9d92670e409020d5bd Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Sat, 19 Jul 2008 09:58:09 +0100 Subject: Ensure checked value is a string when validating case-sensitive uniqueness [#361 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_test.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 3eec1305e4..b957ee3b9e 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -664,7 +664,7 @@ module ActiveRecord # As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate # column in ruby when case sensitive option if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text? - found = results.any? { |a| a[attr_name.to_s] == value } + found = results.any? { |a| a[attr_name.to_s] == value.to_s } end if found diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 60b00b3e8f..4b2d28c80b 100755 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -477,6 +477,15 @@ class ValidationsTest < ActiveRecord::TestCase assert_not_equal "has already been taken", t3.errors.on(:title) end + def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer + Topic.validates_uniqueness_of(:title, :case_sensitve => true) + t = Topic.create!('title' => 101) + + t2 = Topic.new('title' => 101) + assert !t2.valid? + assert t2.errors.on(:title) + end + def test_validate_uniqueness_with_non_standard_table_names i1 = WarehouseThing.create(:value => 1000) assert !i1.valid?, "i1 should not be valid" -- cgit v1.2.3 From d84d99a8f7dc672b050a6ab891c1680a323a7c97 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 10:52:30 -0500 Subject: Undefine old run method --- activesupport/lib/active_support/testing/setup_and_teardown.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 21d71eb92a..a514b61fea 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -15,12 +15,15 @@ module ActiveSupport define_callbacks :setup, :teardown if defined?(::Mini) + undef_method :run alias_method :run, :run_with_callbacks_and_miniunit else begin require 'mocha' + undef_method :run alias_method :run, :run_with_callbacks_and_mocha rescue LoadError + undef_method :run alias_method :run, :run_with_callbacks_and_testunit end end -- cgit v1.2.3 From 8c2e839e5a0fb1662ae867c70114c3fc91850a55 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 11:04:23 -0500 Subject: Fix some warnings in i18n lib --- activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb | 6 +++--- .../lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb index 2185194da9..1bb65263a3 100755 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb @@ -53,7 +53,7 @@ module I18n # storage. Decoupled for backends like a db backend that persist their # translations, so the backend can decide whether/when to yield or not. def populate(&block) - backend.populate &block + backend.populate(&block) end # Stores translations for the given locale in the backend. @@ -173,8 +173,8 @@ module I18n # keys are Symbols. def normalize_translation_keys(locale, key, scope) keys = [locale] + Array(scope) + [key] - keys = keys.map{|key| key.to_s.split(/\./) } - keys.flatten.map{|key| key.to_sym} + keys = keys.map{|k| k.to_s.split(/\./) } + keys.flatten.map{|k| k.to_sym} end end end diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb index 284f2bfcbd..b8be1cecfb 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb @@ -23,7 +23,7 @@ module I18n def translate(locale, key, options = {}) raise InvalidLocale.new(locale) if locale.nil? - return key.map{|key| translate locale, key, options } if key.is_a? Array + return key.map{|k| translate locale, k, options } if key.is_a? Array reserved = :scope, :default count, scope, default = options.values_at(:count, *reserved) @@ -66,7 +66,7 @@ module I18n def lookup(locale, key, scope = []) return unless key keys = I18n.send :normalize_translation_keys, locale, key, scope - keys.inject(@@translations){|result, key| result[key.to_sym] or return nil } + keys.inject(@@translations){|result, k| result[k.to_sym] or return nil } end # Evaluates a default translation. -- cgit v1.2.3 From b74b97fef5d94f91d6fbf9aec20516c7fe4ce24d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 11:14:12 -0500 Subject: Update uses_mocha in ActionMailer and ActiveResource --- actionmailer/test/abstract_unit.rb | 18 +++++++++++++----- activeresource/test/abstract_unit.rb | 22 +++++++++++++--------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index 11058a770d..107b2e8bbe 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -30,12 +30,20 @@ class Net::SMTP end end -# Wrap tests that use Mocha and skip if unavailable. -def uses_mocha(test_name) - gem 'mocha', ">=0.9.0" +def uses_gem(gem_name, test_name, version = '> 0') + require 'rubygems' + gem gem_name.to_s, version + require gem_name.to_s yield -rescue Gem::LoadError - $stderr.puts "Skipping #{test_name} tests (Mocha >= 0.5 is required). `gem install mocha` and try again." +rescue LoadError + $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." +end + +# Wrap tests that use Mocha and skip if unavailable. +unless defined? uses_mocha + def uses_mocha(test_name, &block) + uses_gem('mocha', test_name, '>= 0.5.5', &block) + end end def set_delivery_method(delivery_method) diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 615a6d9222..e612412033 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -9,14 +9,18 @@ require 'setter_trap' ActiveResource::Base.logger = Logger.new("#{File.dirname(__FILE__)}/debug.log") +def uses_gem(gem_name, test_name, version = '> 0') + require 'rubygems' + gem gem_name.to_s, version + require gem_name.to_s + yield +rescue LoadError + $stderr.puts "Skipping #{test_name} tests. `gem install #{gem_name}` and try again." +end + # Wrap tests that use Mocha and skip if unavailable. -def uses_mocha(test_name) - unless Object.const_defined?(:Mocha) - require 'mocha' - require 'stubba' +unless defined? uses_mocha + def uses_mocha(test_name, &block) + uses_gem('mocha', test_name, '>= 0.5.5', &block) end - yield -rescue LoadError => load_error - raise unless load_error.message =~ /mocha/i - $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again." -end \ No newline at end of file +end -- cgit v1.2.3 From 3fdd1acab61a46ab4823d63ad0bab4879d2bb446 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 11:30:15 -0500 Subject: Dropped SQLite 2 from default test runner --- activerecord/Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 60b17e02b9..983528aff7 100755 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -30,7 +30,7 @@ desc 'Run mysql, sqlite, and postgresql tests by default' task :default => :test desc 'Run mysql, sqlite, and postgresql tests' -task :test => %w(test_mysql test_sqlite test_sqlite3 test_postgresql) +task :test => %w(test_mysql test_sqlite3 test_postgresql) for adapter in %w( mysql postgresql sqlite sqlite3 firebird db2 oracle sybase openbase frontbase ) Rake::TestTask.new("test_#{adapter}") { |t| -- cgit v1.2.3 From 576cae004342899b0506a7834edc524a02a7d9ef Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 11:34:32 -0500 Subject: Stub out timestamped_migrations in generator tests --- railties/test/generators/generator_test_helper.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/railties/test/generators/generator_test_helper.rb b/railties/test/generators/generator_test_helper.rb index 80d5b145be..0901b215e4 100644 --- a/railties/test/generators/generator_test_helper.rb +++ b/railties/test/generators/generator_test_helper.rb @@ -5,9 +5,10 @@ require 'fileutils' module ActiveRecord class Base class << self - attr_accessor :pluralize_table_names + attr_accessor :pluralize_table_names, :timestamped_migrations end self.pluralize_table_names = true + self.timestamped_migrations = true end module ConnectionAdapters -- cgit v1.2.3 From e0d7bace4ecb9152fac112e809af521e36fbc6a5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 11:42:27 -0500 Subject: Prefer Mongrel over Thin [#658 state:resolved] --- railties/lib/commands/server.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/commands/server.rb b/railties/lib/commands/server.rb index 7306c248fb..15f417b5be 100644 --- a/railties/lib/commands/server.rb +++ b/railties/lib/commands/server.rb @@ -23,10 +23,10 @@ server = case ARGV.first when "lighttpd", "mongrel", "new_mongrel", "webrick", "thin" ARGV.shift else - if defined?(Thin) - "thin" - elsif defined?(Mongrel) + if defined?(Mongrel) "mongrel" + elsif defined?(Thin) + "thin" elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `lighttpd -version` }.blank? && defined?(FCGI) "lighttpd" else -- cgit v1.2.3 From 746122735269b9077c7d5d99d88e8b22d88ad8d5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 12:23:08 -0500 Subject: Ruby 1.9: Call join on template_root instead of to_s --- actionmailer/lib/action_mailer/base.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 5a71935009..e4920f0c86 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -529,7 +529,7 @@ module ActionMailer #:nodoc: end def render_message(method_name, body) - render :file => method_name, :body => body, :use_full_path => true + render :file => method_name, :body => body end def render(opts) @@ -537,12 +537,11 @@ module ActionMailer #:nodoc: if opts[:file] && opts[:file] !~ /\// opts[:file] = "#{mailer_name}/#{opts[:file]}" end - opts[:use_full_path] = true initialize_template_class(body).render(opts) end def template_path - "#{template_root}/#{mailer_name}" + "#{template_root.join}/#{mailer_name}" end def initialize_template_class(assigns) -- cgit v1.2.3 From e23156e87bd32206a5ea529fbecc04fdf37d7bc2 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 12:35:42 -0500 Subject: Only create a path for ActionMailer template root instead of a path set. Better fix than 7461227 --- actionmailer/lib/action_mailer/base.rb | 4 ++-- actionmailer/test/mail_service_test.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index e4920f0c86..bf60e2f3d5 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -426,7 +426,7 @@ module ActionMailer #:nodoc: end def template_root=(root) - write_inheritable_attribute(:template_root, ActionView::PathSet.new(Array(root))) + write_inheritable_attribute(:template_root, ActionView::PathSet::Path.new(root)) end end @@ -541,7 +541,7 @@ module ActionMailer #:nodoc: end def template_path - "#{template_root.join}/#{mailer_name}" + "#{template_root}/#{mailer_name}" end def initialize_template_class(assigns) diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index 7f4a8817ca..e5ecb0e254 100755 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -942,13 +942,13 @@ end # uses_mocha class InheritableTemplateRootTest < Test::Unit::TestCase def test_attr expected = "#{File.dirname(__FILE__)}/fixtures/path.with.dots" - assert_equal [expected], FunkyPathMailer.template_root.map(&:to_s) + assert_equal expected, FunkyPathMailer.template_root sub = Class.new(FunkyPathMailer) sub.template_root = 'test/path' - assert_equal ['test/path'], sub.template_root.map(&:to_s) - assert_equal [expected], FunkyPathMailer.template_root.map(&:to_s) + assert_equal 'test/path', sub.template_root + assert_equal expected, FunkyPathMailer.template_root end end -- cgit v1.2.3 From 938caf4e6b2448b45939d36824794ea0aa5e1804 Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Sat, 19 Jul 2008 12:40:30 -0500 Subject: Removed unused option from FormHelper#fields_for [#641 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_view/helpers/form_helper.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index fa26aa4640..4fa46d9ee3 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -304,10 +304,6 @@ module ActionView when String, Symbol object_name = record_or_name_or_array object = args.first - when Array - object = record_or_name_or_array.last - object_name = ActionController::RecordIdentifier.singular_class_name(object) - apply_form_for_options!(record_or_name_or_array, options) else object = record_or_name_or_array object_name = ActionController::RecordIdentifier.singular_class_name(object) -- cgit v1.2.3 From c98692abcfd3576ee5fcde3910330d1eb39a18a5 Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Sat, 19 Jul 2008 13:06:43 -0500 Subject: Removed handling of string parameter in link_to to have all URL generation done by url_for Signed-off-by: Joshua Peek --- actionpack/lib/action_view/helpers/url_helper.rb | 93 +++++++++++++----------- actionpack/test/template/url_helper_test.rb | 11 ++- 2 files changed, 60 insertions(+), 44 deletions(-) diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 94e1f1d33a..f31502d99d 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -3,8 +3,8 @@ require 'action_view/helpers/javascript_helper' module ActionView module Helpers #:nodoc: # Provides a set of methods for making links and getting URLs that - # depend on the routing subsystem (see ActionController::Routing). - # This allows you to use the same format for links in views + # depend on the routing subsystem (see ActionController::Routing). + # This allows you to use the same format for links in views # and controllers. module UrlHelper include JavaScriptHelper @@ -33,8 +33,8 @@ module ActionView # # If you instead of a hash pass a record (like an Active Record or Active Resource) as the options parameter, # you'll trigger the named route for that record. The lookup will happen on the name of the class. So passing - # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as - # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route). + # a Workshop object will attempt to use the workshop_path route. If you have a nested route, such as + # admin_workshop_path you'll have to call that explicitly (it's impossible for url_for to guess that route). # # ==== Examples # <%= url_for(:action => 'index') %> @@ -62,19 +62,33 @@ module ActionView # <%= url_for(@workshop) %> # # calls @workshop.to_s # # => /workshops/5 + # + # <%= url_for("http://www.example.com") %> + # # => http://www.example.com + # + # <%= url_for(:back) %> + # # if request.env["HTTP_REFERER"] is set to "http://www.example.com" + # # => http://www.example.com + # + # <%= url_for(:back) %> + # # if request.env["HTTP_REFERER"] is not set or is blank + # # => javascript:history.back() def url_for(options = {}) options ||= {} - case options + url = case options + when String + escape = true + options when Hash options = { :only_path => options[:host].nil? }.update(options.symbolize_keys) escape = options.key?(:escape) ? options.delete(:escape) : true - url = @controller.send(:url_for, options) - when String - escape = true - url = options + @controller.send(:url_for, options) + when :back + escape = false + @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' else escape = false - url = polymorphic_path(options) + polymorphic_path(options) end escape ? escape_once(url) : url @@ -116,8 +130,8 @@ module ActionView # # Note that if the user has JavaScript disabled, the request will fall back # to using GET. If :href => '#' is used and the user has JavaScript disabled - # clicking the link will have no effect. If you are relying on the POST - # behavior, your should check for it in your controller's action by using the + # clicking the link will have no effect. If you are relying on the POST + # behavior, your should check for it in your controller's action by using the # request object's methods for post?, delete? or put?. # # You can mix and match the +html_options+ with the exception of @@ -141,8 +155,8 @@ module ActionView # # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile # # => Profile - # - # Similarly, + # + # Similarly, # # link_to "Profiles", profiles_path # # => Profiles @@ -197,9 +211,9 @@ module ActionView # # => View Image # # link_to "Delete Image", @image, :confirm => "Are you sure?", :method => :delete - # # => Delete Image def link_to(*args, &block) if block_given? @@ -211,14 +225,7 @@ module ActionView options = args.second || {} html_options = args.third - url = case options - when String - options - when :back - @controller.request.env["HTTP_REFERER"] || 'javascript:history.back()' - else - self.url_for(options) - end + url = url_for(options) if html_options html_options = html_options.stringify_keys @@ -228,7 +235,7 @@ module ActionView else tag_options = nil end - + href_attr = "href=\"#{url}\"" unless href "#{name || url}" end @@ -260,7 +267,7 @@ module ActionView # * :confirm - This will add a JavaScript confirm # prompt with the question specified. If the user accepts, the link is # processed normally, otherwise no action is taken. - # + # # ==== Examples # <%= button_to "New", :action => "new" %> # # => "
@@ -286,12 +293,12 @@ module ActionView end form_method = method.to_s == 'get' ? 'get' : 'post' - + request_token_tag = '' if form_method == 'post' && protect_against_forgery? request_token_tag = tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token) end - + if confirm = html_options.delete("confirm") html_options["onclick"] = "return #{confirm_javascript_function(confirm)};" end @@ -309,7 +316,7 @@ module ActionView # Creates a link tag of the given +name+ using a URL created by the set of # +options+ unless the current request URI is the same as the links, in # which case only the name is returned (or the given block is yielded, if - # one exists). You can give link_to_unless_current a block which will + # one exists). You can give link_to_unless_current a block which will # specialize the default behavior (e.g., show a "Start Here" link rather # than the link's text). # @@ -336,13 +343,13 @@ module ActionView # # # The implicit block given to link_to_unless_current is evaluated if the current - # action is the action given. So, if we had a comments page and wanted to render a + # action is the action given. So, if we had a comments page and wanted to render a # "Go Back" link instead of a link to the comments page, we could do something like this... - # - # <%= + # + # <%= # link_to_unless_current("Comment", { :controller => 'comments', :action => 'new}) do - # link_to("Go back", { :controller => 'posts', :action => 'index' }) - # end + # link_to("Go back", { :controller => 'posts', :action => 'index' }) + # end # %> def link_to_unless_current(name, options = {}, html_options = {}, &block) link_to_unless current_page?(options), name, options, html_options, &block @@ -359,10 +366,10 @@ module ActionView # # If the user is logged in... # # => Reply # - # <%= + # <%= # link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name| # link_to(name, { :controller => "accounts", :action => "signup" }) - # end + # end # %> # # If the user is logged in... # # => Reply @@ -391,10 +398,10 @@ module ActionView # # If the user isn't logged in... # # => Login # - # <%= + # <%= # link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do # link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user }) - # end + # end # %> # # If the user isn't logged in... # # => Login @@ -431,20 +438,20 @@ module ActionView # * :bcc - Blind Carbon Copy additional recipients on the email. # # ==== Examples - # mail_to "me@domain.com" + # mail_to "me@domain.com" # # => me@domain.com # - # mail_to "me@domain.com", "My email", :encode => "javascript" + # mail_to "me@domain.com", "My email", :encode => "javascript" # # => # - # mail_to "me@domain.com", "My email", :encode => "hex" + # mail_to "me@domain.com", "My email", :encode => "hex" # # => My email # - # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email" + # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email" # # => # # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com", - # :subject => "This is an example email" + # :subject => "This is an example email" # # => My email def mail_to(email_address, name = nil, html_options = {}) html_options = html_options.stringify_keys diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 91d5c6ffb5..867503fb29 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -28,6 +28,16 @@ class UrlHelperTest < ActionView::TestCase assert_equal "http://www.example.com?a=b&c=d", url_for("http://www.example.com?a=b&c=d") end + def test_url_for_with_back + @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {'HTTP_REFERER' => 'http://www.example.com/referer'}) + assert_equal 'http://www.example.com/referer', url_for(:back) + end + + def test_url_for_with_back_and_no_referer + @controller.request = RequestMock.new("http://www.example.com/weblog/show", nil, nil, {}) + assert_equal 'javascript:history.back()', url_for(:back) + end + # todo: missing test cases def test_button_to_with_straight_url assert_dom_equal "
", button_to("Hello", "http://www.example.com") @@ -419,7 +429,6 @@ class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase end end - class Workshop attr_accessor :id, :new_record -- cgit v1.2.3 From 706425e154a2a2581195c98309f30a18a0002a58 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 19 Jul 2008 13:56:38 -0500 Subject: Update Prototype to 1.6.0.2 (Patrick Joyce) [#599 status:committed] --- actionpack/CHANGELOG | 2 + .../action_view/helpers/javascripts/prototype.js | 304 ++++++++++----------- railties/html/javascripts/prototype.js | 304 ++++++++++----------- 3 files changed, 302 insertions(+), 308 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index f6432fe0be..ebe4c047b8 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Update Prototype to 1.6.0.2 #599 [Patrick Joyce] + * Conditional GET utility methods. [Jeremy Kemper] * etag!([:admin, post, current_user]) sets the ETag response header and returns head(:not_modified) if it matches the If-None-Match request header. * last_modified!(post.updated_at) sets Last-Modified and returns head(:not_modified) if it's no later than If-Modified-Since. diff --git a/actionpack/lib/action_view/helpers/javascripts/prototype.js b/actionpack/lib/action_view/helpers/javascripts/prototype.js index 546f9fe449..2c70b8a7e8 100644 --- a/actionpack/lib/action_view/helpers/javascripts/prototype.js +++ b/actionpack/lib/action_view/helpers/javascripts/prototype.js @@ -1,5 +1,5 @@ -/* Prototype JavaScript framework, version 1.6.0.1 - * (c) 2005-2007 Sam Stephenson +/* Prototype JavaScript framework, version 1.6.0.2 + * (c) 2005-2008 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ @@ -7,7 +7,7 @@ *--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.6.0.1', + Version: '1.6.0.2', Browser: { IE: !!(window.attachEvent && !window.opera), @@ -110,7 +110,7 @@ Object.extend(Object, { try { if (Object.isUndefined(object)) return 'undefined'; if (object === null) return 'null'; - return object.inspect ? object.inspect() : object.toString(); + return object.inspect ? object.inspect() : String(object); } catch (e) { if (e instanceof RangeError) return '...'; throw e; @@ -171,7 +171,8 @@ Object.extend(Object, { }, isArray: function(object) { - return object && object.constructor === Array; + return object != null && typeof object == "object" && + 'splice' in object && 'join' in object; }, isHash: function(object) { @@ -578,7 +579,7 @@ var Template = Class.create({ } return before + String.interpret(ctx); - }.bind(this)); + }); } }); Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; @@ -806,20 +807,20 @@ Object.extend(Enumerable, { function $A(iterable) { if (!iterable) return []; if (iterable.toArray) return iterable.toArray(); - var length = iterable.length, results = new Array(length); + var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; } if (Prototype.Browser.WebKit) { - function $A(iterable) { + $A = function(iterable) { if (!iterable) return []; if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && iterable.toArray) return iterable.toArray(); - var length = iterable.length, results = new Array(length); + var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; - } + }; } Array.from = $A; @@ -1298,7 +1299,7 @@ Ajax.Request = Class.create(Ajax.Base, { var contentType = response.getHeader('Content-type'); if (this.options.evalJS == 'force' - || (this.options.evalJS && contentType + || (this.options.evalJS && this.isSameOrigin() && contentType && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) this.evalResponse(); } @@ -1316,9 +1317,18 @@ Ajax.Request = Class.create(Ajax.Base, { } }, + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + getHeader: function(name) { try { - return this.transport.getResponseHeader(name); + return this.transport.getResponseHeader(name) || null; } catch (e) { return null } }, @@ -1391,7 +1401,8 @@ Ajax.Response = Class.create({ if (!json) return null; json = decodeURIComponent(escape(json)); try { - return json.evalJSON(this.request.options.sanitizeJSON); + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } @@ -1404,7 +1415,8 @@ Ajax.Response = Class.create({ this.responseText.blank()) return null; try { - return this.responseText.evalJSON(options.sanitizeJSON); + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } @@ -1608,24 +1620,28 @@ Element.Methods = { Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) insertions = {bottom:insertions}; - var content, t, range; + var content, insert, tagName, childNodes; - for (position in insertions) { + for (var position in insertions) { content = insertions[position]; position = position.toLowerCase(); - t = Element._insertionTranslations[position]; + insert = Element._insertionTranslations[position]; if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) { - t.insert(element, content); + insert(element, content); continue; } content = Object.toHTML(content); - range = element.ownerDocument.createRange(); - t.initializeRange(element, range); - t.insert(element, range.createContextualFragment(content.stripScripts())); + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); content.evalScripts.bind(content).defer(); } @@ -1670,7 +1686,7 @@ Element.Methods = { }, descendants: function(element) { - return $(element).getElementsBySelector("*"); + return $(element).select("*"); }, firstDescendant: function(element) { @@ -1709,32 +1725,31 @@ Element.Methods = { element = $(element); if (arguments.length == 1) return $(element.parentNode); var ancestors = element.ancestors(); - return expression ? Selector.findElement(ancestors, expression, index) : - ancestors[index || 0]; + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); }, down: function(element, expression, index) { element = $(element); if (arguments.length == 1) return element.firstDescendant(); - var descendants = element.descendants(); - return expression ? Selector.findElement(descendants, expression, index) : - descendants[index || 0]; + return Object.isNumber(expression) ? element.descendants()[expression] : + element.select(expression)[index || 0]; }, previous: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); var previousSiblings = element.previousSiblings(); - return expression ? Selector.findElement(previousSiblings, expression, index) : - previousSiblings[index || 0]; + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); }, next: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); var nextSiblings = element.nextSiblings(); - return expression ? Selector.findElement(nextSiblings, expression, index) : - nextSiblings[index || 0]; + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); }, select: function() { @@ -1860,7 +1875,8 @@ Element.Methods = { do { ancestor = ancestor.parentNode; } while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); } - if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex); + if (nextAncestor && nextAncestor.sourceIndex) + return (e > a && e < nextAncestor.sourceIndex); } while (element = element.parentNode) @@ -2004,7 +2020,7 @@ Element.Methods = { if (element) { if (element.tagName == 'BODY') break; var p = Element.getStyle(element, 'position'); - if (p == 'relative' || p == 'absolute') break; + if (p !== 'static') break; } } while (element); return Element._returnOffset(valueL, valueT); @@ -2153,46 +2169,6 @@ Element._attributeTranslations = { } }; - -if (!document.createRange || Prototype.Browser.Opera) { - Element.Methods.insert = function(element, insertions) { - element = $(element); - - if (Object.isString(insertions) || Object.isNumber(insertions) || - Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) - insertions = { bottom: insertions }; - - var t = Element._insertionTranslations, content, position, pos, tagName; - - for (position in insertions) { - content = insertions[position]; - position = position.toLowerCase(); - pos = t[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - pos.insert(element, content); - continue; - } - - content = Object.toHTML(content); - tagName = ((position == 'before' || position == 'after') - ? element.parentNode : element).tagName.toUpperCase(); - - if (t.tags[tagName]) { - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - if (position == 'top' || position == 'after') fragments.reverse(); - fragments.each(pos.insert.curry(element)); - } - else element.insertAdjacentHTML(pos.adjacency, content.stripScripts()); - - content.evalScripts.bind(content).defer(); - } - - return element; - }; -} - if (Prototype.Browser.Opera) { Element.Methods.getStyle = Element.Methods.getStyle.wrap( function(proceed, element, style) { @@ -2237,12 +2213,31 @@ if (Prototype.Browser.Opera) { } else if (Prototype.Browser.IE) { - $w('positionedOffset getOffsetParent viewportOffset').each(function(method) { + // IE doesn't report offsets correctly for static elements, so we change them + // to "relative" to get the values, then change them back. + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { Element.Methods[method] = Element.Methods[method].wrap( function(proceed, element) { element = $(element); var position = element.getStyle('position'); - if (position != 'static') return proceed(element); + if (position !== 'static') return proceed(element); + // Trigger hasLayout on the offset parent so that IE6 reports + // accurate offsetTop and offsetLeft values for position: fixed. + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); element.setStyle({ position: 'relative' }); var value = proceed(element); element.setStyle({ position: position }); @@ -2324,7 +2319,10 @@ else if (Prototype.Browser.IE) { }; Element._attributeTranslations.write = { - names: Object.clone(Element._attributeTranslations.read.names), + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), values: { checked: function(element, value) { element.checked = !!value; @@ -2444,7 +2442,7 @@ if (Prototype.Browser.IE || Prototype.Browser.Opera) { }; } -if (document.createElement('div').outerHTML) { +if ('outerHTML' in document.createElement('div')) { Element.Methods.replace = function(element, content) { element = $(element); @@ -2482,45 +2480,25 @@ Element._returnOffset = function(l, t) { Element._getContentFromAnonymousElement = function(tagName, html) { var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; return $A(div.childNodes); }; Element._insertionTranslations = { - before: { - adjacency: 'beforeBegin', - insert: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - initializeRange: function(element, range) { - range.setStartBefore(element); - } + before: function(element, node) { + element.parentNode.insertBefore(node, element); }, - top: { - adjacency: 'afterBegin', - insert: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - initializeRange: function(element, range) { - range.selectNodeContents(element); - range.collapse(true); - } + top: function(element, node) { + element.insertBefore(node, element.firstChild); }, - bottom: { - adjacency: 'beforeEnd', - insert: function(element, node) { - element.appendChild(node); - } + bottom: function(element, node) { + element.appendChild(node); }, - after: { - adjacency: 'afterEnd', - insert: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - initializeRange: function(element, range) { - range.setStartAfter(element); - } + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); }, tags: { TABLE: ['', '
', 1], @@ -2532,7 +2510,6 @@ Element._insertionTranslations = { }; (function() { - this.bottom.initializeRange = this.top.initializeRange; Object.extend(this.tags, { THEAD: this.tags.TBODY, TFOOT: this.tags.TBODY, @@ -2716,7 +2693,7 @@ document.viewport = { window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); } }; -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style * license. Please see http://www.yui-ext.com/ for more information. */ @@ -2959,13 +2936,13 @@ Object.extend(Selector, { }, criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', attr: function(m) { m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); }, pseudo: function(m) { if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); @@ -2989,7 +2966,8 @@ Object.extend(Selector, { tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, id: /^#([\w\-\*]+)(\b|$)/, className: /^\.([\w\-\*]+)(\b|$)/, - pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/, + pseudo: +/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, attrPresence: /^\[([\w]+)\]/, attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }, @@ -3014,7 +2992,7 @@ Object.extend(Selector, { attr: function(element, matches) { var nodeValue = Element.readAttribute(element, matches[1]); - return Selector.operators[matches[2]](nodeValue, matches[3]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); } }, @@ -3029,14 +3007,15 @@ Object.extend(Selector, { // marks an array of nodes for counting mark: function(nodes) { + var _true = Prototype.emptyFunction; for (var i = 0, node; node = nodes[i]; i++) - node._counted = true; + node._countedByPrototype = _true; return nodes; }, unmark: function(nodes) { for (var i = 0, node; node = nodes[i]; i++) - node._counted = undefined; + node._countedByPrototype = undefined; return nodes; }, @@ -3044,15 +3023,15 @@ Object.extend(Selector, { // "ofType" flag indicates whether we're indexing for nth-of-type // rather than nth-child index: function(parentNode, reverse, ofType) { - parentNode._counted = true; + parentNode._countedByPrototype = Prototype.emptyFunction; if (reverse) { for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; } } else { for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; } }, @@ -3061,8 +3040,8 @@ Object.extend(Selector, { if (nodes.length == 0) return nodes; var results = [], n; for (var i = 0, l = nodes.length; i < l; i++) - if (!(n = nodes[i])._counted) { - n._counted = true; + if (!(n = nodes[i])._countedByPrototype) { + n._countedByPrototype = Prototype.emptyFunction; results.push(Element.extend(n)); } return Selector.handlers.unmark(results); @@ -3114,7 +3093,7 @@ Object.extend(Selector, { // TOKEN FUNCTIONS tagName: function(nodes, root, tagName, combinator) { - tagName = tagName.toUpperCase(); + var uTagName = tagName.toUpperCase(); var results = [], h = Selector.handlers; if (nodes) { if (combinator) { @@ -3127,7 +3106,7 @@ Object.extend(Selector, { if (tagName == "*") return nodes; } for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() == tagName) results.push(node); + if (node.tagName.toUpperCase() === uTagName) results.push(node); return results; } else return root.getElementsByTagName(tagName); }, @@ -3174,16 +3153,18 @@ Object.extend(Selector, { return results; }, - attrPresence: function(nodes, root, attr) { + attrPresence: function(nodes, root, attr, combinator) { if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); var results = []; for (var i = 0, node; node = nodes[i]; i++) if (Element.hasAttribute(node, attr)) results.push(node); return results; }, - attr: function(nodes, root, attr, value, operator) { + attr: function(nodes, root, attr, value, operator, combinator) { if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); var handler = Selector.operators[operator], results = []; for (var i = 0, node; node = nodes[i]; i++) { var nodeValue = Element.readAttribute(node, attr); @@ -3262,7 +3243,7 @@ Object.extend(Selector, { var h = Selector.handlers, results = [], indexed = [], m; h.mark(nodes); for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._counted) { + if (!node.parentNode._countedByPrototype) { h.index(node.parentNode, reverse, ofType); indexed.push(node.parentNode); } @@ -3300,7 +3281,7 @@ Object.extend(Selector, { var exclusions = new Selector(selector).findElements(root); h.mark(exclusions); for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._counted) results.push(node); + if (!node._countedByPrototype) results.push(node); h.unmark(exclusions); return results; }, @@ -3334,11 +3315,19 @@ Object.extend(Selector, { '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } }, + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + matchElements: function(elements, expression) { - var matches = new Selector(expression).findElements(), h = Selector.handlers; + var matches = $$(expression), h = Selector.handlers; h.mark(matches); for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._counted) results.push(element); + if (element._countedByPrototype) results.push(element); h.unmark(matches); return results; }, @@ -3351,11 +3340,7 @@ Object.extend(Selector, { }, findChildElements: function(element, expressions) { - var exprs = expressions.join(','); - expressions = []; - exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); - }); + expressions = Selector.split(expressions.join(',')); var results = [], h = Selector.handlers; for (var i = 0, l = expressions.length, selector; i < l; i++) { selector = new Selector(expressions[i].strip()); @@ -3366,13 +3351,22 @@ Object.extend(Selector, { }); if (Prototype.Browser.IE) { - // IE returns comment nodes on getElementsByTagName("*"). - // Filter them out. - Selector.handlers.concat = function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - }; + Object.extend(Selector.handlers, { + // IE returns comment nodes on getElementsByTagName("*"). + // Filter them out. + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + }, + + // IE improperly serializes _countedByPrototype in (inner|outer)HTML. + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } + }); } function $$() { @@ -3850,9 +3844,9 @@ Object.extend(Event, (function() { var cache = Event.cache; function getEventID(element) { - if (element._eventID) return element._eventID; + if (element._prototypeEventID) return element._prototypeEventID[0]; arguments.callee.id = arguments.callee.id || 1; - return element._eventID = ++arguments.callee.id; + return element._prototypeEventID = [++arguments.callee.id]; } function getDOMEventName(eventName) { @@ -3880,7 +3874,7 @@ Object.extend(Event, (function() { return false; Event.extend(event); - handler.call(element, event) + handler.call(element, event); }; wrapper.handler = handler; @@ -3962,11 +3956,12 @@ Object.extend(Event, (function() { if (element == document && document.createEvent && !element.dispatchEvent) element = document.documentElement; + var event; if (document.createEvent) { - var event = document.createEvent("HTMLEvents"); + event = document.createEvent("HTMLEvents"); event.initEvent("dataavailable", true, true); } else { - var event = document.createEventObject(); + event = document.createEventObject(); event.eventType = "ondataavailable"; } @@ -3995,20 +3990,21 @@ Element.addMethods({ Object.extend(document, { fire: Element.Methods.fire.methodize(), observe: Element.Methods.observe.methodize(), - stopObserving: Element.Methods.stopObserving.methodize() + stopObserving: Element.Methods.stopObserving.methodize(), + loaded: false }); (function() { /* Support for the DOMContentLoaded event is based on work by Dan Webb, Matthias Miller, Dean Edwards and John Resig. */ - var timer, fired = false; + var timer; function fireContentLoadedEvent() { - if (fired) return; + if (document.loaded) return; if (timer) window.clearInterval(timer); document.fire("dom:loaded"); - fired = true; + document.loaded = true; } if (document.addEventListener) { diff --git a/railties/html/javascripts/prototype.js b/railties/html/javascripts/prototype.js index 546f9fe449..2c70b8a7e8 100644 --- a/railties/html/javascripts/prototype.js +++ b/railties/html/javascripts/prototype.js @@ -1,5 +1,5 @@ -/* Prototype JavaScript framework, version 1.6.0.1 - * (c) 2005-2007 Sam Stephenson +/* Prototype JavaScript framework, version 1.6.0.2 + * (c) 2005-2008 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ @@ -7,7 +7,7 @@ *--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.6.0.1', + Version: '1.6.0.2', Browser: { IE: !!(window.attachEvent && !window.opera), @@ -110,7 +110,7 @@ Object.extend(Object, { try { if (Object.isUndefined(object)) return 'undefined'; if (object === null) return 'null'; - return object.inspect ? object.inspect() : object.toString(); + return object.inspect ? object.inspect() : String(object); } catch (e) { if (e instanceof RangeError) return '...'; throw e; @@ -171,7 +171,8 @@ Object.extend(Object, { }, isArray: function(object) { - return object && object.constructor === Array; + return object != null && typeof object == "object" && + 'splice' in object && 'join' in object; }, isHash: function(object) { @@ -578,7 +579,7 @@ var Template = Class.create({ } return before + String.interpret(ctx); - }.bind(this)); + }); } }); Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; @@ -806,20 +807,20 @@ Object.extend(Enumerable, { function $A(iterable) { if (!iterable) return []; if (iterable.toArray) return iterable.toArray(); - var length = iterable.length, results = new Array(length); + var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; } if (Prototype.Browser.WebKit) { - function $A(iterable) { + $A = function(iterable) { if (!iterable) return []; if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && iterable.toArray) return iterable.toArray(); - var length = iterable.length, results = new Array(length); + var length = iterable.length || 0, results = new Array(length); while (length--) results[length] = iterable[length]; return results; - } + }; } Array.from = $A; @@ -1298,7 +1299,7 @@ Ajax.Request = Class.create(Ajax.Base, { var contentType = response.getHeader('Content-type'); if (this.options.evalJS == 'force' - || (this.options.evalJS && contentType + || (this.options.evalJS && this.isSameOrigin() && contentType && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) this.evalResponse(); } @@ -1316,9 +1317,18 @@ Ajax.Request = Class.create(Ajax.Base, { } }, + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + getHeader: function(name) { try { - return this.transport.getResponseHeader(name); + return this.transport.getResponseHeader(name) || null; } catch (e) { return null } }, @@ -1391,7 +1401,8 @@ Ajax.Response = Class.create({ if (!json) return null; json = decodeURIComponent(escape(json)); try { - return json.evalJSON(this.request.options.sanitizeJSON); + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } @@ -1404,7 +1415,8 @@ Ajax.Response = Class.create({ this.responseText.blank()) return null; try { - return this.responseText.evalJSON(options.sanitizeJSON); + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); } catch (e) { this.request.dispatchException(e); } @@ -1608,24 +1620,28 @@ Element.Methods = { Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) insertions = {bottom:insertions}; - var content, t, range; + var content, insert, tagName, childNodes; - for (position in insertions) { + for (var position in insertions) { content = insertions[position]; position = position.toLowerCase(); - t = Element._insertionTranslations[position]; + insert = Element._insertionTranslations[position]; if (content && content.toElement) content = content.toElement(); if (Object.isElement(content)) { - t.insert(element, content); + insert(element, content); continue; } content = Object.toHTML(content); - range = element.ownerDocument.createRange(); - t.initializeRange(element, range); - t.insert(element, range.createContextualFragment(content.stripScripts())); + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); content.evalScripts.bind(content).defer(); } @@ -1670,7 +1686,7 @@ Element.Methods = { }, descendants: function(element) { - return $(element).getElementsBySelector("*"); + return $(element).select("*"); }, firstDescendant: function(element) { @@ -1709,32 +1725,31 @@ Element.Methods = { element = $(element); if (arguments.length == 1) return $(element.parentNode); var ancestors = element.ancestors(); - return expression ? Selector.findElement(ancestors, expression, index) : - ancestors[index || 0]; + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); }, down: function(element, expression, index) { element = $(element); if (arguments.length == 1) return element.firstDescendant(); - var descendants = element.descendants(); - return expression ? Selector.findElement(descendants, expression, index) : - descendants[index || 0]; + return Object.isNumber(expression) ? element.descendants()[expression] : + element.select(expression)[index || 0]; }, previous: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); var previousSiblings = element.previousSiblings(); - return expression ? Selector.findElement(previousSiblings, expression, index) : - previousSiblings[index || 0]; + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); }, next: function(element, expression, index) { element = $(element); if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); var nextSiblings = element.nextSiblings(); - return expression ? Selector.findElement(nextSiblings, expression, index) : - nextSiblings[index || 0]; + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); }, select: function() { @@ -1860,7 +1875,8 @@ Element.Methods = { do { ancestor = ancestor.parentNode; } while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); } - if (nextAncestor) return (e > a && e < nextAncestor.sourceIndex); + if (nextAncestor && nextAncestor.sourceIndex) + return (e > a && e < nextAncestor.sourceIndex); } while (element = element.parentNode) @@ -2004,7 +2020,7 @@ Element.Methods = { if (element) { if (element.tagName == 'BODY') break; var p = Element.getStyle(element, 'position'); - if (p == 'relative' || p == 'absolute') break; + if (p !== 'static') break; } } while (element); return Element._returnOffset(valueL, valueT); @@ -2153,46 +2169,6 @@ Element._attributeTranslations = { } }; - -if (!document.createRange || Prototype.Browser.Opera) { - Element.Methods.insert = function(element, insertions) { - element = $(element); - - if (Object.isString(insertions) || Object.isNumber(insertions) || - Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) - insertions = { bottom: insertions }; - - var t = Element._insertionTranslations, content, position, pos, tagName; - - for (position in insertions) { - content = insertions[position]; - position = position.toLowerCase(); - pos = t[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - pos.insert(element, content); - continue; - } - - content = Object.toHTML(content); - tagName = ((position == 'before' || position == 'after') - ? element.parentNode : element).tagName.toUpperCase(); - - if (t.tags[tagName]) { - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); - if (position == 'top' || position == 'after') fragments.reverse(); - fragments.each(pos.insert.curry(element)); - } - else element.insertAdjacentHTML(pos.adjacency, content.stripScripts()); - - content.evalScripts.bind(content).defer(); - } - - return element; - }; -} - if (Prototype.Browser.Opera) { Element.Methods.getStyle = Element.Methods.getStyle.wrap( function(proceed, element, style) { @@ -2237,12 +2213,31 @@ if (Prototype.Browser.Opera) { } else if (Prototype.Browser.IE) { - $w('positionedOffset getOffsetParent viewportOffset').each(function(method) { + // IE doesn't report offsets correctly for static elements, so we change them + // to "relative" to get the values, then change them back. + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { Element.Methods[method] = Element.Methods[method].wrap( function(proceed, element) { element = $(element); var position = element.getStyle('position'); - if (position != 'static') return proceed(element); + if (position !== 'static') return proceed(element); + // Trigger hasLayout on the offset parent so that IE6 reports + // accurate offsetTop and offsetLeft values for position: fixed. + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); element.setStyle({ position: 'relative' }); var value = proceed(element); element.setStyle({ position: position }); @@ -2324,7 +2319,10 @@ else if (Prototype.Browser.IE) { }; Element._attributeTranslations.write = { - names: Object.clone(Element._attributeTranslations.read.names), + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), values: { checked: function(element, value) { element.checked = !!value; @@ -2444,7 +2442,7 @@ if (Prototype.Browser.IE || Prototype.Browser.Opera) { }; } -if (document.createElement('div').outerHTML) { +if ('outerHTML' in document.createElement('div')) { Element.Methods.replace = function(element, content) { element = $(element); @@ -2482,45 +2480,25 @@ Element._returnOffset = function(l, t) { Element._getContentFromAnonymousElement = function(tagName, html) { var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; return $A(div.childNodes); }; Element._insertionTranslations = { - before: { - adjacency: 'beforeBegin', - insert: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - initializeRange: function(element, range) { - range.setStartBefore(element); - } + before: function(element, node) { + element.parentNode.insertBefore(node, element); }, - top: { - adjacency: 'afterBegin', - insert: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - initializeRange: function(element, range) { - range.selectNodeContents(element); - range.collapse(true); - } + top: function(element, node) { + element.insertBefore(node, element.firstChild); }, - bottom: { - adjacency: 'beforeEnd', - insert: function(element, node) { - element.appendChild(node); - } + bottom: function(element, node) { + element.appendChild(node); }, - after: { - adjacency: 'afterEnd', - insert: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - initializeRange: function(element, range) { - range.setStartAfter(element); - } + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); }, tags: { TABLE: ['', '
', 1], @@ -2532,7 +2510,6 @@ Element._insertionTranslations = { }; (function() { - this.bottom.initializeRange = this.top.initializeRange; Object.extend(this.tags, { THEAD: this.tags.TBODY, TFOOT: this.tags.TBODY, @@ -2716,7 +2693,7 @@ document.viewport = { window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); } }; -/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style * license. Please see http://www.yui-ext.com/ for more information. */ @@ -2959,13 +2936,13 @@ Object.extend(Selector, { }, criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}"); c = false;', + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', attr: function(m) { m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}"); c = false;').evaluate(m); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); }, pseudo: function(m) { if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); @@ -2989,7 +2966,8 @@ Object.extend(Selector, { tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, id: /^#([\w\-\*]+)(\b|$)/, className: /^\.([\w\-\*]+)(\b|$)/, - pseudo: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s)|(?=:))/, + pseudo: +/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, attrPresence: /^\[([\w]+)\]/, attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ }, @@ -3014,7 +2992,7 @@ Object.extend(Selector, { attr: function(element, matches) { var nodeValue = Element.readAttribute(element, matches[1]); - return Selector.operators[matches[2]](nodeValue, matches[3]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); } }, @@ -3029,14 +3007,15 @@ Object.extend(Selector, { // marks an array of nodes for counting mark: function(nodes) { + var _true = Prototype.emptyFunction; for (var i = 0, node; node = nodes[i]; i++) - node._counted = true; + node._countedByPrototype = _true; return nodes; }, unmark: function(nodes) { for (var i = 0, node; node = nodes[i]; i++) - node._counted = undefined; + node._countedByPrototype = undefined; return nodes; }, @@ -3044,15 +3023,15 @@ Object.extend(Selector, { // "ofType" flag indicates whether we're indexing for nth-of-type // rather than nth-child index: function(parentNode, reverse, ofType) { - parentNode._counted = true; + parentNode._countedByPrototype = Prototype.emptyFunction; if (reverse) { for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; } } else { for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._counted)) node.nodeIndex = j++; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; } }, @@ -3061,8 +3040,8 @@ Object.extend(Selector, { if (nodes.length == 0) return nodes; var results = [], n; for (var i = 0, l = nodes.length; i < l; i++) - if (!(n = nodes[i])._counted) { - n._counted = true; + if (!(n = nodes[i])._countedByPrototype) { + n._countedByPrototype = Prototype.emptyFunction; results.push(Element.extend(n)); } return Selector.handlers.unmark(results); @@ -3114,7 +3093,7 @@ Object.extend(Selector, { // TOKEN FUNCTIONS tagName: function(nodes, root, tagName, combinator) { - tagName = tagName.toUpperCase(); + var uTagName = tagName.toUpperCase(); var results = [], h = Selector.handlers; if (nodes) { if (combinator) { @@ -3127,7 +3106,7 @@ Object.extend(Selector, { if (tagName == "*") return nodes; } for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() == tagName) results.push(node); + if (node.tagName.toUpperCase() === uTagName) results.push(node); return results; } else return root.getElementsByTagName(tagName); }, @@ -3174,16 +3153,18 @@ Object.extend(Selector, { return results; }, - attrPresence: function(nodes, root, attr) { + attrPresence: function(nodes, root, attr, combinator) { if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); var results = []; for (var i = 0, node; node = nodes[i]; i++) if (Element.hasAttribute(node, attr)) results.push(node); return results; }, - attr: function(nodes, root, attr, value, operator) { + attr: function(nodes, root, attr, value, operator, combinator) { if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); var handler = Selector.operators[operator], results = []; for (var i = 0, node; node = nodes[i]; i++) { var nodeValue = Element.readAttribute(node, attr); @@ -3262,7 +3243,7 @@ Object.extend(Selector, { var h = Selector.handlers, results = [], indexed = [], m; h.mark(nodes); for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._counted) { + if (!node.parentNode._countedByPrototype) { h.index(node.parentNode, reverse, ofType); indexed.push(node.parentNode); } @@ -3300,7 +3281,7 @@ Object.extend(Selector, { var exclusions = new Selector(selector).findElements(root); h.mark(exclusions); for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._counted) results.push(node); + if (!node._countedByPrototype) results.push(node); h.unmark(exclusions); return results; }, @@ -3334,11 +3315,19 @@ Object.extend(Selector, { '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } }, + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + matchElements: function(elements, expression) { - var matches = new Selector(expression).findElements(), h = Selector.handlers; + var matches = $$(expression), h = Selector.handlers; h.mark(matches); for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._counted) results.push(element); + if (element._countedByPrototype) results.push(element); h.unmark(matches); return results; }, @@ -3351,11 +3340,7 @@ Object.extend(Selector, { }, findChildElements: function(element, expressions) { - var exprs = expressions.join(','); - expressions = []; - exprs.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); - }); + expressions = Selector.split(expressions.join(',')); var results = [], h = Selector.handlers; for (var i = 0, l = expressions.length, selector; i < l; i++) { selector = new Selector(expressions[i].strip()); @@ -3366,13 +3351,22 @@ Object.extend(Selector, { }); if (Prototype.Browser.IE) { - // IE returns comment nodes on getElementsByTagName("*"). - // Filter them out. - Selector.handlers.concat = function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - }; + Object.extend(Selector.handlers, { + // IE returns comment nodes on getElementsByTagName("*"). + // Filter them out. + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + }, + + // IE improperly serializes _countedByPrototype in (inner|outer)HTML. + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } + }); } function $$() { @@ -3850,9 +3844,9 @@ Object.extend(Event, (function() { var cache = Event.cache; function getEventID(element) { - if (element._eventID) return element._eventID; + if (element._prototypeEventID) return element._prototypeEventID[0]; arguments.callee.id = arguments.callee.id || 1; - return element._eventID = ++arguments.callee.id; + return element._prototypeEventID = [++arguments.callee.id]; } function getDOMEventName(eventName) { @@ -3880,7 +3874,7 @@ Object.extend(Event, (function() { return false; Event.extend(event); - handler.call(element, event) + handler.call(element, event); }; wrapper.handler = handler; @@ -3962,11 +3956,12 @@ Object.extend(Event, (function() { if (element == document && document.createEvent && !element.dispatchEvent) element = document.documentElement; + var event; if (document.createEvent) { - var event = document.createEvent("HTMLEvents"); + event = document.createEvent("HTMLEvents"); event.initEvent("dataavailable", true, true); } else { - var event = document.createEventObject(); + event = document.createEventObject(); event.eventType = "ondataavailable"; } @@ -3995,20 +3990,21 @@ Element.addMethods({ Object.extend(document, { fire: Element.Methods.fire.methodize(), observe: Element.Methods.observe.methodize(), - stopObserving: Element.Methods.stopObserving.methodize() + stopObserving: Element.Methods.stopObserving.methodize(), + loaded: false }); (function() { /* Support for the DOMContentLoaded event is based on work by Dan Webb, Matthias Miller, Dean Edwards and John Resig. */ - var timer, fired = false; + var timer; function fireContentLoadedEvent() { - if (fired) return; + if (document.loaded) return; if (timer) window.clearInterval(timer); document.fire("dom:loaded"); - fired = true; + document.loaded = true; } if (document.addEventListener) { -- cgit v1.2.3 From 1b4b1aa725a4f44c3473ae99b36d7cededba2bea Mon Sep 17 00:00:00 2001 From: Kevin Glowacz Date: Sat, 19 Jul 2008 15:08:53 -0500 Subject: Fixed index and auto index for nested fields_for [#327 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_view/helpers/form_helper.rb | 30 ++++-- actionpack/test/template/form_helper_test.rb | 116 +++++++++++++++++++++- 2 files changed, 136 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 4fa46d9ee3..ada6fa2ea8 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -528,10 +528,10 @@ module ActionView def initialize(object_name, method_name, template_object, object = nil) @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup - @template_object= template_object + @template_object = template_object @object = object - if @object_name.sub!(/\[\]$/,"") - if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param) + if @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]") + if (object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param) @auto_index = object.to_param else raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}" @@ -708,7 +708,7 @@ module ActionView end def sanitized_object_name - @sanitized_object_name ||= @object_name.gsub(/[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "") + @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "") end def sanitized_method_name @@ -726,6 +726,13 @@ module ActionView def initialize(object_name, object, template, options, proc) @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc @default_options = @options ? @options.slice(:index) : {} + if @object_name.to_s.match(/\[\]$/) + if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param) + @auto_index = object.to_param + else + raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}" + end + end end (field_helpers - %w(label check_box radio_button fields_for)).each do |selector| @@ -738,16 +745,25 @@ module ActionView end def fields_for(record_or_name_or_array, *args, &block) + if options.has_key?(:index) + index = "[#{options[:index]}]" + elsif defined?(@auto_index) + self.object_name = @object_name.to_s.sub(/\[\]$/,"") + index = "[#{@auto_index}]" + else + index = "" + end + case record_or_name_or_array when String, Symbol - name = "#{object_name}[#{record_or_name_or_array}]" + name = "#{object_name}#{index}[#{record_or_name_or_array}]" when Array object = record_or_name_or_array.last - name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" + name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" args.unshift(object) else object = record_or_name_or_array - name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" + name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]" args.unshift(object) end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 39649c3622..52e8bf376a 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -6,7 +6,7 @@ silence_warnings do alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast) alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) - alias_method :secret?, :secret + alias_method :secret?, :secret def new_record=(boolean) @new_record = boolean @@ -22,6 +22,7 @@ silence_warnings do attr_reader :post_id def save; @id = 1; @post_id = 1 end def new_record?; @id.nil? end + def to_param; @id; end def name @id.nil? ? 'new comment' : "comment ##{@id}" end @@ -30,7 +31,6 @@ end class Comment::Nested < Comment; end - class FormHelperTest < ActionView::TestCase tests ActionView::Helpers::FormHelper @@ -447,6 +447,117 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_nested_fields_for_with_nested_collections + form_for('post[]', @post) do |f| + concat f.text_field(:title) + f.fields_for('comment[]', @comment) do |c| + concat c.text_field(:name) + end + end + + expected = "
" + + "" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_index + form_for('post', @post, :index => 1) do |c| + concat c.text_field(:title) + c.fields_for('comment', @comment, :index => 1) do |r| + concat r.text_field(:name) + end + end + + expected = "
" + + "" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_index + form_for(:post, @post, :index => 1) do |f| + f.fields_for(:comment, @post) do |c| + concat c.text_field(:title) + end + end + + expected = "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_index_on_both + form_for(:post, @post, :index => 1) do |f| + f.fields_for(:comment, @post, :index => 5) do |c| + concat c.text_field(:title) + end + end + + expected = "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_auto_index + form_for("post[]", @post) do |f| + f.fields_for(:comment, @post) do |c| + concat c.text_field(:title) + end + end + + expected = "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_auto_index_on_both + form_for("post[]", @post) do |f| + f.fields_for("comment[]", @post) do |c| + concat c.text_field(:title) + end + end + + expected = "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + + def test_nested_fields_for_with_index_and_auto_index + form_for("post[]", @post) do |f| + f.fields_for(:comment, @post, :index => 5) do |c| + concat c.text_field(:title) + end + end + + form_for(:post, @post, :index => 1) do |f| + f.fields_for("comment[]", @post) do |c| + concat c.text_field(:title) + end + end + + expected = "
" + + "" + + "
" + + "
" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + def test_fields_for fields_for(:post, @post) do |f| concat f.text_field(:title) @@ -831,7 +942,6 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end - protected def comments_path(post) "/posts/#{post.id}/comments" -- cgit v1.2.3 From 55bfe6be52da1130abb1c148f10d8e9a11368ffd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 15:27:55 -0500 Subject: Ensure ActionView::PathSet::Path is not initialized with a precompiled path --- actionmailer/lib/action_mailer/base.rb | 3 ++- actionpack/lib/action_view/paths.rb | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index bf60e2f3d5..a43296461b 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -426,7 +426,8 @@ module ActionMailer #:nodoc: end def template_root=(root) - write_inheritable_attribute(:template_root, ActionView::PathSet::Path.new(root)) + root = ActionView::PathSet::Path.new(root) if root.is_a?(String) + write_inheritable_attribute(:template_root, root) end end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index c7a5df762f..78548d4aa2 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -28,6 +28,8 @@ module ActionView #:nodoc: delegate :to_s, :to_str, :inspect, :to => :path def initialize(path) + raise ArgumentError, "path already is a Path class" if path.is_a?(Path) + @path = path.freeze reload! end -- cgit v1.2.3 From 598b4546fd505efd98c4955f4881b4fd5b953588 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 15:43:39 -0500 Subject: Fixed spelling mistake in deprecation warning [#381 state:resolved] --- actionpack/lib/action_view/base.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index fe51af62e6..619a4270f8 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -172,13 +172,15 @@ module ActionView #:nodoc: end def self.cache_template_loading=(*args) - ActiveSupport::Deprecation.warn("config.action_view.cache_template_loading option has been deprecated and has no affect. " << - "Please remove it from your config files.", caller) + ActiveSupport::Deprecation.warn( + "config.action_view.cache_template_loading option has been deprecated" + + "and has no effect. Please remove it from your config files.", caller) end def self.cache_template_extensions=(*args) - ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no effect. " << - "Please remove it from your config files.", caller) + ActiveSupport::Deprecation.warn( + "config.action_view.cache_template_extensions option has been" + + "deprecated and has no effect. Please remove it from your config files.", caller) end # Specify whether RJS responses should be wrapped in a try/catch block -- cgit v1.2.3 From da85251f85420d3a906a1eb949b73bb286122ea9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 17:32:22 -0500 Subject: Refactor ActiveSupport::Dependencies injector so it would be possible to disable it --- activesupport/lib/active_support/dependencies.rb | 218 ++++++++++++----------- 1 file changed, 114 insertions(+), 104 deletions(-) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 2f3fa72bb4..e3d4f3d7eb 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,8 +1,3 @@ -require 'set' -require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/core_ext/load_error' -require 'active_support/core_ext/kernel' - module ActiveSupport #:nodoc: module Dependencies #:nodoc: extend self @@ -52,6 +47,119 @@ module ActiveSupport #:nodoc: mattr_accessor :constant_watch_stack self.constant_watch_stack = [] + # Module includes this module + module ModuleConstMissing #:nodoc: + def self.included(base) #:nodoc: + base.class_eval do + # Rename the original handler so we can chain it to the new one + alias_method :rails_original_const_missing, :const_missing + + # Use const_missing to autoload associations so we don't have to + # require_association when using single-table inheritance. + def const_missing(class_id) + ActiveSupport::Dependencies.load_missing_constant self, class_id + end + end + end + + def unloadable(const_desc = self) + super(const_desc) + end + end + + # Class includes this module + module ClassConstMissing #:nodoc: + def const_missing(const_name) + if [Object, Kernel].include?(self) || parent == self + super + else + begin + begin + Dependencies.load_missing_constant self, const_name + rescue NameError + parent.send :const_missing, const_name + end + rescue NameError => e + # Make sure that the name we are missing is the one that caused the error + parent_qualified_name = Dependencies.qualified_name_for parent, const_name + raise unless e.missing_name? parent_qualified_name + qualified_name = Dependencies.qualified_name_for self, const_name + raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) + end + end + end + end + + # Object includes this module + module Loadable #:nodoc: + def load(file, *extras) #:nodoc: + Dependencies.new_constants_in(Object) { super } + rescue Exception => exception # errors from loading file + exception.blame_file! file + raise + end + + def require(file, *extras) #:nodoc: + Dependencies.new_constants_in(Object) { super } + rescue Exception => exception # errors from required file + exception.blame_file! file + raise + end + + # Mark the given constant as unloadable. Unloadable constants are removed each + # time dependencies are cleared. + # + # Note that marking a constant for unloading need only be done once. Setup + # or init scripts may list each unloadable constant that may need unloading; + # each constant will be removed for every subsequent clear, as opposed to for + # the first clear. + # + # The provided constant descriptor may be a (non-anonymous) module or class, + # or a qualified constant name as a string or symbol. + # + # Returns true if the constant was not previously marked for unloading, false + # otherwise. + def unloadable(const_desc) + Dependencies.mark_for_unload const_desc + end + end + + # Exception file-blaming + module Blamable #:nodoc: + def blame_file!(file) + (@blamed_files ||= []).unshift file + end + + def blamed_files + @blamed_files ||= [] + end + + def describe_blame + return nil if blamed_files.empty? + "This error occurred while loading the following files:\n #{blamed_files.join "\n "}" + end + + def copy_blame!(exc) + @blamed_files = exc.blamed_files.clone + self + end + end + + def inject! + Object.instance_eval do + define_method(:require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) + define_method(:require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency) + define_method(:require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association) + + alias_method :load_without_new_constant_marking, :load + include Loadable + end + + Module.instance_eval { include ModuleConstMissing } + Class.instance_eval { include ClassConstMissing } + Exception.instance_eval { include Blamable } + end + def load? mechanism == :load end @@ -452,102 +560,4 @@ module ActiveSupport #:nodoc: end end -Object.instance_eval do - define_method(:require_or_load) { |file_name| ActiveSupport::Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) - define_method(:require_dependency) { |file_name| ActiveSupport::Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency) - define_method(:require_association) { |file_name| ActiveSupport::Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association) -end - -class Module #:nodoc: - # Rename the original handler so we can chain it to the new one - alias :rails_original_const_missing :const_missing - - # Use const_missing to autoload associations so we don't have to - # require_association when using single-table inheritance. - def const_missing(class_id) - ActiveSupport::Dependencies.load_missing_constant self, class_id - end - - def unloadable(const_desc = self) - super(const_desc) - end - -end - -class Class - def const_missing(const_name) - if [Object, Kernel].include?(self) || parent == self - super - else - begin - begin - ActiveSupport::Dependencies.load_missing_constant self, const_name - rescue NameError - parent.send :const_missing, const_name - end - rescue NameError => e - # Make sure that the name we are missing is the one that caused the error - parent_qualified_name = ActiveSupport::Dependencies.qualified_name_for parent, const_name - raise unless e.missing_name? parent_qualified_name - qualified_name = ActiveSupport::Dependencies.qualified_name_for self, const_name - raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) - end - end - end -end - -class Object - alias_method :load_without_new_constant_marking, :load - - def load(file, *extras) #:nodoc: - ActiveSupport::Dependencies.new_constants_in(Object) { super } - rescue Exception => exception # errors from loading file - exception.blame_file! file - raise - end - - def require(file, *extras) #:nodoc: - ActiveSupport::Dependencies.new_constants_in(Object) { super } - rescue Exception => exception # errors from required file - exception.blame_file! file - raise - end - - # Mark the given constant as unloadable. Unloadable constants are removed each - # time dependencies are cleared. - # - # Note that marking a constant for unloading need only be done once. Setup - # or init scripts may list each unloadable constant that may need unloading; - # each constant will be removed for every subsequent clear, as opposed to for - # the first clear. - # - # The provided constant descriptor may be a (non-anonymous) module or class, - # or a qualified constant name as a string or symbol. - # - # Returns true if the constant was not previously marked for unloading, false - # otherwise. - def unloadable(const_desc) - ActiveSupport::Dependencies.mark_for_unload const_desc - end -end - -# Add file-blaming to exceptions -class Exception #:nodoc: - def blame_file!(file) - (@blamed_files ||= []).unshift file - end - - def blamed_files - @blamed_files ||= [] - end - - def describe_blame - return nil if blamed_files.empty? - "This error occurred while loading the following files:\n #{blamed_files.join "\n "}" - end - - def copy_blame!(exc) - @blamed_files = exc.blamed_files.clone - self - end -end +ActiveSupport::Dependencies.inject! -- cgit v1.2.3 From 006cbb8fde3f20a684eabcfd11c53ae762cf8435 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 20 Jul 2008 21:23:18 +0200 Subject: Handle the case where 64bit time_t won't overflow. --- activesupport/test/core_ext/time_ext_test.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 17a0968c0e..14309f4e2d 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -528,7 +528,12 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005) assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0, 0) assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec - assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) + # This won't overflow on 64bit linux + expected_to_overflow = Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1) + unless expected_to_overflow.is_a?(Time) + assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), + DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) + end assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value end -- cgit v1.2.3 From 5bec5848b22527ee77c007565f7eea336e5c864f Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 20 Jul 2008 21:25:31 +0200 Subject: If it doesn't overflow, it won't have a .start method. So don't run the second assertion either --- activesupport/test/core_ext/time_ext_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 14309f4e2d..8740497b3d 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -533,8 +533,8 @@ class TimeExtCalculationsTest < Test::Unit::TestCase unless expected_to_overflow.is_a?(Time) assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) + assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value end - assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value end def test_utc_time -- cgit v1.2.3 From ff9f6fcc75526d9fd89be834982dec8624c909c5 Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Mon, 21 Jul 2008 12:57:15 -0500 Subject: Refactor DateHelper and improve test coverage [#665 state:resolved] Signed-off-by: Joshua Peek --- actionpack/lib/action_view/helpers/date_helper.rb | 298 +++++++++++----------- actionpack/test/template/date_helper_test.rb | 144 +++++++---- 2 files changed, 238 insertions(+), 204 deletions(-) diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 2cdb9a224e..c7a1d40ff2 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -3,14 +3,15 @@ require 'action_view/helpers/tag_helper' module ActionView module Helpers - # The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the select-type methods - # share a number of common options that are as follows: + # The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the + # select-type methods share a number of common options that are as follows: # - # * :prefix - overwrites the default prefix of "date" used for the select names. So specifying "birthday" would give - # birthday[month] instead of date[month] if passed to the select_month method. + # * :prefix - overwrites the default prefix of "date" used for the select names. So specifying "birthday" + # would give birthday[month] instead of date[month] if passed to the select_month method. # * :include_blank - set to true if it should be possible to set an empty date. - # * :discard_type - set to true if you want to discard the type part of the select name. If set to true, the select_month - # method would use simply "date" (which can be overwritten using :prefix) instead of "date[month]". + # * :discard_type - set to true if you want to discard the type part of the select name. If set to true, + # the select_month method would use simply "date" (which can be overwritten using :prefix) instead of + # "date[month]". module DateHelper include ActionView::Helpers::TagHelper DEFAULT_PREFIX = 'date' unless const_defined?('DEFAULT_PREFIX') @@ -67,7 +68,7 @@ module ActionView I18n.with_options :locale => options[:locale], :scope => :'datetime.distance_in_words' do |locale| case distance_in_minutes when 0..1 - return distance_in_minutes == 0 ? + return distance_in_minutes == 0 ? locale.t(:less_than_x_minutes, :count => 1) : locale.t(:x_minutes, :count => distance_in_minutes) unless include_seconds @@ -91,7 +92,7 @@ module ActionView else locale.t :over_x_years, :count => (distance_in_minutes / 525600).round end end - end + end # Like distance_of_time_in_words, but where to_time is fixed to Time.now. # @@ -107,15 +108,18 @@ module ActionView alias_method :distance_of_time_in_words_to_now, :time_ago_in_words - # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based attribute (identified by - # +method+) on an object assigned to the template (identified by +object+). It's possible to tailor the selects through the +options+ hash, - # which accepts all the keys that each of the individual select builders do (like :use_month_numbers for select_month) as well as a range of - # discard options. The discard options are :discard_year, :discard_month and :discard_day. Set to true, they'll - # drop the respective select. Discarding the month select will also automatically discard the day select. It's also possible to explicitly - # set the order of the tags using the :order option with an array of symbols :year, :month and :day in - # the desired order. Symbols may be omitted and the respective select is not included. + # Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based + # attribute (identified by +method+) on an object assigned to the template (identified by +object+). It's + # possible to tailor the selects through the +options+ hash, which accepts all the keys that each of the + # individual select builders do (like :use_month_numbers for select_month) as well as a range of discard + # options. The discard options are :discard_year, :discard_month and :discard_day. Set + # to true, they'll drop the respective select. Discarding the month select will also automatically discard the + # day select. It's also possible to explicitly set the order of the tags using the :order option with an + # array of symbols :year, :month and :day in the desired order. Symbols may be omitted + # and the respective select is not included. # - # Pass the :default option to set the default date. Use a Time object or a Hash of :year, :month, :day, :hour, :minute, and :second. + # Pass the :default option to set the default date. Use a Time object or a Hash of :year, + # :month, :day, :hour, :minute, and :second. # # Passing :disabled => true as part of the +options+ will make elements inaccessible for change. # @@ -133,7 +137,7 @@ module ActionView # # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute, # # with the year in the year drop down box starting at 1995, numbers used for months instead of words, - # # and without a day select box. + # # and without a day select box. # date_select("post", "written_on", :start_year => 1995, :use_month_numbers => true, # :discard_day => true, :include_blank => true) # @@ -155,8 +159,8 @@ module ActionView # # The selects are prepared for multi-parameter assignment to an Active Record object. # - # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that all month - # choices are valid. + # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that + # all month choices are valid. def date_select(object_name, method, options = {}, html_options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options) end @@ -180,12 +184,12 @@ module ActionView # # Creates a time select tag that, when POSTed, will be stored in the mail variable in the sent_at attribute # time_select("mail", "sent_at") # - # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in - # # the sunrise attribute. + # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in + # # the sunrise attribute. # time_select("post", "start_time", :include_seconds => true) # - # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the entry variables in - # # the submission_time attribute. + # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the entry variables in + # # the submission_time attribute. # time_select("entry", "submission_time", :include_seconds => true) # # # You can set the :minute_step to 15 which will give you: 00, 15, 30 and 45. @@ -193,14 +197,15 @@ module ActionView # # The selects are prepared for multi-parameter assignment to an Active Record object. # - # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that all month - # choices are valid. + # Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that + # all month choices are valid. def time_select(object_name, method, options = {}, html_options = {}) InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options) end - # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a specified datetime-based - # attribute (identified by +method+) on an object assigned to the template (identified by +object+). Examples: + # Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a + # specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified + # by +object+). Examples: # # If anything is passed in the html_options hash it will be applied to every select tag in the set. # @@ -208,16 +213,16 @@ module ActionView # # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on attribute # datetime_select("post", "written_on") # - # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the + # # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the # # post variable in the written_on attribute. # datetime_select("post", "written_on", :start_year => 1995) # - # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will be stored in the - # # trip variable in the departing attribute. + # # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will + # # be stored in the trip variable in the departing attribute. # datetime_select("trip", "departing", :default => 3.days.from_now) # - # # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable as the written_on - # # attribute. + # # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable + # # as the written_on attribute. # datetime_select("post", "written_on", :discard_type => true) # # The selects are prepared for multi-parameter assignment to an Active Record object. @@ -227,9 +232,10 @@ module ActionView # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the +datetime+. # It's also possible to explicitly set the order of the tags using the :order option with an array of - # symbols :year, :month and :day in the desired order. If you do not supply a Symbol, it - # will be appended onto the :order passed in. You can also add :date_separator and :time_separator - # keys to the +options+ to control visual display of the elements. + # symbols :year, :month and :day in the desired order. If you do not supply a Symbol, + # it will be appended onto the :order passed in. You can also add :date_separator, + # :datetime_separator and :time_separator keys to the +options+ to control visual display of + # the elements. # # If anything is passed in the html_options hash it will be applied to every select tag in the set. # @@ -250,7 +256,12 @@ module ActionView # # with a '/' between each date field. # select_datetime(my_date_time, :date_separator => '/') # - # # Generates a datetime select that discards the type of the field and defaults to the datetime in + # # Generates a datetime select that defaults to the datetime in my_date_time (four days after today) + # # with a date fields separated by '/', time fields separated by '' and the date and time fields + # # separated by a comma (','). + # select_datetime(my_date_time, :date_separator => '/', :time_separator => '', :datetime_separator => ',') + # + # # Generates a datetime select that discards the type of the field and defaults to the datetime in # # my_date_time (four days after today) # select_datetime(my_date_time, :discard_type => true) # @@ -261,7 +272,7 @@ module ActionView def select_datetime(datetime = Time.current, options = {}, html_options = {}) separator = options[:datetime_separator] || '' select_date(datetime, options, html_options) + separator + select_time(datetime, options, html_options) - end + end # Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+. # It's possible to explicitly set the order of the tags using the :order option with an array of @@ -283,27 +294,29 @@ module ActionView # # with the fields ordered year, month, day rather than month, day, year. # select_date(my_date, :order => [:year, :month, :day]) # - # # Generates a date select that discards the type of the field and defaults to the date in + # # Generates a date select that discards the type of the field and defaults to the date in # # my_date (six days after today) # select_date(my_date, :discard_type => true) # + # # Generates a date select that defaults to the date in my_date, + # # which has fields separated by '/' + # select_date(my_date, :date_separator => '/') + # # # Generates a date select that defaults to the datetime in my_date (six days after today) # # prefixed with 'payday' rather than 'date' # select_date(my_date, :prefix => 'payday') # def select_date(date = Date.current, options = {}, html_options = {}) - options[:order] ||= [] + options.reverse_merge!(:order => [], :date_separator => '') [:year, :month, :day].each { |o| options[:order].push(o) unless options[:order].include?(o) } - select_date = '' - options[:order].each do |o| - select_date << self.send("select_#{o}", date, options, html_options) - end - select_date + options[:order].inject([]) { |s, o| + s << self.send("select_#{o}", date, options, html_options) + }.join(options[:date_separator]) end # Returns a set of html select-tags (one for hour and minute) - # You can set :time_separator key to format the output, and + # You can set :time_separator key to format the output, and # the :include_seconds option to include an input for seconds. # # If anything is passed in the html_options hash it will be applied to every select tag in the set. @@ -318,7 +331,7 @@ module ActionView # select_time() # # # Generates a time select that defaults to the time in my_time, - # # which has fields separated by ':' + # # which has fields separated by ':' # select_time(my_time, :time_separator => ':') # # # Generates a time select that defaults to the time in my_time, @@ -331,7 +344,8 @@ module ActionView # def select_time(datetime = Time.current, options = {}, html_options = {}) separator = options[:time_separator] || '' - select_hour(datetime, options, html_options) + separator + select_minute(datetime, options, html_options) + (options[:include_seconds] ? separator + select_second(datetime, options, html_options) : '') + select_hour(datetime, options, html_options) + separator + select_minute(datetime, options, html_options) + + (options[:include_seconds] ? separator + select_second(datetime, options, html_options) : '') end # Returns a select tag with options for each of the seconds 0 through 59 with the current second selected. @@ -346,26 +360,16 @@ module ActionView # # # Generates a select field for seconds that defaults to the number given # select_second(33) - # + # # # Generates a select field for seconds that defaults to the seconds for the time in my_time # # that is named 'interval' rather than 'second' # select_second(my_time, :field_name => 'interval') # def select_second(datetime, options = {}, html_options = {}) val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.sec) : '' - if options[:use_hidden] - options[:include_seconds] ? hidden_html(options[:field_name] || 'second', val, options) : '' - else - second_options = [] - 0.upto(59) do |second| - second_options << ((val == second) ? - content_tag(:option, leading_zero_on_single_digits(second), :value => leading_zero_on_single_digits(second), :selected => "selected") : - content_tag(:option, leading_zero_on_single_digits(second), :value => leading_zero_on_single_digits(second)) - ) - second_options << "\n" - end - select_html(options[:field_name] || 'second', second_options.join, options, html_options) - end + options[:use_hidden] ? + (options[:include_seconds] ? _date_hidden_html(options[:field_name] || 'second', val, options) : '') : + _date_select_html(options[:field_name] || 'second', _date_build_options(val), options, html_options) end # Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected. @@ -381,26 +385,17 @@ module ActionView # # # Generates a select field for minutes that defaults to the number given # select_minute(14) - # + # # # Generates a select field for minutes that defaults to the minutes for the time in my_time # # that is named 'stride' rather than 'second' # select_minute(my_time, :field_name => 'stride') # def select_minute(datetime, options = {}, html_options = {}) val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.min) : '' - if options[:use_hidden] - hidden_html(options[:field_name] || 'minute', val, options) - else - minute_options = [] - 0.step(59, options[:minute_step] || 1) do |minute| - minute_options << ((val == minute) ? - content_tag(:option, leading_zero_on_single_digits(minute), :value => leading_zero_on_single_digits(minute), :selected => "selected") : - content_tag(:option, leading_zero_on_single_digits(minute), :value => leading_zero_on_single_digits(minute)) - ) - minute_options << "\n" - end - select_html(options[:field_name] || 'minute', minute_options.join, options, html_options) - end + options[:use_hidden] ? + _date_hidden_html(options[:field_name] || 'minute', val, options) : + _date_select_html(options[:field_name] || 'minute', + _date_build_options(val, :step => options[:minute_step]), options, html_options) end # Returns a select tag with options for each of the hours 0 through 23 with the current hour selected. @@ -415,26 +410,15 @@ module ActionView # # # Generates a select field for minutes that defaults to the number given # select_minute(14) - # + # # # Generates a select field for minutes that defaults to the minutes for the time in my_time # # that is named 'stride' rather than 'second' # select_minute(my_time, :field_name => 'stride') # def select_hour(datetime, options = {}, html_options = {}) val = datetime ? (datetime.kind_of?(Fixnum) ? datetime : datetime.hour) : '' - if options[:use_hidden] - hidden_html(options[:field_name] || 'hour', val, options) - else - hour_options = [] - 0.upto(23) do |hour| - hour_options << ((val == hour) ? - content_tag(:option, leading_zero_on_single_digits(hour), :value => leading_zero_on_single_digits(hour), :selected => "selected") : - content_tag(:option, leading_zero_on_single_digits(hour), :value => leading_zero_on_single_digits(hour)) - ) - hour_options << "\n" - end - select_html(options[:field_name] || 'hour', hour_options.join, options, html_options) - end + options[:use_hidden] ? _date_hidden_html(options[:field_name] || 'hour', val, options) : + _date_select_html(options[:field_name] || 'hour', _date_build_options(val, :end => 23), options, html_options) end # Returns a select tag with options for each of the days 1 through 31 with the current day selected. @@ -449,36 +433,27 @@ module ActionView # # # Generates a select field for days that defaults to the number given # select_day(5) - # + # # # Generates a select field for days that defaults to the day for the date in my_date # # that is named 'due' rather than 'day' # select_day(my_time, :field_name => 'due') # def select_day(date, options = {}, html_options = {}) val = date ? (date.kind_of?(Fixnum) ? date : date.day) : '' - if options[:use_hidden] - hidden_html(options[:field_name] || 'day', val, options) - else - day_options = [] - 1.upto(31) do |day| - day_options << ((val == day) ? - content_tag(:option, day, :value => day, :selected => "selected") : - content_tag(:option, day, :value => day) - ) - day_options << "\n" - end - select_html(options[:field_name] || 'day', day_options.join, options, html_options) - end + options[:use_hidden] ? _date_hidden_html(options[:field_name] || 'day', val, options) : + _date_select_html(options[:field_name] || 'day', + _date_build_options(val, :start => 1, :end => 31, :leading_zeros => false), + options, html_options) end - # Returns a select tag with options for each of the months January through December with the current month selected. - # The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are used as values - # (what's submitted to the server). It's also possible to use month numbers for the presentation instead of names -- - # set the :use_month_numbers key in +options+ to true for this to happen. If you want both numbers and names, - # set the :add_month_numbers key in +options+ to true. If you would prefer to show month names as abbreviations, - # set the :use_short_month key in +options+ to true. If you want to use your own month names, set the - # :use_month_names key in +options+ to an array of 12 month names. Override the field name using the - # :field_name option, 'month' by default. + # Returns a select tag with options for each of the months January through December with the current month + # selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are + # used as values (what's submitted to the server). It's also possible to use month numbers for the presentation + # instead of names -- set the :use_month_numbers key in +options+ to true for this to happen. If you + # want both numbers and names, set the :add_month_numbers key in +options+ to true. If you would prefer + # to show month names as abbreviations, set the :use_short_month key in +options+ to true. If you want + # to use your own month names, set the :use_month_names key in +options+ to an array of 12 month names. + # Override the field name using the :field_name option, 'month' by default. # # ==== Examples # # Generates a select field for months that defaults to the current month that @@ -490,7 +465,7 @@ module ActionView # select_month(Date.today, :field_name => 'start') # # # Generates a select field for months that defaults to the current month that - # # will use keys like "1", "3". + # # will use keys like "1", "3". # select_month(Date.today, :use_month_numbers => true) # # # Generates a select field for months that defaults to the current month that @@ -506,11 +481,11 @@ module ActionView # select_month(Date.today, :use_month_names => %w(Januar Februar Marts ...)) # def select_month(date, options = {}, html_options = {}) - locale = options[:locale] + locale = options[:locale] val = date ? (date.kind_of?(Fixnum) ? date : date.month) : '' if options[:use_hidden] - hidden_html(options[:field_name] || 'month', val, options) + _date_hidden_html(options[:field_name] || 'month', val, options) else month_options = [] month_names = options[:use_month_names] || begin @@ -534,14 +509,15 @@ module ActionView ) month_options << "\n" end - select_html(options[:field_name] || 'month', month_options.join, options, html_options) + _date_select_html(options[:field_name] || 'month', month_options.join, options, html_options) end - end + end - # Returns a select tag with options for each of the five years on each side of the current, which is selected. The five year radius - # can be changed using the :start_year and :end_year keys in the +options+. Both ascending and descending year - # lists are supported by making :start_year less than or greater than :end_year. The date can also be - # substituted for a year given as a number. Override the field name using the :field_name option, 'year' by default. + # Returns a select tag with options for each of the five years on each side of the current, which is selected. + # The five year radius can be changed using the :start_year and :end_year keys in the + # +options+. Both ascending and descending year lists are supported by making :start_year less than or + # greater than :end_year. The date can also be substituted for a year given as a number. + # Override the field name using the :field_name option, 'year' by default. # # ==== Examples # # Generates a select field for years that defaults to the current year that @@ -562,38 +538,48 @@ module ActionView # def select_year(date, options = {}, html_options = {}) if !date || date == 0 - value = '' + val = '' middle_year = Date.today.year elsif date.kind_of?(Fixnum) - value = middle_year = date + val = middle_year = date else - value = middle_year = date.year + val = middle_year = date.year end if options[:use_hidden] - hidden_html(options[:field_name] || 'year', value, options) + _date_hidden_html(options[:field_name] || 'year', val, options) else - year_options = '' - start_year = options[:start_year] || middle_year - 5 - end_year = options[:end_year] || middle_year + 5 - step_val = start_year < end_year ? 1 : -1 - - start_year.step(end_year, step_val) do |year| - if value == year - year_options << content_tag(:option, year, :value => year, :selected => "selected") - else - year_options << content_tag(:option, year, :value => year) - end - year_options << "\n" - end - select_html(options[:field_name] || 'year', year_options, options, html_options) + options[:start_year] ||= middle_year - 5 + options[:end_year] ||= middle_year + 5 + step = options[:start_year] < options[:end_year] ? 1 : -1 + + _date_select_html(options[:field_name] || 'year', + _date_build_options(val, + :start => options[:start_year], + :end => options[:end_year], + :step => step, + :leading_zeros => false + ), options, html_options) end end private + def _date_build_options(selected, options={}) + options.reverse_merge!(:start => 0, :end => 59, :step => 1, :leading_zeros => true) + + select_options = [] + (options[:start] || 0).step((options[:end] || 59), options[:step] || 1) do |i| + value = options[:leading_zeros] ? sprintf("%02d", i) : i + tag_options = { :value => value } + tag_options[:selected] = "selected" if selected == i + + select_options << content_tag(:option, value, tag_options) + end + select_options.join("\n") + "\n" + end - def select_html(type, html_options, options, select_tag_options = {}) - name_and_id_from_options(options, type) + def _date_select_html(type, html_options, options, select_tag_options = {}) + _date_name_and_id_from_options(options, type) select_options = {:id => options[:id], :name => options[:name]} select_options.merge!(:disabled => 'disabled') if options[:disabled] select_options.merge!(select_tag_options) unless select_tag_options.empty? @@ -603,19 +589,15 @@ module ActionView content_tag(:select, select_html, select_options) + "\n" end - def hidden_html(type, value, options) - name_and_id_from_options(options, type) + def _date_hidden_html(type, value, options) + _date_name_and_id_from_options(options, type) hidden_html = tag(:input, :type => "hidden", :id => options[:id], :name => options[:name], :value => value) + "\n" end - def name_and_id_from_options(options, type) + def _date_name_and_id_from_options(options, type) options[:name] = (options[:prefix] || DEFAULT_PREFIX) + (options[:discard_type] ? '' : "[#{type}]") options[:id] = options[:name].gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '') end - - def leading_zero_on_single_digits(number) - number > 9 ? number : "0#{number}" - end end class InstanceTag #:nodoc: @@ -641,11 +623,11 @@ module ActionView options = defaults.merge(options) datetime = value(object) datetime ||= default_time_from_options(options[:default]) unless options[:include_blank] - + position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 } order = options[:order] ||= I18n.translate(:'date.order', :locale => locale) - + # Discard explicit and implicit by not being included in the :order discard = {} discard[:year] = true if options[:discard_year] or !order.include?(:year) @@ -654,26 +636,30 @@ module ActionView discard[:hour] = true if options[:discard_hour] discard[:minute] = true if options[:discard_minute] or discard[:hour] discard[:second] = true unless options[:include_seconds] && !discard[:minute] - + # If the day is hidden and the month is visible, the day should be set to the 1st so all month choices are valid # (otherwise it could be 31 and february wouldn't be a valid date) if datetime && discard[:day] && !discard[:month] datetime = datetime.change(:day => 1) end - + # Maintain valid dates by including hidden fields for discarded elements [:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) } - + # Ensure proper ordering of :hour, :minute and :second [:hour, :minute, :second].each { |o| order.delete(o); order.push(o) } - + date_or_time_select = '' order.reverse.each do |param| # Send hidden fields for discarded elements once output has started # This ensures AR can reconstruct valid dates using ParseDate next if discard[param] && (date_or_time_select.empty? || options[:ignore_date]) - date_or_time_select.insert(0, self.send("select_#{param}", datetime, options_with_prefix(position[param], options.merge(:use_hidden => discard[param])), html_options)) + date_or_time_select.insert(0, + self.send("select_#{param}", + datetime, + options_with_prefix(position[param], options.merge(:use_hidden => discard[param])), + html_options)) date_or_time_select.insert(0, case param when :hour then (discard[:year] && discard[:day] ? "" : " — ") @@ -682,7 +668,7 @@ module ActionView else "" end) end - + date_or_time_select end @@ -708,7 +694,7 @@ module ActionView default[:sec] ||= default[:second] time = Time.current - + [:year, :month, :day, :hour, :min, :sec].each do |key| default[key] ||= time.send(key) end diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 8b4e94c67f..d8c07e731b 100755 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -17,7 +17,7 @@ class DateHelperTest < ActionView::TestCase end end end - + def assert_distance_of_time_in_words(from, to=nil) to ||= from @@ -86,13 +86,13 @@ class DateHelperTest < ActionView::TestCase from = Time.mktime(2004, 6, 6, 21, 45, 0) assert_distance_of_time_in_words(from) end - + def test_distance_in_words_with_time_zones from = Time.mktime(2004, 6, 6, 21, 45, 0) assert_distance_of_time_in_words(from.in_time_zone('Alaska')) assert_distance_of_time_in_words(from.in_time_zone('Hawaii')) end - + def test_distance_in_words_with_different_time_zones from = Time.mktime(2004, 6, 6, 21, 45, 0) assert_distance_of_time_in_words( @@ -100,13 +100,13 @@ class DateHelperTest < ActionView::TestCase from.in_time_zone('Hawaii') ) end - + def test_distance_in_words_with_dates start_date = Date.new 1975, 1, 31 end_date = Date.new 1977, 1, 31 assert_equal("over 2 years", distance_of_time_in_words(start_date, end_date)) end - + def test_distance_in_words_with_integers assert_equal "less than a minute", distance_of_time_in_words(59) assert_equal "about 1 hour", distance_of_time_in_words(60*60) @@ -757,6 +757,26 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), {:start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => "selector") end + def test_select_date_with_separator + expected = %(\n" + + expected << " / " + + expected << %(\n" + + expected << " / " + + expected << %(\n" + + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { :date_separator => " / ", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"}) + end + def test_select_datetime expected = %(\n) + expected << %(\n\n\n) + expected << "\n" + + expected << "/" + + expected << %(\n" + + expected << "/" + + expected << %(\n" + + expected << "—" + + expected << %(\n" + + expected << ":" + + expected << %(\n" + + assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { :datetime_separator => "—", :date_separator => "/", :time_separator => ":", :start_year => 2003, :end_year => 2005, :prefix => "date[first]"}, :class => 'selector') + end + def test_select_time expected = %(\n} expected << %(\n" expected << " : " expected << %(\n" assert_dom_equal expected, time_select("post", "written_on") @@ -1203,11 +1255,11 @@ class DateHelperTest < ActionView::TestCase @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) expected = %(\n" expected << " : " expected << %(\n" assert_dom_equal expected, time_select("post", "written_on", :ignore_date => true) @@ -1222,15 +1274,15 @@ class DateHelperTest < ActionView::TestCase expected << %{\n} expected << %(\n" expected << " : " expected << %(\n" expected << " : " expected << %(\n" assert_dom_equal expected, time_select("post", "written_on", :include_seconds => true) @@ -1245,11 +1297,11 @@ class DateHelperTest < ActionView::TestCase expected << %{\n} expected << %(\n" expected << " : " expected << %(\n" assert_dom_equal expected, time_select("post", "written_on", {}, :class => 'selector') @@ -1268,11 +1320,11 @@ class DateHelperTest < ActionView::TestCase expected << %{\n} expected << %(\n" expected << " : " expected << %(\n" assert_dom_equal expected, output_buffer @@ -1306,7 +1358,7 @@ class DateHelperTest < ActionView::TestCase assert_dom_equal expected, datetime_select("post", "updated_at") end - + uses_mocha 'TestDatetimeSelectDefaultsToTimeZoneNowWhenConfigTimeZoneIsSet' do def test_datetime_select_defaults_to_time_zone_now_when_config_time_zone_is_set time = stub(:year => 2004, :month => 6, :day => 15, :hour => 16, :min => 35, :sec => 0) @@ -1370,8 +1422,7 @@ class DateHelperTest < ActionView::TestCase expected << "\n" expected << %(\n" assert_dom_equal expected, select_date(0, :end_year => Date.today.year+1, :prefix => "date[first]") @@ -1388,8 +1439,7 @@ class DateHelperTest < ActionView::TestCase expected << "\n" expected << %(\n" assert_dom_equal expected, select_date(0, :start_year => 2003, :prefix => "date[first]") @@ -1405,8 +1455,7 @@ class DateHelperTest < ActionView::TestCase expected << "\n" expected << %(\n" assert_dom_equal expected, select_date(0, :prefix => "date[first]") @@ -1422,8 +1471,7 @@ class DateHelperTest < ActionView::TestCase expected << "\n" expected << %(\n" assert_dom_equal expected, select_date(nil, :prefix => "date[first]") @@ -1530,15 +1578,15 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :include_seconds => true) @@ -1559,11 +1607,11 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true) @@ -1582,11 +1630,11 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :discard_month => true) @@ -1601,11 +1649,11 @@ class DateHelperTest < ActionView::TestCase expected << %{\n} expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :discard_year => true, :discard_month => true) @@ -1628,11 +1676,11 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :order => [:minute, :day, :hour, :month, :year, :second]) @@ -1653,11 +1701,11 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :order => [:day, :month]) @@ -1680,11 +1728,11 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :default => Time.local(2006, 9, 19, 15, 16, 35)) @@ -1727,11 +1775,11 @@ class DateHelperTest < ActionView::TestCase expected << " — " expected << %{\n" expected << " : " expected << %{\n" assert_dom_equal expected, datetime_select("post", "updated_at", :default => { :month => 10, :minute => 42, :hour => 9 }) @@ -1780,19 +1828,19 @@ class DateHelperTest < ActionView::TestCase assert_equal 2, dummy_instance_tag.send!(:default_time_from_options, :hour => 2).hour end end - + def test_instance_tag_default_time_from_options_handles_far_future_date dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3) time = dummy_instance_tag.send!(:default_time_from_options, :year => 2050, :month => 2, :day => 10, :hour => 15, :min => 30, :sec => 45) assert_equal 2050, time.year end end - + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz yield ensure old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end + end end -- cgit v1.2.3 From 0f43de644ea48c1ad11d4bc73307af066bb52870 Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Mon, 21 Jul 2008 13:05:27 -0500 Subject: Refactored NumberHelper API to accept arguments as an options hash [#666 state:resolved] Signed-off-by: Joshua Peek --- .../lib/action_view/helpers/number_helper.rb | 98 +++++++++++++++------- actionpack/test/template/number_helper_test.rb | 28 ++++++- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 6bb8263794..c4ba7ccc8e 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -22,7 +22,7 @@ module ActionView # number_to_phone(1235551234, :country_code => 1) # => +1-123-555-1234 # # number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".") - # => +1.123.555.1234 x 1343 + # => +1.123.555.1234 x 1343 def number_to_phone(number, options = {}) number = number.to_s.strip unless number.nil? options = options.stringify_keys @@ -71,14 +71,14 @@ module ActionView def number_to_currency(number, options = {}) options = options.symbolize_keys defaults = I18n.translate(:'currency.format', :locale => options[:locale]) || {} - + precision = options[:precision] || defaults[:precision] unit = options[:unit] || defaults[:unit] separator = options[:separator] || defaults[:separator] delimiter = options[:delimiter] || defaults[:delimiter] format = options[:format] || defaults[:format] separator = '' if precision == 0 - + begin parts = number_with_precision(number, precision).split('.') format.gsub(/%n/, number_with_delimiter(parts[0], delimiter) + separator + parts[1].to_s).gsub(/%u/, unit) @@ -118,49 +118,72 @@ module ActionView end end - # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You - # can customize the format using optional delimiter and separator parameters. + # Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can + # customize the format in the +options+ hash. # # ==== Options - # * delimiter - Sets the thousands delimiter (defaults to ","). - # * separator - Sets the separator between the units (defaults to "."). + # * :delimiter - Sets the thousands delimiter (defaults to ","). + # * :separator - Sets the separator between the units (defaults to "."). # # ==== Examples - # number_with_delimiter(12345678) # => 12,345,678 - # number_with_delimiter(12345678.05) # => 12,345,678.05 - # number_with_delimiter(12345678, ".") # => 12.345.678 - # - # number_with_delimiter(98765432.98, " ", ",") + # number_with_delimiter(12345678) # => 12,345,678 + # number_with_delimiter(12345678.05) # => 12,345,678.05 + # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678 + # number_with_delimiter(12345678, :seperator => ",") # => 12,345,678 + # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",") # # => 98 765 432,98 - def number_with_delimiter(number, delimiter=",", separator=".") + # + # You can still use number_with_delimiter with the old API that accepts the + # +delimiter+ as its optional second and the +separator+ as its + # optional third parameter: + # number_with_delimiter(12345678, " ") # => 12 345.678 + # number_with_delimiter(12345678.05, ".", ",") # => 12.345.678,05 + def number_with_delimiter(number, *args) + options = args.extract_options! + unless args.empty? + options[:delimiter] = args[0] || "," + options[:separator] = args[1] || "." + end + options.reverse_merge!(:delimiter => ",", :separator => ".") + begin parts = number.to_s.split('.') - parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}") - parts.join separator + parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}") + parts.join options[:separator] rescue number end end - # Formats a +number+ with the specified level of +precision+ (e.g., 112.32 has a precision of 2). The default - # level of precision is 3. + # Formats a +number+ with the specified level of :precision (e.g., 112.32 has a precision of 2). + # The default level of precision is 3. # # ==== Examples - # number_with_precision(111.2345) # => 111.235 - # number_with_precision(111.2345, 2) # => 111.23 - # number_with_precision(13, 5) # => 13.00000 - # number_with_precision(389.32314, 0) # => 389 - def number_with_precision(number, precision=3) - "%01.#{precision}f" % ((Float(number) * (10 ** precision)).round.to_f / 10 ** precision) + # number_with_precision(111.2345) # => 111.235 + # number_with_precision(111.2345, :precision => 2) # => 111.23 + # number_with_precision(13, :precision => 5) # => 13.00000 + # number_with_precision(389.32314, :precision => 0) # => 389 + # + # You can still use number_with_precision with the old API that accepts the + # +precision+ as its optional second parameter: + # number_with_precision(number_with_precision(111.2345, 2) # => 111.23 + def number_with_precision(number, *args) + options = args.extract_options! + unless args.empty? + options[:precision] = args[0] || 3 + end + options.reverse_merge!(:precision => 3) + "%01.#{options[:precision]}f" % + ((Float(number) * (10 ** options[:precision])).round.to_f / 10 ** options[:precision]) rescue number end # Formats the bytes in +size+ into a more understandable representation - # (e.g., giving it 1500 yields 1.5 KB). This method is useful for + # (e.g., giving it 1500 yields 1.5 KB). This method is useful for # reporting file sizes to users. This method returns nil if # +size+ cannot be converted into a number. You can change the default - # precision of 1 using the precision parameter +precision+. + # precision of 1 using the precision parameter :precision. # # ==== Examples # number_to_human_size(123) # => 123 Bytes @@ -169,17 +192,28 @@ module ActionView # number_to_human_size(1234567) # => 1.2 MB # number_to_human_size(1234567890) # => 1.1 GB # number_to_human_size(1234567890123) # => 1.1 TB + # number_to_human_size(1234567, :precision => 2) # => 1.18 MB + # number_to_human_size(483989, :precision => 0) # => 473 KB + # + # You can still use number_to_human_size with the old API that accepts the + # +precision+ as its optional second parameter: # number_to_human_size(1234567, 2) # => 1.18 MB - # number_to_human_size(483989, 0) # => 4 MB - def number_to_human_size(size, precision=1) - size = Kernel.Float(size) + # number_to_human_size(483989, 0) # => 473 KB + def number_to_human_size(size, *args) + options = args.extract_options! + unless args.empty? + options[:precision] = args[0] || 1 + end + options.reverse_merge!(:precision => 1) + + size = Float(size) case when size.to_i == 1; "1 Byte" when size < 1.kilobyte; "%d Bytes" % size - when size < 1.megabyte; "%.#{precision}f KB" % (size / 1.0.kilobyte) - when size < 1.gigabyte; "%.#{precision}f MB" % (size / 1.0.megabyte) - when size < 1.terabyte; "%.#{precision}f GB" % (size / 1.0.gigabyte) - else "%.#{precision}f TB" % (size / 1.0.terabyte) + when size < 1.megabyte; "%.#{options[:precision]}f KB" % (size / 1.0.kilobyte) + when size < 1.gigabyte; "%.#{options[:precision]}f MB" % (size / 1.0.megabyte) + when size < 1.terabyte; "%.#{options[:precision]}f GB" % (size / 1.0.gigabyte) + else "%.#{options[:precision]}f TB" % (size / 1.0.terabyte) end.sub(/([0-9]\.\d*?)0+ /, '\1 ' ).sub(/\. /,' ') rescue nil diff --git a/actionpack/test/template/number_helper_test.rb b/actionpack/test/template/number_helper_test.rb index 4a8d09b544..bff349a754 100644 --- a/actionpack/test/template/number_helper_test.rb +++ b/actionpack/test/template/number_helper_test.rb @@ -54,9 +54,16 @@ class NumberHelperTest < ActionView::TestCase assert_nil number_with_delimiter(nil) end + def test_number_with_delimiter_with_options_hash + assert_equal '12 345 678', number_with_delimiter(12345678, :delimiter => ' ') + assert_equal '12,345,678-05', number_with_delimiter(12345678.05, :separator => '-') + assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :separator => ',', :delimiter => '.') + assert_equal '12.345.678,05', number_with_delimiter(12345678.05, :delimiter => '.', :separator => ',') + end + def test_number_with_precision assert_equal("111.235", number_with_precision(111.2346)) - assert_equal("31.83", number_with_precision(31.825, 2)) + assert_equal("31.83", number_with_precision(31.825, 2)) assert_equal("111.23", number_with_precision(111.2346, 2)) assert_equal("111.00", number_with_precision(111, 2)) assert_equal("111.235", number_with_precision("111.2346")) @@ -69,6 +76,17 @@ class NumberHelperTest < ActionView::TestCase assert_nil number_with_precision(nil) end + def test_number_with_precision_with_options_hash + assert_equal '111.235', number_with_precision(111.2346) + assert_equal '31.83', number_with_precision(31.825, :precision => 2) + assert_equal '111.23', number_with_precision(111.2346, :precision => 2) + assert_equal '111.00', number_with_precision(111, :precision => 2) + assert_equal '111.235', number_with_precision("111.2346") + assert_equal '31.83', number_with_precision("31.825", :precision => 2) + assert_equal '112', number_with_precision(111.50, :precision => 0) + assert_equal '1234567892', number_with_precision(1234567891.50, :precision => 0) + end + def test_number_to_human_size assert_equal '0 Bytes', number_to_human_size(0) assert_equal '1 Byte', number_to_human_size(1) @@ -94,4 +112,12 @@ class NumberHelperTest < ActionView::TestCase assert_nil number_to_human_size('x') assert_nil number_to_human_size(nil) end + + def test_number_to_human_size_with_options_hash + assert_equal '1.18 MB', number_to_human_size(1234567, :precision => 2) + assert_equal '3 Bytes', number_to_human_size(3.14159265, :precision => 4) + assert_equal '1.01 KB', number_to_human_size(1.0123.kilobytes, :precision => 2) + assert_equal '1.01 KB', number_to_human_size(1.0100.kilobytes, :precision => 4) + assert_equal '10 KB', number_to_human_size(10.000.kilobytes, :precision => 4) + end end -- cgit v1.2.3 From 3bd34b6ffe017dd81fd26743aab052fc4324eb0f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 14 Jun 2008 16:24:23 -0500 Subject: Preload application classes. Uses same strategy as phusion passenger. --- railties/lib/initializer.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index b8b071d4c9..3be95de8d3 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -168,6 +168,9 @@ module Rails # Observers are loaded after plugins in case Observers or observed models are modified by plugins. load_observers + # load application classes + load_application_classes + # Flag initialized Rails.initialized = true end @@ -330,6 +333,14 @@ Run `rake gems:install` to install the missing gems. end end + def load_application_classes + require_dependency 'application' + + Dir.glob('app/{models,controllers,helpers}/*.rb').each do |file| + require_dependency file + end + end + # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the # multibyte safe operations. Plugin authors supporting other encodings # should override this behaviour and set the relevant +default_charset+ -- cgit v1.2.3 From 89ec72c2818a592323fe4ec3277638d379f1ac2a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Jul 2008 13:42:34 -0500 Subject: Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers --- railties/CHANGELOG | 2 ++ railties/lib/initializer.rb | 25 +++++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 9c5e5b59c6..5ff1867568 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Added configurable eager load paths. Defaults to app/models, app/controllers, and app/helpers [Josh Peek] + * Introduce simple internationalization support. [Ruby i18n team] * Make script/plugin install -r option work with git based plugins. #257. [Tim Pope Jakub Kuźma]. Example: diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 3be95de8d3..828d688475 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -333,11 +333,14 @@ Run `rake gems:install` to install the missing gems. end end + # Eager load application classes def load_application_classes - require_dependency 'application' - - Dir.glob('app/{models,controllers,helpers}/*.rb').each do |file| - require_dependency file + if configuration.cache_classes + configuration.eager_load_paths.each do |load_path| + Dir.glob("#{load_path}/*.rb").each do |file| + require_dependency file + end + end end end @@ -578,6 +581,11 @@ Run `rake gems:install` to install the missing gems. # All elements of this array must also be in +load_paths+. attr_accessor :load_once_paths + # An array of paths from which Rails will eager load on boot if cache + # classes is enabled. All elements of this array must also be in + # +load_paths+. + attr_accessor :eager_load_paths + # The log level to use for the default Rails logger. In production mode, # this defaults to :info. In development mode, it defaults to # :debug. @@ -686,6 +694,7 @@ Run `rake gems:install` to install the missing gems. self.frameworks = default_frameworks self.load_paths = default_load_paths self.load_once_paths = default_load_once_paths + self.eager_load_paths = default_eager_load_paths self.log_path = default_log_path self.log_level = default_log_level self.view_path = default_view_path @@ -826,6 +835,14 @@ Run `rake gems:install` to install the missing gems. [] end + def default_eager_load_paths + %w( + app/models + app/controllers + app/helpers + ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) } + end + def default_log_path File.join(root_path, 'log', "#{environment}.log") end -- cgit v1.2.3 From c67713a2fe78d6f2db49b09771841f5022995703 Mon Sep 17 00:00:00 2001 From: Daniel Guettler Date: Mon, 21 Jul 2008 15:21:13 -0400 Subject: Use klass.sti_name to make sure associations take store_full_sti_class into account. [#671 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/associations.rb | 4 ++-- .../cases/associations/has_many_associations_test.rb | 18 ++++++++++++++++++ activerecord/test/models/company.rb | 7 +++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index fd9a443eb9..d916275ab9 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1884,7 +1884,7 @@ module ActiveRecord jt_sti_extra = " AND %s.%s = %s" % [ connection.quote_table_name(aliased_join_table_name), connection.quote_column_name(through_reflection.active_record.inheritance_column), - through_reflection.klass.quote_value(through_reflection.klass.name.demodulize)] + through_reflection.klass.quote_value(through_reflection.klass.sti_name)] end when :belongs_to first_key = primary_key @@ -1952,7 +1952,7 @@ module ActiveRecord join << %(AND %s.%s = %s ) % [ connection.quote_table_name(aliased_table_name), connection.quote_column_name(klass.inheritance_column), - klass.quote_value(klass.name.demodulize)] unless klass.descends_from_active_record? + klass.quote_value(klass.sti_name)] unless klass.descends_from_active_record? [through_reflection, reflection].each do |ref| join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions]))} " if ref && ref.options[:conditions] diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index b9c7ec6377..f8b8b1f96d 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -999,4 +999,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert firm.clients.loaded? end + def test_joins_with_namespaced_model_should_use_correct_type + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + + firm = Namespaced::Firm.create({ :name => 'Some Company' }) + firm.clients.create({ :name => 'Some Client' }) + + stats = Namespaced::Firm.find(firm.id, { + :select => "#{Namespaced::Firm.table_name}.*, COUNT(#{Namespaced::Client.table_name}.id) AS num_clients", + :joins => :clients, + :group => "#{Namespaced::Firm.table_name}.id" + }) + assert_equal 1, stats.num_clients.to_i + + ensure + ActiveRecord::Base.store_full_sti_class = old + end + end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index e6aa810146..cd435948a1 100755 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -18,6 +18,13 @@ end module Namespaced class Company < ::Company end + + class Firm < ::Company + has_many :clients, :class_name => 'Namespaced::Client' + end + + class Client < ::Company + end end class Firm < Company -- cgit v1.2.3 From 92f944818eece9fe4bc62ffb39accdb71ebc32be Mon Sep 17 00:00:00 2001 From: Miles Georgi Date: Sat, 19 Jul 2008 16:04:35 -0700 Subject: Make script/plugin work with svn+ssh urls. [#662 state:resolved] Signed-off-by: Pratik Naik --- railties/lib/commands/plugin.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/commands/plugin.rb b/railties/lib/commands/plugin.rb index 0256090d16..980244a71b 100644 --- a/railties/lib/commands/plugin.rb +++ b/railties/lib/commands/plugin.rb @@ -907,7 +907,7 @@ class RecursiveHTTPFetcher def ls @urls_to_fetch.collect do |url| - if url =~ /^svn:\/\/.*/ + if url =~ /^svn(\+ssh)?:\/\/.*/ `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil else open(url) do |stream| -- cgit v1.2.3 From 8b858782fa693e89a47fc3dd5ae38d842ede6d04 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 21 Jul 2008 22:41:38 -0500 Subject: Ensure adapater specific code is loaded on ActiveRecord::Base.establish_connection --- .../connection_adapters/abstract/connection_specification.rb | 1 + 1 file changed, 1 insertion(+) 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 2a8807fb78..07b122efd1 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -211,6 +211,7 @@ module ActiveRecord clear_active_connection_name @active_connection_name = name @@defined_connections[name] = spec + connection when Symbol, String if configuration = configurations[spec.to_s] establish_connection(configuration) -- cgit v1.2.3 From 8a87d8a6c2c6dfb423bcaf61c750010d80993b16 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Jul 2008 10:26:44 -0500 Subject: Improved Memoizable test coverage and added support for multiple arguments --- .../active_support/core_ext/object/metaclass.rb | 5 + activesupport/lib/active_support/memoizable.rb | 55 ++++--- activesupport/test/memoizable_test.rb | 166 ++++++++++++++++----- 3 files changed, 165 insertions(+), 61 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/object/metaclass.rb b/activesupport/lib/active_support/core_ext/object/metaclass.rb index 169a76dfb7..93fb0ad594 100644 --- a/activesupport/lib/active_support/core_ext/object/metaclass.rb +++ b/activesupport/lib/active_support/core_ext/object/metaclass.rb @@ -5,4 +5,9 @@ class Object self end end + + # If class_eval is called on an object, add those methods to its metaclass + def class_eval(*args, &block) + metaclass.class_eval(*args, &block) + end end diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index f7cd73d39c..21636b8af4 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,32 +1,43 @@ module ActiveSupport - module Memoizable #:nodoc: + module Memoizable + module Freezable + def self.included(base) + base.class_eval do + unless base.method_defined?(:freeze_without_memoizable) + alias_method_chain :freeze, :memoizable + end + end + end + + def freeze_with_memoizable + methods.each do |method| + if m = method.to_s.match(/^_unmemoized_(.*)/) + send(m[1]) + end + end + freeze_without_memoizable + end + end + def memoize(*symbols) symbols.each do |symbol| - original_method = "unmemoized_#{symbol}" - memoized_ivar = "@#{symbol}" + original_method = "_unmemoized_#{symbol}" + memoized_ivar = "@_memoized_#{symbol}" - klass = respond_to?(:class_eval) ? self : self.metaclass - raise "Already memoized #{symbol}" if klass.instance_methods.map(&:to_s).include?(original_method) + class_eval <<-EOS, __FILE__, __LINE__ + include Freezable - klass.class_eval <<-EOS, __FILE__, __LINE__ - unless instance_methods.map(&:to_s).include?("freeze_without_memoizable") - alias_method :freeze_without_memoizable, :freeze - def freeze - methods.each do |method| - if m = method.to_s.match(/^unmemoized_(.*)/) - send(m[1]) - end - end - freeze_without_memoizable - end - end + raise "Already memoized #{symbol}" if method_defined?(:#{original_method}) + alias #{original_method} #{symbol} + + def #{symbol}(*args) + #{memoized_ivar} ||= {} + reload = args.pop if args.last == true || args.last == :reload - alias_method :#{original_method}, :#{symbol} - def #{symbol}(reload = false) - if !reload && defined? #{memoized_ivar} - #{memoized_ivar} + if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args) + #{memoized_ivar}[args] else - #{memoized_ivar} = #{original_method}.freeze + #{memoized_ivar}[args] = #{original_method}(*args).freeze end end EOS diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 79769631ad..cd84dcda53 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -5,86 +5,174 @@ uses_mocha 'Memoizable' do class Person extend ActiveSupport::Memoizable - def name - fetch_name_from_floppy + attr_reader :name_calls, :age_calls + def initialize + @name_calls = 0 + @age_calls = 0 end - memoize :name + def name + @name_calls += 1 + "Josh" + end def age + @age_calls += 1 nil end - def counter - @counter ||= 0 - @counter += 1 + memoize :name, :age + end + + class Company + attr_reader :name_calls + def initialize + @name_calls = 0 end - memoize :age, :counter + def name + @name_calls += 1 + "37signals" + end + end + + module Rates + extend ActiveSupport::Memoizable - private - def fetch_name_from_floppy - "Josh" + attr_reader :sales_tax_calls + def sales_tax(price) + @sales_tax_calls ||= 0 + @sales_tax_calls += 1 + price * 0.1025 + end + memoize :sales_tax + end + + class Calculator + extend ActiveSupport::Memoizable + include Rates + + attr_reader :fib_calls + def initialize + @fib_calls = 0 + end + + def fib(n) + @fib_calls += 1 + + if n == 0 || n == 1 + n + else + fib(n - 1) + fib(n - 2) end + end + memoize :fib + + def counter + @count ||= 0 + @count += 1 + end + memoize :counter end def setup @person = Person.new + @calculator = Calculator.new end def test_memoization assert_equal "Josh", @person.name + assert_equal 1, @person.name_calls - @person.expects(:fetch_name_from_floppy).never - 2.times { assert_equal "Josh", @person.name } + 3.times { assert_equal "Josh", @person.name } + assert_equal 1, @person.name_calls + end + + def test_memoization_with_nil_value + assert_equal nil, @person.age + assert_equal 1, @person.age_calls + + 3.times { assert_equal nil, @person.age } + assert_equal 1, @person.age_calls end def test_reloadable - counter = @person.counter - assert_equal 1, @person.counter - assert_equal 2, @person.counter(:reload) + counter = @calculator.counter + assert_equal 1, @calculator.counter + assert_equal 2, @calculator.counter(:reload) + assert_equal 2, @calculator.counter + assert_equal 3, @calculator.counter(true) + assert_equal 3, @calculator.counter end - def test_memoized_methods_are_frozen - assert_equal true, @person.name.frozen? + def test_memoization_cache_is_different_for_each_instance + assert_equal 1, @calculator.counter + assert_equal 2, @calculator.counter(:reload) + assert_equal 1, Calculator.new.counter + end + def test_memoized_is_not_affected_by_freeze @person.freeze assert_equal "Josh", @person.name - assert_equal true, @person.name.frozen? end - def test_memoization_frozen_with_nil_value - @person.freeze - assert_equal nil, @person.age + def test_memoization_with_args + assert_equal 55, @calculator.fib(10) + assert_equal 11, @calculator.fib_calls end - def test_double_memoization - assert_raise(RuntimeError) { Person.memoize :name } + def test_reloadable_with_args + assert_equal 55, @calculator.fib(10) + assert_equal 11, @calculator.fib_calls + assert_equal 55, @calculator.fib(10, :reload) + assert_equal 12, @calculator.fib_calls + assert_equal 55, @calculator.fib(10, true) + assert_equal 13, @calculator.fib_calls end - class Company - def name - lookup_name + def test_object_memoization + [Company.new, Company.new, Company.new].each do |company| + company.extend ActiveSupport::Memoizable + company.memoize :name + + assert_equal "37signals", company.name + assert_equal 1, company.name_calls + assert_equal "37signals", company.name + assert_equal 1, company.name_calls end + end - def lookup_name - "37signals" - end + def test_memoized_module_methods + assert_equal 1.025, @calculator.sales_tax(10) + assert_equal 1, @calculator.sales_tax_calls + assert_equal 1.025, @calculator.sales_tax(10) + assert_equal 1, @calculator.sales_tax_calls + assert_equal 2.5625, @calculator.sales_tax(25) + assert_equal 2, @calculator.sales_tax_calls end - def test_object_memoization + def test_object_memoized_module_methods company = Company.new - company.extend ActiveSupport::Memoizable - company.memoize :name + company.extend(Rates) + + assert_equal 1.025, company.sales_tax(10) + assert_equal 1, company.sales_tax_calls + assert_equal 1.025, company.sales_tax(10) + assert_equal 1, company.sales_tax_calls + assert_equal 2.5625, company.sales_tax(25) + assert_equal 2, company.sales_tax_calls + end - assert_equal "37signals", company.name - # Mocha doesn't play well with frozen objects - company.metaclass.instance_eval { define_method(:lookup_name) { b00m } } - assert_equal "37signals", company.name + def test_double_memoization + assert_raise(RuntimeError) { Person.memoize :name } + person = Person.new + person.extend ActiveSupport::Memoizable + assert_raise(RuntimeError) { person.memoize :name } - assert_equal true, company.name.frozen? - company.freeze - assert_equal true, company.name.frozen? + company = Company.new + company.extend ActiveSupport::Memoizable + company.memoize :name + assert_raise(RuntimeError) { company.memoize :name } end end end -- cgit v1.2.3 From bc5896e708bf8070835bebe61de03b701fa5e6f7 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Jul 2008 10:27:32 -0500 Subject: Memoize ActionView::Base pick_template and find_partial_path for rendering duration --- actionpack/lib/action_controller/base.rb | 2 ++ actionpack/lib/action_view/base.rb | 7 +++++-- actionpack/lib/action_view/partials.rb | 9 +++++---- actionpack/lib/action_view/template.rb | 2 +- .../test/template/compiled_templates_test.rb | 23 ++++++++++++---------- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 50727c67c4..4dabff637b 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -1261,6 +1261,8 @@ module ActionController #:nodoc: def template_exempt_from_layout?(template_name = default_template_name) template_name = @template.pick_template(template_name).to_s if @template @@exempt_from_layout.any? { |ext| template_name =~ ext } + rescue ActionView::MissingTemplate + false end def default_template_name(action_name = self.action_name) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 619a4270f8..bdcb1dc246 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -323,8 +323,8 @@ module ActionView #:nodoc: if self.class.warn_cache_misses && logger = ActionController::Base.logger logger.debug "[PERFORMANCE] Rendering a template that was " + "not found in view path. Templates outside the view path are " + - "not cached and result in expensive disk operations. Move this " + - "file into #{view_paths.join(':')} or add the folder to your " + + "not cached and result in expensive disk operations. Move this " + + "file into #{view_paths.join(':')} or add the folder to your " + "view path list" end @@ -332,6 +332,9 @@ module ActionView #:nodoc: end end + extend ActiveSupport::Memoizable + memoize :pick_template + private # Renders the template present at template_path. The hash in local_assigns # is made available as local variables. diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 5aa4c83009..eb74d4a4c7 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -102,6 +102,8 @@ module ActionView # # As you can see, the :locals hash is shared between both the partial and its layout. module Partials + extend ActiveSupport::Memoizable + private def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc: local_assigns ||= {} @@ -129,14 +131,12 @@ module ActionView local_assigns = local_assigns ? local_assigns.clone : {} spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : '' - _paths = {} - _templates = {} index = 0 collection.map do |object| _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) - path = _paths[_partial_path] ||= find_partial_path(_partial_path) - template = _templates[path] ||= pick_template(path) + path = find_partial_path(_partial_path) + template = pick_template(path) local_assigns[template.counter_name] = index result = template.render_partial(self, object, local_assigns, as) index += 1 @@ -153,5 +153,6 @@ module ActionView "_#{partial_path}" end end + memoize :find_partial_path end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 1f528dd900..3fcd9a2d01 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -75,7 +75,7 @@ module ActionView #:nodoc: load_paths = Array(load_paths) + [nil] load_paths.each do |load_path| file = [load_path, path].compact.join('/') - return load_path, file if File.exist?(file) + return load_path, file if File.exist?(file) && File.file?(file) end raise MissingTemplate.new(load_paths, path) end diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb index 4b34827f91..52996c7fcb 100644 --- a/actionpack/test/template/compiled_templates_test.rb +++ b/actionpack/test/template/compiled_templates_test.rb @@ -4,7 +4,6 @@ require 'controller/fake_models' uses_mocha 'TestTemplateRecompilation' do class CompiledTemplatesTest < Test::Unit::TestCase def setup - @view = ActionView::Base.new(ActionController::Base.view_paths, {}) @compiled_templates = ActionView::Base::CompiledTemplates @compiled_templates.instance_methods.each do |m| @compiled_templates.send(:remove_method, m) if m =~ /^_run_/ @@ -13,29 +12,33 @@ uses_mocha 'TestTemplateRecompilation' do def test_template_gets_compiled assert_equal 0, @compiled_templates.instance_methods.size - assert_equal "Hello world!", @view.render("test/hello_world.erb") + assert_equal "Hello world!", render("test/hello_world.erb") assert_equal 1, @compiled_templates.instance_methods.size end def test_template_gets_recompiled_when_using_different_keys_in_local_assigns assert_equal 0, @compiled_templates.instance_methods.size - assert_equal "Hello world!", @view.render("test/hello_world.erb") - assert_equal "Hello world!", @view.render("test/hello_world.erb", {:foo => "bar"}) + assert_equal "Hello world!", render("test/hello_world.erb") + assert_equal "Hello world!", render("test/hello_world.erb", {:foo => "bar"}) assert_equal 2, @compiled_templates.instance_methods.size end def test_compiled_template_will_not_be_recompiled_when_rendered_with_identical_local_assigns assert_equal 0, @compiled_templates.instance_methods.size - assert_equal "Hello world!", @view.render("test/hello_world.erb") + assert_equal "Hello world!", render("test/hello_world.erb") ActionView::Template.any_instance.expects(:compile!).never - assert_equal "Hello world!", @view.render("test/hello_world.erb") + assert_equal "Hello world!", render("test/hello_world.erb") end - def test_compiled_template_will_always_be_recompiled_when_rendered_if_template_is_outside_cache + def test_compiled_template_will_be_recompiled_when_rendered_if_template_is_outside_cache assert_equal 0, @compiled_templates.instance_methods.size - assert_equal "Hello world!", @view.render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") - ActionView::Template.any_instance.expects(:compile!).times(3) - 3.times { assert_equal "Hello world!", @view.render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") } + assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") + assert_equal 1, @compiled_templates.instance_methods.size end + + private + def render(*args) + ActionView::Base.new(ActionController::Base.view_paths, {}).render(*args) + end end end -- cgit v1.2.3 From 2681685450631238511cfc3c2f0fa044c1f8033a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Jul 2008 11:12:16 -0500 Subject: Extract ActiveSupport::TypedArray class to ensure an array is all of the same type [#673 state:resolved] --- actionpack/lib/action_view/paths.rb | 31 +++------------ actionpack/test/controller/view_paths_test.rb | 10 +---- activesupport/lib/active_support.rb | 1 + activesupport/lib/active_support/typed_array.rb | 31 +++++++++++++++ activesupport/test/typed_array_test.rb | 51 +++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 33 deletions(-) create mode 100644 activesupport/lib/active_support/typed_array.rb create mode 100644 activesupport/test/typed_array_test.rb diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 78548d4aa2..9cb50ab4f8 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -1,5 +1,5 @@ module ActionView #:nodoc: - class PathSet < Array #:nodoc: + class PathSet < ActiveSupport::TypedArray #:nodoc: def self.type_cast(obj) if obj.is_a?(String) if Base.warn_cache_misses && defined?(Rails) && Rails.initialized? @@ -25,7 +25,7 @@ module ActionView #:nodoc: end attr_reader :path, :paths - delegate :to_s, :to_str, :inspect, :to => :path + delegate :to_s, :to_str, :hash, :inspect, :to => :path def initialize(path) raise ArgumentError, "path already is a Path class" if path.is_a?(Path) @@ -38,6 +38,10 @@ module ActionView #:nodoc: to_str == path.to_str end + def eql?(path) + to_str == path.to_str + end + def [](path) @paths[path] end @@ -67,28 +71,10 @@ module ActionView #:nodoc: end end - def initialize(*args) - super(*args).map! { |obj| self.class.type_cast(obj) } - end - def reload! each { |path| path.reload! } end - def <<(obj) - super(self.class.type_cast(obj)) - end - - def push(*objs) - delete_paths!(objs) - super(*objs.map { |obj| self.class.type_cast(obj) }) - end - - def unshift(*objs) - delete_paths!(objs) - super(*objs.map { |obj| self.class.type_cast(obj) }) - end - def [](template_path) each do |path| if template = path[template_path] @@ -97,10 +83,5 @@ module ActionView #:nodoc: end nil end - - private - def delete_paths!(paths) - paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } } - end end end diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index 85fa58a45b..b859a92cbd 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -54,10 +54,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths @controller.append_view_path(FIXTURE_LOAD_PATH) - assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths - - @controller.append_view_path([FIXTURE_LOAD_PATH]) - assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths end def test_controller_prepends_view_path_correctly @@ -68,10 +65,7 @@ class ViewLoadPathsTest < Test::Unit::TestCase assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths @controller.prepend_view_path(FIXTURE_LOAD_PATH) - assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths - - @controller.prepend_view_path([FIXTURE_LOAD_PATH]) - assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths end def test_template_appends_view_path_correctly diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 1df911a3f2..51067e910e 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -39,6 +39,7 @@ require 'active_support/cache' require 'active_support/dependencies' require 'active_support/deprecation' +require 'active_support/typed_array' require 'active_support/ordered_hash' require 'active_support/ordered_options' require 'active_support/option_merger' diff --git a/activesupport/lib/active_support/typed_array.rb b/activesupport/lib/active_support/typed_array.rb new file mode 100644 index 0000000000..1a4d8a8faf --- /dev/null +++ b/activesupport/lib/active_support/typed_array.rb @@ -0,0 +1,31 @@ +module ActiveSupport + class TypedArray < Array + def self.type_cast(obj) + obj + end + + def initialize(*args) + super(*args).map! { |obj| self.class.type_cast(obj) } + end + + def <<(obj) + super(self.class.type_cast(obj)) + end + + def concat(array) + super(array.map! { |obj| self.class.type_cast(obj) }) + end + + def insert(index, obj) + super(index, self.class.type_cast(obj)) + end + + def push(*objs) + super(*objs.map { |obj| self.class.type_cast(obj) }) + end + + def unshift(*objs) + super(*objs.map { |obj| self.class.type_cast(obj) }) + end + end +end diff --git a/activesupport/test/typed_array_test.rb b/activesupport/test/typed_array_test.rb new file mode 100644 index 0000000000..023f3a1b84 --- /dev/null +++ b/activesupport/test/typed_array_test.rb @@ -0,0 +1,51 @@ +require 'abstract_unit' + +class TypedArrayTest < Test::Unit::TestCase + class StringArray < ActiveSupport::TypedArray + def self.type_cast(obj) + obj.to_s + end + end + + def setup + @array = StringArray.new + end + + def test_string_array_initialize + assert_equal ["1", "2", "3"], StringArray.new([1, "2", :"3"]) + end + + def test_string_array_append + @array << 1 + @array << "2" + @array << :"3" + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_concat + @array.concat([1, "2"]) + @array.concat([:"3"]) + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_insert + @array.insert(0, 1) + @array.insert(1, "2") + @array.insert(2, :"3") + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_push + @array.push(1) + @array.push("2") + @array.push(:"3") + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_unshift + @array.unshift(:"3") + @array.unshift("2") + @array.unshift(1) + assert_equal ["1", "2", "3"], @array + end +end -- cgit v1.2.3 From 93e10f9911fb2a096681ee0a0bc82487a9a06c44 Mon Sep 17 00:00:00 2001 From: Jan De Poorter Date: Wed, 23 Jul 2008 12:50:16 +0200 Subject: Ensure NamedScope#any? uses COUNT query wherever possible. [#680 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/named_scope.rb | 10 +++++++++- activerecord/test/cases/named_scope_test.rb | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 080e3d0f5e..d5a1c5fe08 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -103,7 +103,7 @@ module ActiveRecord attr_reader :proxy_scope, :proxy_options [].methods.each do |m| - unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?)/ + unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|find|count|sum|average|maximum|minimum|paginate|first|last|empty?|any?)/ delegate m, :to => :proxy_found end end @@ -140,6 +140,14 @@ module ActiveRecord @found ? @found.empty? : count.zero? end + def any? + if block_given? + proxy_found.any? { |*block_args| yield(*block_args) } + else + !empty? + end + end + protected def proxy_found @found || load_found diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb index 0c1eb23428..e21ffbbdba 100644 --- a/activerecord/test/cases/named_scope_test.rb +++ b/activerecord/test/cases/named_scope_test.rb @@ -184,6 +184,28 @@ class NamedScopeTest < ActiveRecord::TestCase end end + def test_any_should_not_load_results + topics = Topic.base + assert_queries(1) do + topics.expects(:empty?).returns(true) + assert !topics.any? + end + end + + def test_any_should_call_proxy_found_if_using_a_block + topics = Topic.base + assert_queries(1) do + topics.expects(:empty?).never + topics.any? { true } + end + end + + def test_any_should_not_fire_query_if_named_scope_loaded + topics = Topic.base + topics.collect # force load + assert_no_queries { assert topics.any? } + end + def test_should_build_with_proxy_options topic = Topic.approved.build({}) assert topic.approved -- cgit v1.2.3 From db1bac796e2d53fac4b51a3f560010b8f663fb54 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 23 Jul 2008 10:24:47 -0500 Subject: Just file? --- actionpack/lib/action_view/template.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 3fcd9a2d01..b281ff61f2 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -75,7 +75,7 @@ module ActionView #:nodoc: load_paths = Array(load_paths) + [nil] load_paths.each do |load_path| file = [load_path, path].compact.join('/') - return load_path, file if File.exist?(file) && File.file?(file) + return load_path, file if File.file?(file) end raise MissingTemplate.new(load_paths, path) end -- cgit v1.2.3 From 97a954bf1dd05e79a873bffc94fcf5420b807371 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 23 Jul 2008 10:41:28 -0500 Subject: Load view path cache after plugins and gems. --- actionpack/lib/action_view/paths.rb | 18 +++++++++++++++--- railties/lib/initializer.rb | 13 ++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 9cb50ab4f8..a37706faee 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -27,11 +27,10 @@ module ActionView #:nodoc: attr_reader :path, :paths delegate :to_s, :to_str, :hash, :inspect, :to => :path - def initialize(path) + def initialize(path, load = true) raise ArgumentError, "path already is a Path class" if path.is_a?(Path) - @path = path.freeze - reload! + reload! if load end def ==(path) @@ -46,6 +45,14 @@ module ActionView #:nodoc: @paths[path] end + def loaded? + @loaded ? true : false + end + + def load + reload! unless loaded? + end + # Rebuild load path directory cache def reload! @paths = {} @@ -59,6 +66,7 @@ module ActionView #:nodoc: end @paths.freeze + @loaded = true end private @@ -71,6 +79,10 @@ module ActionView #:nodoc: end end + def load + each { |path| path.load } + end + def reload! each { |path| path.reload! } end diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 828d688475..97bb81a3c8 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -168,6 +168,9 @@ module Rails # Observers are loaded after plugins in case Observers or observed models are modified by plugins. load_observers + # Load view path cache + load_view_paths + # load application classes load_application_classes @@ -333,6 +336,12 @@ Run `rake gems:install` to install the missing gems. end end + def load_view_paths + ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes + ActionMailer::Base.template_root.load + ActionController::Base.view_paths.load + end + # Eager load application classes def load_application_classes if configuration.cache_classes @@ -428,9 +437,7 @@ Run `rake gems:install` to install the missing gems. # paths have already been set, it is not changed, otherwise it is # set to use Configuration#view_path. def initialize_framework_views - ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes - view_path = ActionView::PathSet::Path.new(configuration.view_path) - + view_path = ActionView::PathSet::Path.new(configuration.view_path, false) ActionMailer::Base.template_root ||= view_path if configuration.frameworks.include?(:action_mailer) ActionController::Base.view_paths = view_path if configuration.frameworks.include?(:action_controller) && ActionController::Base.view_paths.empty? end -- cgit v1.2.3 From e0db925be04ab3e9c3db67dd0daa8caf3680dd21 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 23 Jul 2008 11:23:25 -0500 Subject: Revert 'bc5896e' --- actionpack/lib/action_view/base.rb | 3 --- actionpack/lib/action_view/partials.rb | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index bdcb1dc246..c769013d42 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -332,9 +332,6 @@ module ActionView #:nodoc: end end - extend ActiveSupport::Memoizable - memoize :pick_template - private # Renders the template present at template_path. The hash in local_assigns # is made available as local variables. diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index eb74d4a4c7..5aa4c83009 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -102,8 +102,6 @@ module ActionView # # As you can see, the :locals hash is shared between both the partial and its layout. module Partials - extend ActiveSupport::Memoizable - private def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc: local_assigns ||= {} @@ -131,12 +129,14 @@ module ActionView local_assigns = local_assigns ? local_assigns.clone : {} spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : '' + _paths = {} + _templates = {} index = 0 collection.map do |object| _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) - path = find_partial_path(_partial_path) - template = pick_template(path) + path = _paths[_partial_path] ||= find_partial_path(_partial_path) + template = _templates[path] ||= pick_template(path) local_assigns[template.counter_name] = index result = template.render_partial(self, object, local_assigns, as) index += 1 @@ -153,6 +153,5 @@ module ActionView "_#{partial_path}" end end - memoize :find_partial_path end end -- cgit v1.2.3 From 55adaa2efc08c892bf7be55d79ac571848068256 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 23 Jul 2008 13:47:30 -0500 Subject: Fixed bc5896e, and added test case for the caching bug it originally introduced. --- actionpack/lib/action_view/base.rb | 3 +++ actionpack/lib/action_view/partials.rb | 9 +++++---- actionpack/lib/action_view/renderable.rb | 2 +- actionpack/test/template/compiled_templates_test.rb | 5 ++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c769013d42..bdcb1dc246 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -332,6 +332,9 @@ module ActionView #:nodoc: end end + extend ActiveSupport::Memoizable + memoize :pick_template + private # Renders the template present at template_path. The hash in local_assigns # is made available as local variables. diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 5aa4c83009..eb74d4a4c7 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -102,6 +102,8 @@ module ActionView # # As you can see, the :locals hash is shared between both the partial and its layout. module Partials + extend ActiveSupport::Memoizable + private def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc: local_assigns ||= {} @@ -129,14 +131,12 @@ module ActionView local_assigns = local_assigns ? local_assigns.clone : {} spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : '' - _paths = {} - _templates = {} index = 0 collection.map do |object| _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) - path = _paths[_partial_path] ||= find_partial_path(_partial_path) - template = _templates[path] ||= pick_template(path) + path = find_partial_path(_partial_path) + template = pick_template(path) local_assigns[template.counter_name] = index result = template.render_partial(self, object, local_assigns, as) index += 1 @@ -153,5 +153,6 @@ module ActionView "_#{partial_path}" end end + memoize :find_partial_path end end diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb index 2b825ac4e9..5fe1ca86f3 100644 --- a/actionpack/lib/action_view/renderable.rb +++ b/actionpack/lib/action_view/renderable.rb @@ -84,7 +84,7 @@ module ActionView # The template will be compiled if the file has not been compiled yet, or # if local_assigns has a new key, which isn't supported by the compiled code yet. def recompile?(symbol) - !(frozen? && Base::CompiledTemplates.method_defined?(symbol)) + !(ActionView::PathSet::Path.eager_load_templates? && Base::CompiledTemplates.method_defined?(symbol)) end end end diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb index 52996c7fcb..e005aa0f03 100644 --- a/actionpack/test/template/compiled_templates_test.rb +++ b/actionpack/test/template/compiled_templates_test.rb @@ -30,9 +30,12 @@ uses_mocha 'TestTemplateRecompilation' do assert_equal "Hello world!", render("test/hello_world.erb") end - def test_compiled_template_will_be_recompiled_when_rendered_if_template_is_outside_cache + def test_compiled_template_will_always_be_recompiled_when_eager_loaded_templates_is_off + ActionView::PathSet::Path.expects(:eager_load_templates?).times(4).returns(false) assert_equal 0, @compiled_templates.instance_methods.size assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") + ActionView::Template.any_instance.expects(:compile!).times(3) + 3.times { assert_equal "Hello world!", render("#{FIXTURE_LOAD_PATH}/test/hello_world.erb") } assert_equal 1, @compiled_templates.instance_methods.size end -- cgit v1.2.3 From 3fd9036fc554979e951422a79f0f77f061112bdc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 24 Jul 2008 11:58:26 -0500 Subject: Added config.dependency_loading to enable or disable the dependency loader after initialization --- activesupport/lib/active_support/dependencies.rb | 78 ++++++++++++++++++------ activesupport/test/dependencies_test.rb | 12 ++++ railties/lib/initializer.rb | 27 +++++++- 3 files changed, 95 insertions(+), 22 deletions(-) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e3d4f3d7eb..a3f5f799a2 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -51,17 +51,28 @@ module ActiveSupport #:nodoc: module ModuleConstMissing #:nodoc: def self.included(base) #:nodoc: base.class_eval do - # Rename the original handler so we can chain it to the new one - alias_method :rails_original_const_missing, :const_missing + unless defined? const_missing_without_dependencies + alias_method_chain :const_missing, :dependencies + end + end + end - # Use const_missing to autoload associations so we don't have to - # require_association when using single-table inheritance. - def const_missing(class_id) - ActiveSupport::Dependencies.load_missing_constant self, class_id + def self.excluded(base) #:nodoc: + base.class_eval do + if defined? const_missing_without_dependencies + undef_method :const_missing + alias_method :const_missing, :const_missing_without_dependencies + undef_method :const_missing_without_dependencies end end end + # Use const_missing to autoload associations so we don't have to + # require_association when using single-table inheritance. + def const_missing_with_dependencies(class_id) + ActiveSupport::Dependencies.load_missing_constant self, class_id + end + def unloadable(const_desc = self) super(const_desc) end @@ -92,8 +103,38 @@ module ActiveSupport #:nodoc: # Object includes this module module Loadable #:nodoc: - def load(file, *extras) #:nodoc: - Dependencies.new_constants_in(Object) { super } + def self.included(base) #:nodoc: + base.class_eval do + unless defined? load_without_new_constant_marking + alias_method_chain :load, :new_constant_marking + end + end + end + + def self.excluded(base) #:nodoc: + base.class_eval do + if defined? load_without_new_constant_marking + undef_method :load + alias_method :load, :load_without_new_constant_marking + undef_method :load_without_new_constant_marking + end + end + end + + def require_or_load(file_name) + Dependencies.require_or_load(file_name) + end + + def require_dependency(file_name) + Dependencies.depend_on(file_name) + end + + def require_association(file_name) + Dependencies.associate_with(file_name) + end + + def load_with_new_constant_marking(file, *extras) #:nodoc: + Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) } rescue Exception => exception # errors from loading file exception.blame_file! file raise @@ -145,19 +186,18 @@ module ActiveSupport #:nodoc: end end - def inject! - Object.instance_eval do - define_method(:require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) - define_method(:require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency) - define_method(:require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association) - - alias_method :load_without_new_constant_marking, :load - include Loadable - end - + def hook! + Object.instance_eval { include Loadable } Module.instance_eval { include ModuleConstMissing } Class.instance_eval { include ClassConstMissing } Exception.instance_eval { include Blamable } + true + end + + def unhook! + ModuleConstMissing.excluded(Module) + Loadable.excluded(Object) + true end def load? @@ -560,4 +600,4 @@ module ActiveSupport #:nodoc: end end -ActiveSupport::Dependencies.inject! +ActiveSupport::Dependencies.hook! diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 038547a862..39c9c74c94 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -762,4 +762,16 @@ class DependenciesTest < Test::Unit::TestCase ensure ActiveSupport::Dependencies.load_once_paths = [] end + + def test_hook_called_multiple_times + assert_nothing_raised { ActiveSupport::Dependencies.hook! } + end + + def test_unhook + ActiveSupport::Dependencies.unhook! + assert !Module.new.respond_to?(:const_missing_without_dependencies) + assert !Module.new.respond_to?(:load_without_new_constant_marking) + ensure + ActiveSupport::Dependencies.hook! + end end diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 97bb81a3c8..44863ab026 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -171,9 +171,12 @@ module Rails # Load view path cache load_view_paths - # load application classes + # Load application classes load_application_classes + # Disable dependency loading during request cycle + disable_dependency_loading + # Flag initialized Rails.initialized = true end @@ -525,6 +528,12 @@ Run `rake gems:install` to install the missing gems. Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) Dispatcher.new(RAILS_DEFAULT_LOGGER).send :run_callbacks, :prepare_dispatch end + + def disable_dependency_loading + if configuration.cache_classes && !configuration.dependency_loading + ActiveSupport::Dependencies.unhook! + end + end end # The Configuration class holds all the parameters for the Initializer and @@ -659,6 +668,17 @@ Run `rake gems:install` to install the missing gems. !!@reload_plugins end + # Enables or disables dependency loading during the request cycle. Setting + # dependency_loading to true will allow new classes to be loaded + # during a request. Setting it to false will disable this behavior. + # + # Those who want to run in a threaded environment should disable this + # option and eager load or require all there classes on initialization. + # + # If cache_classes is disabled, dependency loaded will always be + # on. + attr_accessor :dependency_loading + # An array of gems that this rails application depends on. Rails will automatically load # these gems during installation, and allow you to install any missing gems with: # @@ -707,6 +727,7 @@ Run `rake gems:install` to install the missing gems. self.view_path = default_view_path self.controller_paths = default_controller_paths self.cache_classes = default_cache_classes + self.dependency_loading = default_dependency_loading self.whiny_nils = default_whiny_nils self.plugins = default_plugins self.plugin_paths = default_plugin_paths @@ -876,8 +897,8 @@ Run `rake gems:install` to install the missing gems. paths end - def default_dependency_mechanism - :load + def default_dependency_loading + true end def default_cache_classes -- cgit v1.2.3 From a87462afcb6c642e59bfcd2e11e3351e2b718c38 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 24 Jul 2008 13:41:51 -0500 Subject: AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root --- actionpack/CHANGELOG | 2 + actionpack/lib/action_controller/base.rb | 4 + actionpack/lib/action_controller/request.rb | 41 +++----- .../lib/action_controller/routing/optimisations.rb | 2 +- actionpack/lib/action_controller/url_rewriter.rb | 6 +- .../lib/action_view/helpers/asset_tag_helper.rb | 6 +- actionpack/test/controller/redirect_test.rb | 109 ++++++++++----------- actionpack/test/controller/request_test.rb | 100 +++++-------------- actionpack/test/controller/routing_test.rb | 10 +- actionpack/test/controller/url_rewriter_test.rb | 55 +++++------ actionpack/test/template/asset_tag_helper_test.rb | 45 ++++----- 11 files changed, 161 insertions(+), 219 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index ebe4c047b8..03e011c75c 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root [Josh Peek] + * Update Prototype to 1.6.0.2 #599 [Patrick Joyce] * Conditional GET utility methods. [Jeremy Kemper] diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4dabff637b..bae7e8c12e 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -354,6 +354,10 @@ module ActionController #:nodoc: class_inheritable_accessor :allow_forgery_protection self.allow_forgery_protection = true + # If you are deploying to a subdirectory, you will need to set + # config.action_controller.relative_url_root + class_inheritable_accessor :relative_url_root + # Holds the request object that's primarily used to get environment variables through access like # request.env["REQUEST_URI"]. attr_internal :request diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index c42f113d2c..c55788a531 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -3,13 +3,16 @@ require 'stringio' require 'strscan' module ActionController - # HTTP methods which are accepted by default. + # HTTP methods which are accepted by default. ACCEPTED_HTTP_METHODS = Set.new(%w( get head put post delete options )) # CgiRequest and TestRequest provide concrete implementations. class AbstractRequest - cattr_accessor :relative_url_root - remove_method :relative_url_root + def self.relative_url_root=(*args) + ActiveSupport::Deprecation.warn( + "ActionController::AbstractRequest.relative_url_root= has been renamed." + + "You can now set it with config.action_controller.relative_url_root=", caller) + end # The hash of environment variables for this request, # such as { 'RAILS_ENV' => 'production' }. @@ -111,14 +114,14 @@ module ActionController end end end - - + + # Sets the format by string extension, which can be used to force custom formats that are not controlled by the extension. # Example: # # class ApplicationController < ActionController::Base # before_filter :adjust_format_for_iphone - # + # # private # def adjust_format_for_iphone # request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/] @@ -303,26 +306,10 @@ EOM path = (uri = request_uri) ? uri.split('?').first.to_s : '' # Cut off the path to the installation directory if given - path.sub!(%r/^#{relative_url_root}/, '') - path || '' - end - - # Returns the path minus the web server relative installation directory. - # This can be set with the environment variable RAILS_RELATIVE_URL_ROOT. - # It can be automatically extracted for Apache setups. If the server is not - # Apache, this method returns an empty string. - def relative_url_root - @@relative_url_root ||= case - when @env["RAILS_RELATIVE_URL_ROOT"] - @env["RAILS_RELATIVE_URL_ROOT"] - when server_software == 'apache' - @env["SCRIPT_NAME"].to_s.sub(/\/dispatch\.(fcgi|rb|cgi)$/, '') - else - '' - end + path.sub!(%r/^#{ActionController::Base.relative_url_root}/, '') + path || '' end - # Read the request body. This is useful for web services that need to # work with raw requests directly. def raw_post @@ -343,15 +330,15 @@ EOM @symbolized_path_parameters = @parameters = nil end - # The same as path_parameters with explicitly symbolized keys - def symbolized_path_parameters + # The same as path_parameters with explicitly symbolized keys + def symbolized_path_parameters @symbolized_path_parameters ||= path_parameters.symbolize_keys end # Returns a hash with the parameters used to form the path of the request. # Returned hash keys are strings. See symbolized_path_parameters for symbolized keys. # - # Example: + # Example: # # {'action' => 'my_action', 'controller' => 'my_controller'} def path_parameters diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb index cd4a423e6b..4b70ea13f2 100644 --- a/actionpack/lib/action_controller/routing/optimisations.rb +++ b/actionpack/lib/action_controller/routing/optimisations.rb @@ -76,7 +76,7 @@ module ActionController elements << '#{request.host_with_port}' end - elements << '#{request.relative_url_root if request.relative_url_root}' + elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}' # The last entry in route.segments appears to *always* be a # 'divider segment' for '/' but we have assertions to ensure that diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb index 3a38f23396..d0bf6c0bd4 100644 --- a/actionpack/lib/action_controller/url_rewriter.rb +++ b/actionpack/lib/action_controller/url_rewriter.rb @@ -37,7 +37,7 @@ module ActionController # * :port - Optionally specify the port to connect to. # * :anchor - An anchor name to be appended to the path. # * :skip_relative_url_root - If true, the url is not constructed using the - # +relative_url_root+ set in ActionController::AbstractRequest.relative_url_root. + # +relative_url_root+ set in ActionController::Base.relative_url_root. # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2009/" # # Any other key (:controller, :action, etc.) given to @@ -67,7 +67,7 @@ module ActionController [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) } end trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash) - url << ActionController::AbstractRequest.relative_url_root.to_s unless options[:skip_relative_url_root] + url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root] anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor] generated = Routing::Routes.generate(options, {}) url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated) @@ -108,7 +108,7 @@ module ActionController end path = rewrite_path(options) - rewritten_url << @request.relative_url_root.to_s unless options[:skip_relative_url_root] + rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root] rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path) rewritten_url << "##{options[:anchor]}" if options[:anchor] diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 8d0ee81684..769eada120 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -476,7 +476,7 @@ module ActionView if has_request [ @controller.request.protocol, ActionController::Base.asset_host.to_s, - @controller.request.relative_url_root, + ActionController::Base.relative_url_root, dir, source, ext, include_host ].join else [ ActionController::Base.asset_host.to_s, @@ -492,8 +492,8 @@ module ActionView else source = "/#{dir}/#{source}" unless source[0] == ?/ if has_request - unless source =~ %r{^#{@controller.request.relative_url_root}/} - source = "#{@controller.request.relative_url_root}#{source}" + unless source =~ %r{^#{ActionController::Base.relative_url_root}/} + source = "#{ActionController::Base.relative_url_root}#{source}" end end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 28da5c6163..2f8bf7b6ee 100755 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -9,11 +9,11 @@ class Workshop def initialize(id, new_record) @id, @new_record = id, new_record end - + def new_record? @new_record end - + def to_s id.to_s end @@ -24,32 +24,32 @@ class RedirectController < ActionController::Base redirect_to :action => "hello_world" end - def redirect_with_status + def redirect_with_status redirect_to({:action => "hello_world", :status => 301}) - end + end def redirect_with_status_hash redirect_to({:action => "hello_world"}, {:status => 301}) - end + end - def url_redirect_with_status + def url_redirect_with_status redirect_to("http://www.example.com", :status => :moved_permanently) - end - - def url_redirect_with_status_hash + end + + def url_redirect_with_status_hash redirect_to("http://www.example.com", {:status => 301}) - end + end - def relative_url_redirect_with_status + def relative_url_redirect_with_status redirect_to("/things/stuff", :status => :found) - end - + end + def relative_url_redirect_with_status_hash redirect_to("/things/stuff", {:status => 301}) - end - - def redirect_to_back_with_status - redirect_to :back, :status => 307 + end + + def redirect_to_back_with_status + redirect_to :back, :status => 307 end def host_redirect @@ -90,9 +90,9 @@ class RedirectController < ActionController::Base end def rescue_errors(e) raise e end - + def rescue_action(e) raise end - + protected def dashbord_url(id, message) url_for :action => "dashboard", :params => { "id" => id, "message" => message } @@ -118,48 +118,48 @@ class RedirectTest < Test::Unit::TestCase assert_equal "http://test.host/redirect/hello_world", redirect_to_url end - def test_redirect_with_status - get :redirect_with_status - assert_response 301 - assert_equal "http://test.host/redirect/hello_world", redirect_to_url - end + def test_redirect_with_status + get :redirect_with_status + assert_response 301 + assert_equal "http://test.host/redirect/hello_world", redirect_to_url + end - def test_redirect_with_status_hash + def test_redirect_with_status_hash get :redirect_with_status_hash - assert_response 301 - assert_equal "http://test.host/redirect/hello_world", redirect_to_url + assert_response 301 + assert_equal "http://test.host/redirect/hello_world", redirect_to_url + end + + def test_url_redirect_with_status + get :url_redirect_with_status + assert_response 301 + assert_equal "http://www.example.com", redirect_to_url end - - def test_url_redirect_with_status - get :url_redirect_with_status - assert_response 301 - assert_equal "http://www.example.com", redirect_to_url - end def test_url_redirect_with_status_hash get :url_redirect_with_status_hash - assert_response 301 - assert_equal "http://www.example.com", redirect_to_url - end + assert_response 301 + assert_equal "http://www.example.com", redirect_to_url + end - - def test_relative_url_redirect_with_status - get :relative_url_redirect_with_status + + def test_relative_url_redirect_with_status + get :relative_url_redirect_with_status assert_response 302 - assert_equal "http://test.host/things/stuff", redirect_to_url - end - + assert_equal "http://test.host/things/stuff", redirect_to_url + end + def test_relative_url_redirect_with_status_hash get :relative_url_redirect_with_status_hash - assert_response 301 - assert_equal "http://test.host/things/stuff", redirect_to_url - end - - def test_redirect_to_back_with_status - @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from" - get :redirect_to_back_with_status - assert_response 307 - assert_equal "http://www.example.com/coming/from", redirect_to_url + assert_response 301 + assert_equal "http://test.host/things/stuff", redirect_to_url + end + + def test_redirect_to_back_with_status + @request.env["HTTP_REFERER"] = "http://www.example.com/coming/from" + get :redirect_to_back_with_status + assert_response 307 + assert_equal "http://www.example.com/coming/from", redirect_to_url end def test_simple_redirect_using_options @@ -204,20 +204,20 @@ class RedirectTest < Test::Unit::TestCase assert_response :redirect assert_equal "http://www.example.com/coming/from", redirect_to_url end - + def test_redirect_to_back_with_no_referer assert_raises(ActionController::RedirectBackError) { @request.env["HTTP_REFERER"] = nil get :redirect_to_back } end - + def test_redirect_to_record ActionController::Routing::Routes.draw do |map| map.resources :workshops map.connect ':controller/:action/:id' end - + get :redirect_to_existing_record assert_equal "http://test.host/workshops/5", redirect_to_url assert_redirected_to Workshop.new(5, false) @@ -237,7 +237,6 @@ class RedirectTest < Test::Unit::TestCase get :redirect_to_nil end end - end module ModuleTest diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index 932c0e21a1..7db5264840 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -3,9 +3,14 @@ require 'action_controller/integration' class RequestTest < Test::Unit::TestCase def setup + ActionController::Base.relative_url_root = nil @request = ActionController::TestRequest.new end + def teardown + ActionController::Base.relative_url_root = nil + end + def test_remote_ip assert_equal '0.0.0.0', @request.remote_ip @@ -38,7 +43,7 @@ class RequestTest < Test::Unit::TestCase @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1,3.4.5.6' assert_equal '3.4.5.6', @request.remote_ip - + @request.env['HTTP_X_FORWARDED_FOR'] = '10.0.0.1, 10.0.0.1, 3.4.5.6' assert_equal '3.4.5.6', @request.remote_ip @@ -120,155 +125,105 @@ class RequestTest < Test::Unit::TestCase assert_equal ":8080", @request.port_string end - def test_relative_url_root - @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" - @request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3' - assert_equal '', @request.relative_url_root, "relative_url_root should be disabled on lighttpd" - - @request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text' - - @request.env['SCRIPT_NAME'] = nil - assert_equal "", @request.relative_url_root - - @request.env['SCRIPT_NAME'] = "/dispatch.cgi" - assert_equal "", @request.relative_url_root - - @request.env['SCRIPT_NAME'] = "/myapp.rb" - assert_equal "", @request.relative_url_root - - @request.relative_url_root = nil - @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" - assert_equal "/hieraki", @request.relative_url_root - - @request.relative_url_root = nil - @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi" - assert_equal "/collaboration/hieraki", @request.relative_url_root - - # apache/scgi case - @request.relative_url_root = nil - @request.env['SCRIPT_NAME'] = "/collaboration/hieraki" - assert_equal "/collaboration/hieraki", @request.relative_url_root - - @request.relative_url_root = nil - @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" - @request.env['SERVER_SOFTWARE'] = 'lighttpd/1.2.3' - @request.env['RAILS_RELATIVE_URL_ROOT'] = "/hieraki" - assert_equal "/hieraki", @request.relative_url_root - - # @env overrides path guess - @request.relative_url_root = nil - @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" - @request.env['SERVER_SOFTWARE'] = 'apache/1.2.3 some random text' - @request.env['RAILS_RELATIVE_URL_ROOT'] = "/real_url" - assert_equal "/real_url", @request.relative_url_root - end - def test_request_uri @request.env['SERVER_SOFTWARE'] = 'Apache 42.342.3432' - @request.relative_url_root = nil @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri?mapped=1" assert_equal "/path/of/some/uri?mapped=1", @request.request_uri assert_equal "/path/of/some/uri", @request.path - @request.relative_url_root = nil @request.set_REQUEST_URI "http://www.rubyonrails.org/path/of/some/uri" assert_equal "/path/of/some/uri", @request.request_uri assert_equal "/path/of/some/uri", @request.path - @request.relative_url_root = nil @request.set_REQUEST_URI "/path/of/some/uri" assert_equal "/path/of/some/uri", @request.request_uri assert_equal "/path/of/some/uri", @request.path - @request.relative_url_root = nil @request.set_REQUEST_URI "/" assert_equal "/", @request.request_uri assert_equal "/", @request.path - @request.relative_url_root = nil @request.set_REQUEST_URI "/?m=b" assert_equal "/?m=b", @request.request_uri assert_equal "/", @request.path - @request.relative_url_root = nil @request.set_REQUEST_URI "/" @request.env['SCRIPT_NAME'] = "/dispatch.cgi" assert_equal "/", @request.request_uri assert_equal "/", @request.path - @request.relative_url_root = nil + ActionController::Base.relative_url_root = "/hieraki" @request.set_REQUEST_URI "/hieraki/" @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" assert_equal "/hieraki/", @request.request_uri assert_equal "/", @request.path + ActionController::Base.relative_url_root = nil - @request.relative_url_root = nil + ActionController::Base.relative_url_root = "/collaboration/hieraki" @request.set_REQUEST_URI "/collaboration/hieraki/books/edit/2" @request.env['SCRIPT_NAME'] = "/collaboration/hieraki/dispatch.cgi" assert_equal "/collaboration/hieraki/books/edit/2", @request.request_uri assert_equal "/books/edit/2", @request.path + ActionController::Base.relative_url_root = nil # The following tests are for when REQUEST_URI is not supplied (as in IIS) - @request.relative_url_root = nil @request.set_REQUEST_URI nil @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1" @request.env['SCRIPT_NAME'] = nil #"/path/dispatch.rb" assert_equal "/path/of/some/uri?mapped=1", @request.request_uri assert_equal "/path/of/some/uri", @request.path + ActionController::Base.relative_url_root = '/path' @request.set_REQUEST_URI nil - @request.relative_url_root = nil @request.env['PATH_INFO'] = "/path/of/some/uri?mapped=1" @request.env['SCRIPT_NAME'] = "/path/dispatch.rb" assert_equal "/path/of/some/uri?mapped=1", @request.request_uri assert_equal "/of/some/uri", @request.path + ActionController::Base.relative_url_root = nil @request.set_REQUEST_URI nil - @request.relative_url_root = nil @request.env['PATH_INFO'] = "/path/of/some/uri" @request.env['SCRIPT_NAME'] = nil assert_equal "/path/of/some/uri", @request.request_uri assert_equal "/path/of/some/uri", @request.path @request.set_REQUEST_URI nil - @request.relative_url_root = nil @request.env['PATH_INFO'] = "/" assert_equal "/", @request.request_uri assert_equal "/", @request.path @request.set_REQUEST_URI nil - @request.relative_url_root = nil @request.env['PATH_INFO'] = "/?m=b" assert_equal "/?m=b", @request.request_uri assert_equal "/", @request.path @request.set_REQUEST_URI nil - @request.relative_url_root = nil @request.env['PATH_INFO'] = "/" @request.env['SCRIPT_NAME'] = "/dispatch.cgi" assert_equal "/", @request.request_uri assert_equal "/", @request.path + ActionController::Base.relative_url_root = '/hieraki' @request.set_REQUEST_URI nil - @request.relative_url_root = nil @request.env['PATH_INFO'] = "/hieraki/" @request.env['SCRIPT_NAME'] = "/hieraki/dispatch.cgi" assert_equal "/hieraki/", @request.request_uri assert_equal "/", @request.path + ActionController::Base.relative_url_root = nil @request.set_REQUEST_URI '/hieraki/dispatch.cgi' - @request.relative_url_root = '/hieraki' + ActionController::Base.relative_url_root = '/hieraki' assert_equal "/dispatch.cgi", @request.path - @request.relative_url_root = nil + ActionController::Base.relative_url_root = nil @request.set_REQUEST_URI '/hieraki/dispatch.cgi' - @request.relative_url_root = '/foo' + ActionController::Base.relative_url_root = '/foo' assert_equal "/hieraki/dispatch.cgi", @request.path - @request.relative_url_root = nil + ActionController::Base.relative_url_root = nil # This test ensures that Rails uses REQUEST_URI over PATH_INFO - @request.relative_url_root = nil + ActionController::Base.relative_url_root = nil @request.env['REQUEST_URI'] = "/some/path" @request.env['PATH_INFO'] = "/another/path" @request.env['SCRIPT_NAME'] = "/dispatch.cgi" @@ -276,13 +231,12 @@ class RequestTest < Test::Unit::TestCase assert_equal "/some/path", @request.path end - def test_host_with_default_port @request.host = "rubyonrails.org" @request.port = 80 assert_equal "rubyonrails.org", @request.host_with_port end - + def test_host_with_non_default_port @request.host = "rubyonrails.org" @request.port = 81 @@ -415,15 +369,15 @@ class RequestTest < Test::Unit::TestCase @request.env["CONTENT_TYPE"] = "application/xml; charset=UTF-8" assert_equal Mime::XML, @request.content_type end - + def test_user_agent assert_not_nil @request.user_agent end - + def test_parameters @request.instance_eval { @request_parameters = { "foo" => 1 } } @request.instance_eval { @query_parameters = { "bar" => 2 } } - + assert_equal({"foo" => 1, "bar" => 2}, @request.parameters) assert_equal({"foo" => 1}, @request.request_parameters) assert_equal({"bar" => 2}, @request.query_parameters) @@ -774,19 +728,19 @@ class MultipartRequestParameterParsingTest < Test::Unit::TestCase file = params['file'] foo = params['foo'] - + if RUBY_VERSION > '1.9' assert_kind_of File, file else assert_kind_of Tempfile, file end - + assert_equal 'file.txt', file.original_filename assert_equal "text/plain", file.content_type - + assert_equal 'bar', foo end - + def test_large_text_file params = process('large_text_file') assert_equal %w(file foo), params.keys.sort diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 079189d7b3..84996fd6b1 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -731,15 +731,10 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do def request @request ||= MockRequest.new(:host => "named.route.test", :method => :get) end - - def relative_url_root=(value) - request.relative_url_root=value - end end class MockRequest - attr_accessor :path, :path_parameters, :host, :subdomains, :domain, - :method, :relative_url_root + attr_accessor :path, :path_parameters, :host, :subdomains, :domain, :method def initialize(values={}) values.each { |key, value| send("#{key}=", value) } @@ -920,10 +915,11 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do def test_basic_named_route_with_relative_url_root rs.add_named_route :home, '', :controller => 'content', :action => 'list' x = setup_for_named_route - x.relative_url_root="/foo" + ActionController::Base.relative_url_root = "/foo" assert_equal("http://named.route.test/foo/", x.send(:home_url)) assert_equal "/foo/", x.send(:home_path) + ActionController::Base.relative_url_root = nil end def test_named_route_with_option diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index a9974db5d5..64e9a085ca 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -7,7 +7,7 @@ class UrlRewriterTests < Test::Unit::TestCase @request = ActionController::TestRequest.new @params = {} @rewriter = ActionController::UrlRewriter.new(@request, @params) - end + end def test_port assert_equal('http://test.host:1271/c/a/i', @@ -24,7 +24,7 @@ class UrlRewriterTests < Test::Unit::TestCase @rewriter.rewrite(:protocol => 'https://', :controller => 'c', :action => 'a', :id => 'i') ) end - + def test_user_name_and_password assert_equal( 'http://david:secret@test.host/c/a/i', @@ -38,12 +38,12 @@ class UrlRewriterTests < Test::Unit::TestCase @rewriter.rewrite(:user => "openid.aol.com/nextangler", :password => "one two?", :controller => 'c', :action => 'a', :id => 'i') ) end - - def test_anchor - assert_equal( - 'http://test.host/c/a/i#anchor', - @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor') - ) + + def test_anchor + assert_equal( + 'http://test.host/c/a/i#anchor', + @rewriter.rewrite(:controller => 'c', :action => 'a', :id => 'i', :anchor => 'anchor') + ) end def test_overwrite_params @@ -55,12 +55,12 @@ class UrlRewriterTests < Test::Unit::TestCase u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:action => 'hi'}) assert_match %r(/hi/hi/2$), u end - + def test_overwrite_removes_original @params[:controller] = 'search' @params[:action] = 'list' @params[:list_page] = 1 - + assert_equal '/search/list?list_page=2', @rewriter.rewrite(:only_path => true, :overwrite_params => {"list_page" => 2}) u = @rewriter.rewrite(:only_path => false, :overwrite_params => {:list_page => 2}) assert_equal 'http://test.host/search/list?list_page=2', u @@ -86,19 +86,19 @@ class UrlRewriterTests < Test::Unit::TestCase end class UrlWriterTests < Test::Unit::TestCase - + class W include ActionController::UrlWriter end - + def teardown W.default_url_options.clear end - + def add_host! W.default_url_options[:host] = 'www.basecamphq.com' end - + def test_exception_is_thrown_without_host assert_raises RuntimeError do W.new.url_for :controller => 'c', :action => 'a', :id => 'i' @@ -110,35 +110,35 @@ class UrlWriterTests < Test::Unit::TestCase W.new.url_for(:only_path => true, :controller => 'c', :action => 'a', :anchor => 'anchor') ) end - + def test_default_host add_host! assert_equal('http://www.basecamphq.com/c/a/i', W.new.url_for(:controller => 'c', :action => 'a', :id => 'i') ) end - + def test_host_may_be_overridden add_host! assert_equal('http://37signals.basecamphq.com/c/a/i', W.new.url_for(:host => '37signals.basecamphq.com', :controller => 'c', :action => 'a', :id => 'i') ) end - + def test_port add_host! assert_equal('http://www.basecamphq.com:3000/c/a/i', W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :port => 3000) ) end - + def test_protocol add_host! assert_equal('https://www.basecamphq.com/c/a/i', W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') ) end - + def test_protocol_with_and_without_separator add_host! assert_equal('https://www.basecamphq.com/c/a/i', @@ -184,15 +184,15 @@ class UrlWriterTests < Test::Unit::TestCase end def test_relative_url_root_is_respected - orig_relative_url_root = ActionController::AbstractRequest.relative_url_root - ActionController::AbstractRequest.relative_url_root = '/subdir' + orig_relative_url_root = ActionController::Base.relative_url_root + ActionController::Base.relative_url_root = '/subdir' add_host! assert_equal('https://www.basecamphq.com/subdir/c/a/i', W.new.url_for(:controller => 'c', :action => 'a', :id => 'i', :protocol => 'https') ) ensure - ActionController::AbstractRequest.relative_url_root = orig_relative_url_root + ActionController::Base.relative_url_root = orig_relative_url_root end def test_named_routes @@ -217,8 +217,8 @@ class UrlWriterTests < Test::Unit::TestCase end def test_relative_url_root_is_respected_for_named_routes - orig_relative_url_root = ActionController::AbstractRequest.relative_url_root - ActionController::AbstractRequest.relative_url_root = '/subdir' + orig_relative_url_root = ActionController::Base.relative_url_root + ActionController::Base.relative_url_root = '/subdir' ActionController::Routing::Routes.draw do |map| map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index' @@ -231,7 +231,7 @@ class UrlWriterTests < Test::Unit::TestCase controller.send(:home_url, :host => 'www.basecamphq.com', :user => 'again') ensure ActionController::Routing::Routes.load! - ActionController::AbstractRequest.relative_url_root = orig_relative_url_root + ActionController::Base.relative_url_root = orig_relative_url_root end def test_only_path @@ -239,14 +239,14 @@ class UrlWriterTests < Test::Unit::TestCase map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index' map.connect ':controller/:action/:id' end - + # We need to create a new class in order to install the new named route. kls = Class.new { include ActionController::UrlWriter } controller = kls.new assert controller.respond_to?(:home_url) assert_equal '/brave/new/world', controller.send(:url_for, :controller => 'brave', :action => 'new', :id => 'world', :only_path => true) - + assert_equal("/home/sweet/home/alabama", controller.send(:home_url, :user => 'alabama', :host => 'unused', :only_path => true)) assert_equal("/home/sweet/home/alabama", controller.send(:home_path, 'alabama')) ensure @@ -306,5 +306,4 @@ class UrlWriterTests < Test::Unit::TestCase def extract_params(url) url.split('?', 2).last.split('&') end - end diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 0e7f9a94b7..8410e82c3c 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -30,7 +30,6 @@ class AssetTagHelperTest < ActionView::TestCase end.new @request = Class.new do - def relative_url_root() "" end def protocol() 'http://' end def ssl?() false end def host_with_port() 'localhost' end @@ -118,7 +117,7 @@ class AssetTagHelperTest < ActionView::TestCase %(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) } PathToImageToTag = { @@ -173,7 +172,7 @@ class AssetTagHelperTest < ActionView::TestCase ActionView::Helpers::AssetTagHelper::register_javascript_include_default 'lib1', '/elsewhere/blub/lib2' assert_dom_equal %(\n\n\n\n\n\n\n), javascript_include_tag(:defaults) end - + def test_custom_javascript_expansions ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => ["head", "body", "tail"] assert_dom_equal %(\n\n\n\n), javascript_include_tag('first', :monkey, 'last') @@ -216,7 +215,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_image_path ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end - + def test_path_to_image_alias_for_image_path PathToImageToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end @@ -224,7 +223,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_image_tag ImageLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) } end - + def test_timebased_asset_id expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s assert_equal %(Rails), image_tag("rails.png") @@ -233,7 +232,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_should_skip_asset_id_on_complete_url assert_equal %(Rails), image_tag("http://www.example.com/rails.png") end - + def test_should_use_preset_asset_id ENV["RAILS_ASSET_ID"] = "4500" assert_equal %(Rails), image_tag("rails.png") @@ -255,14 +254,14 @@ class AssetTagHelperTest < ActionView::TestCase ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = 'http://a0.example.com' ActionController::Base.perform_caching = true - + assert_dom_equal( %(), javascript_include_tag(:all, :cache => true) ) assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) - + assert_dom_equal( %(), javascript_include_tag(:all, :cache => "money") @@ -344,7 +343,7 @@ class AssetTagHelperTest < ActionView::TestCase ensure FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js')) end - + def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = 'http://a0.example.com' @@ -390,7 +389,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_caching_javascript_include_tag_when_caching_off ENV["RAILS_ASSET_ID"] = "" ActionController::Base.perform_caching = false - + assert_dom_equal( %(\n\n\n\n\n\n\n), javascript_include_tag(:all, :cache => true) @@ -402,7 +401,7 @@ class AssetTagHelperTest < ActionView::TestCase ) assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) - + assert_dom_equal( %(\n\n\n\n\n\n\n), javascript_include_tag(:all, :cache => "money") @@ -420,7 +419,7 @@ class AssetTagHelperTest < ActionView::TestCase ENV["RAILS_ASSET_ID"] = "" ActionController::Base.asset_host = 'http://a0.example.com' ActionController::Base.perform_caching = true - + assert_dom_equal( %(), stylesheet_link_tag(:all, :cache => true) @@ -459,7 +458,7 @@ class AssetTagHelperTest < ActionView::TestCase def test_caching_stylesheet_include_tag_when_caching_off ENV["RAILS_ASSET_ID"] = "" ActionController::Base.perform_caching = false - + assert_dom_equal( %(\n\n), stylesheet_link_tag(:all, :cache => true) @@ -471,7 +470,7 @@ class AssetTagHelperTest < ActionView::TestCase ) assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) - + assert_dom_equal( %(\n\n), stylesheet_link_tag(:all, :cache => "money") @@ -490,6 +489,8 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase tests ActionView::Helpers::AssetTagHelper def setup + ActionController::Base.relative_url_root = "/collaboration/hieraki" + @controller = Class.new do attr_accessor :request @@ -497,22 +498,22 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase "http://www.example.com/collaboration/hieraki" end end.new - - @request = Class.new do - def relative_url_root - "/collaboration/hieraki" - end + @request = Class.new do def protocol 'gopher://' end end.new - + @controller.request = @request - + ActionView::Helpers::AssetTagHelper::reset_javascript_include_default end + def teardown + ActionController::Base.relative_url_root = nil + end + def test_should_compute_proper_path assert_dom_equal(%(), auto_discovery_link_tag) assert_dom_equal(%(/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr")) @@ -521,7 +522,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase assert_dom_equal(%(Mouse), image_tag("mouse.png", :mouseover => "/images/mouse_over.png")) assert_dom_equal(%(Mouse2), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png"))) end - + def test_should_ignore_relative_root_path_on_complete_url assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png")) end -- cgit v1.2.3 From 11fdcf88c2aea72ec84c5d4ab05986f5d35a9a81 Mon Sep 17 00:00:00 2001 From: Sam Granieri Date: Thu, 24 Jul 2008 13:51:54 -0500 Subject: Check for ActionMailer and ActionController before attempting to eager load their view paths Signed-off-by: Joshua Peek --- railties/lib/initializer.rb | 4 ++-- railties/test/initializer_test.rb | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 44863ab026..32411e8928 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -341,8 +341,8 @@ Run `rake gems:install` to install the missing gems. def load_view_paths ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes - ActionMailer::Base.template_root.load - ActionController::Base.view_paths.load + ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer) + ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller) end # Eager load application classes diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index dee7abe05f..07303a510e 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -136,8 +136,27 @@ uses_mocha 'framework paths' do end end - protected + def test_action_mailer_load_paths_set_only_if_action_mailer_in_use + @config.frameworks = [:action_controller] + initializer = Rails::Initializer.new @config + initializer.send :require_frameworks + + assert_nothing_raised NameError do + initializer.send :load_view_paths + end + end + def test_action_controller_load_paths_set_only_if_action_controller_in_use + @config.frameworks = [] + initializer = Rails::Initializer.new @config + initializer.send :require_frameworks + + assert_nothing_raised NameError do + initializer.send :load_view_paths + end + end + + protected def assert_framework_path(path) assert @config.framework_paths.include?(path), "<#{path.inspect}> not found among <#{@config.framework_paths.inspect}>" -- cgit v1.2.3 From f48b9ab5c2741ddbdbc0a9f4cd06875a1e3c8b02 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 24 Jul 2008 14:06:22 -0500 Subject: ActionController::Base.relative_url_root falls back to ENV['RAILS_RELATIVE_URL_ROOT'] --- actionpack/lib/action_controller/base.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index bae7e8c12e..5f4a38dac0 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -356,7 +356,12 @@ module ActionController #:nodoc: # If you are deploying to a subdirectory, you will need to set # config.action_controller.relative_url_root - class_inheritable_accessor :relative_url_root + # This defaults to ENV['RAILS_RELATIVE_URL_ROOT'] + cattr_writer :relative_url_root + + def self.relative_url_root + @@relative_url_root || ENV['RAILS_RELATIVE_URL_ROOT'] + end # Holds the request object that's primarily used to get environment variables through access like # request.env["REQUEST_URI"]. -- cgit v1.2.3 From e8fc894f66daa7909d1790f2cd145844d256d282 Mon Sep 17 00:00:00 2001 From: George Ogata Date: Wed, 23 Jul 2008 06:38:26 +1000 Subject: Make observers define #after_find in the model only if needed. [#676 state:resolved] Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/observer.rb | 3 +++ activerecord/test/cases/lifecycle_test.rb | 32 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb index c96e5f9d51..b35e407cc1 100644 --- a/activerecord/lib/active_record/observer.rb +++ b/activerecord/lib/active_record/observer.rb @@ -189,6 +189,9 @@ module ActiveRecord def add_observer!(klass) klass.add_observer(self) + if respond_to?(:after_find) && !klass.method_defined?(:after_find) + klass.class_eval 'def after_find() end' + end end end end diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb index 3432abee31..ab005c6b00 100755 --- a/activerecord/test/cases/lifecycle_test.rb +++ b/activerecord/test/cases/lifecycle_test.rb @@ -143,12 +143,12 @@ class LifecycleTest < ActiveRecord::TestCase assert_equal developer.name, multi_observer.record.name end - def test_after_find_cannot_be_observed_when_its_not_defined_on_the_model + def test_after_find_can_be_observed_when_its_not_defined_on_the_model observer = MinimalisticObserver.instance assert_equal Minimalistic, MinimalisticObserver.observed_class minimalistic = Minimalistic.find(1) - assert_nil observer.minimalistic + assert_equal minimalistic, observer.minimalistic end def test_after_find_can_be_observed_when_its_defined_on_the_model @@ -159,6 +159,34 @@ class LifecycleTest < ActiveRecord::TestCase assert_equal topic, observer.topic end + def test_after_find_is_not_created_if_its_not_used + # use a fresh class so an observer can't have defined an + # after_find on it + model_class = Class.new(ActiveRecord::Base) + observer_class = Class.new(ActiveRecord::Observer) + observer_class.observe(model_class) + + observer = observer_class.instance + + assert !model_class.method_defined?(:after_find) + end + + def test_after_find_is_not_clobbered_if_it_already_exists + # use a fresh observer class so we can instantiate it (Observer is + # a Singleton) + model_class = Class.new(ActiveRecord::Base) do + def after_find; end + end + original_method = model_class.instance_method(:after_find) + observer_class = Class.new(ActiveRecord::Observer) do + def after_find; end + end + observer_class.observe(model_class) + + observer = observer_class.instance + assert_equal original_method, model_class.instance_method(:after_find) + end + def test_invalid_observer assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers } end -- cgit v1.2.3 From 490178c93008c6fca20e3e2d6302a28d86aab94d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 27 Jul 2008 16:06:51 -0500 Subject: Revert "Ensure adapater specific code is loaded on ActiveRecord::Base.establish_connection" This reverts commit 8b858782fa693e89a47fc3dd5ae38d842ede6d04. --- .../connection_adapters/abstract/connection_specification.rb | 1 - 1 file changed, 1 deletion(-) 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 07b122efd1..2a8807fb78 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -211,7 +211,6 @@ module ActiveRecord clear_active_connection_name @active_connection_name = name @@defined_connections[name] = spec - connection when Symbol, String if configuration = configurations[spec.to_s] establish_connection(configuration) -- cgit v1.2.3 From f7abf0c9db61621b3f27061debd7983075cdca61 Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Sun, 27 Jul 2008 16:34:20 -0500 Subject: error_message_on takes an options hash instead of ordered parameters [#704 state:resolved] Signed-off-by: Joshua Peek --- .../action_view/helpers/active_record_helper.rb | 46 ++++++++++++++------- actionpack/lib/action_view/helpers/form_helper.rb | 4 +- .../test/template/active_record_helper_test.rb | 48 +++++++++++----------- 3 files changed, 58 insertions(+), 40 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index 959d8a8563..ff70ce9aae 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -25,7 +25,7 @@ module ActionView # Returns an entire form with all needed input tags for a specified Active Record object. For example, if @post # has attributes named +title+ of type +VARCHAR+ and +body+ of type +TEXT+ then # - # form("post") + # form("post") # # would yield a form like the following (modulus formatting): # @@ -90,23 +90,41 @@ module ActionView end # Returns a string containing the error message attached to the +method+ on the +object+ if one exists. - # This error message is wrapped in a DIV tag, which can be extended to include a +prepend_text+ and/or +append_text+ - # (to properly explain the error), and a +css_class+ to style it accordingly. +object+ should either be the name of an instance variable or - # the actual object. As an example, let's say you have a model @post that has an error message on the +title+ attribute: + # This error message is wrapped in a DIV tag, which can be extended to include a :prepend_text + # and/or :append_text (to properly explain the error), and a :css_class to style it + # accordingly. +object+ should either be the name of an instance variable or the actual object. The method can be + # passed in either as a string or a symbol. + # As an example, let's say you have a model @post that has an error message on the +title+ attribute: # # <%= error_message_on "post", "title" %> # # =>
can't be empty
# - # <%= error_message_on @post, "title" %> + # <%= error_message_on @post, :title %> # # =>
can't be empty
# - # <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %> - # # =>
Title simply can't be empty (or it won't work).
- def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError") + # <%= error_message_on "post", "title", + # :prepend_text => "Title simply ", + # :append_text => " (or it won't work).", + # :css_class => "inputError" %> + def error_message_on(object, method, *args) + options = args.extract_options! + unless args.empty? + ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate' + + 'prepend_text, append_text, and css_class arguments', caller) + + options[:prepend_text] = args[0] || '' + options[:append_text] = args[1] || '' + options[:css_class] = args[2] || 'formError' + end + options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') + if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && (errors = obj.errors.on(method)) - content_tag("div", "#{prepend_text}#{errors.is_a?(Array) ? errors.first : errors}#{append_text}", :class => css_class) - else + content_tag("div", + "#{options[:prepend_text]}#{errors.is_a?(Array) ? errors.first : errors}#{options[:append_text]}", + :class => options[:css_class] + ) + else '' end end @@ -133,7 +151,7 @@ module ActionView # # To specify the display for one object, you simply provide its name as a parameter. # For example, for the @user model: - # + # # error_messages_for 'user' # # To specify more than one object, you simply list them; optionally, you can add an extra :object_name parameter, which @@ -157,7 +175,7 @@ module ActionView else objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact end - + count = objects.inject(0) {|sum, object| sum + object.errors.count } unless count.zero? html = {} @@ -174,7 +192,7 @@ module ActionView I18n.with_options :locale => options[:locale], :scope => [:active_record, :error] do |locale| header_message = if options.include?(:header_message) options[:header_message] - else + else object_name = options[:object_name].to_s.gsub('_', ' ') object_name = I18n.t(object_name, :default => object_name) locale.t :header_message, :count => count, :object_name => object_name @@ -193,7 +211,7 @@ module ActionView '' end end - + private def all_input_tags(record, record_name, options) input_block = options[:input_block] || default_input_block diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index ada6fa2ea8..7bb82ba5bb 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -782,8 +782,8 @@ module ActionView @template.radio_button(@object_name, method, tag_value, objectify_options(options)) end - def error_message_on(method, prepend_text = "", append_text = "", css_class = "formError") - @template.error_message_on(@object, method, prepend_text, append_text, css_class) + def error_message_on(method, *args) + @template.error_message_on(@object, method, *args) end def error_messages(options = {}) diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index dfc30e651a..e46f95d18b 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -10,17 +10,17 @@ class ActiveRecordHelperTest < ActionView::TestCase alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) end - + User = Struct.new("User", :email) User.class_eval do alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast) end - + Column = Struct.new("Column", :type, :name, :human_name) end def setup_post - @post = Post.new + @post = Post.new def @post.errors Class.new { def on(field) @@ -33,12 +33,12 @@ class ActiveRecordHelperTest < ActionView::TestCase false end end - def empty?() false end - def count() 1 end + def empty?() false end + def count() 1 end def full_messages() [ "Author name can't be empty" ] end }.new end - + def @post.new_record?() true end def @post.to_param() nil end @@ -58,16 +58,16 @@ class ActiveRecordHelperTest < ActionView::TestCase end def setup_user - @user = User.new + @user = User.new def @user.errors Class.new { def on(field) field == "email" end - def empty?() false end - def count() 1 end + def empty?() false end + def count() 1 end def full_messages() [ "User email can't be empty" ] end }.new end - + def @user.new_record?() true end def @user.to_param() nil end @@ -81,7 +81,7 @@ class ActiveRecordHelperTest < ActionView::TestCase @user.email = "" end - + def protect_against_forgery? @protect_against_forgery ? true : false end @@ -92,7 +92,7 @@ class ActiveRecordHelperTest < ActionView::TestCase setup_user @response = ActionController::TestResponse.new - + @controller = Object.new def @controller.url_for(options) options = options.symbolize_keys @@ -111,7 +111,7 @@ class ActiveRecordHelperTest < ActionView::TestCase assert_dom_equal( %(
), text_area("post", "body") - ) + ) end def test_text_field_with_errors @@ -140,7 +140,7 @@ class ActiveRecordHelperTest < ActionView::TestCase form("post") ) end - + def test_form_with_protect_against_forgery @protect_against_forgery = true @request_forgery_protection_token = 'authenticity_token' @@ -150,7 +150,7 @@ class ActiveRecordHelperTest < ActionView::TestCase form("post") ) end - + def test_form_with_method_option assert_dom_equal( %(


\n


), @@ -211,9 +211,9 @@ class ActiveRecordHelperTest < ActionView::TestCase other_post = @post assert_dom_equal "
can't be empty
", error_message_on(other_post, :author_name) end - - def test_error_message_on_should_use_options - assert_dom_equal "
beforecan't be emptyafter
", error_message_on(:post, :author_name, "before", "after", "differentError") + + def test_error_message_on_with_options_hash + assert_dom_equal "
beforecan't be emptyafter
", error_message_on(:post, :author_name, :css_class => 'differentError', :prepend_text => 'before', :append_text => 'after') end def test_error_messages_for_many_objects @@ -224,10 +224,10 @@ class ActiveRecordHelperTest < ActionView::TestCase # add the default to put post back in the title assert_dom_equal %(

2 errors prohibited this post from being saved

There were problems with the following fields:

  • User email can't be empty
  • Author name can't be empty
), error_messages_for("user", "post", :object_name => "post") - + # symbols work as well assert_dom_equal %(

2 errors prohibited this post from being saved

There were problems with the following fields:

  • User email can't be empty
  • Author name can't be empty
), error_messages_for(:user, :post, :object_name => :post) - + # any default works too assert_dom_equal %(

2 errors prohibited this monkey from being saved

There were problems with the following fields:

  • User email can't be empty
  • Author name can't be empty
), error_messages_for(:user, :post, :object_name => "monkey") @@ -242,7 +242,7 @@ class ActiveRecordHelperTest < ActionView::TestCase message = "Please fix the following fields and resubmit:" assert_dom_equal %(

#{header_message}

#{message}

  • User email can't be empty
  • Author name can't be empty
), error_messages_for(:user, :post, :header_message => header_message, :message => message) end - + def test_error_messages_for_non_instance_variable actual_user = @user actual_post = @post @@ -251,14 +251,14 @@ class ActiveRecordHelperTest < ActionView::TestCase #explicitly set object assert_dom_equal %(

1 error prohibited this post from being saved

There were problems with the following fields:

  • Author name can't be empty
), error_messages_for("post", :object => actual_post) - + #multiple objects assert_dom_equal %(

2 errors prohibited this user from being saved

There were problems with the following fields:

  • User email can't be empty
  • Author name can't be empty
), error_messages_for("user", "post", :object => [actual_user, actual_post]) - + #nil object assert_equal '', error_messages_for('user', :object => nil) end - + def test_form_with_string_multipart assert_dom_equal( %(


\n


), -- cgit v1.2.3 From 10d9fe4bf3110c1d5de0c6b509fe0cbb9d5eda1d Mon Sep 17 00:00:00 2001 From: Clemens Kofler Date: Sun, 27 Jul 2008 16:49:19 -0500 Subject: Refactored TextHelper#truncate, highlight, excerpt, word_wrap and auto_link to accept options hash [#705 state:resolved] Signed-off-by: Joshua Peek --- .../action_view/helpers/active_record_helper.rb | 2 +- actionpack/lib/action_view/helpers/text_helper.rb | 206 +++++++++++++++------ actionpack/test/template/text_helper_test.rb | 51 ++++- 3 files changed, 191 insertions(+), 68 deletions(-) diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index ff70ce9aae..fce03ff605 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -109,7 +109,7 @@ module ActionView def error_message_on(object, method, *args) options = args.extract_options! unless args.empty? - ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate' + + ActiveSupport::Deprecation.warn('error_message_on takes an option hash instead of separate ' + 'prepend_text, append_text, and css_class arguments', caller) options[:prepend_text] = args[0] || '' diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 9342b38680..3c9f7230c3 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -34,40 +34,69 @@ module ActionView end if RUBY_VERSION < '1.9' - # If +text+ is longer than +length+, +text+ will be truncated to the length of - # +length+ (defaults to 30) and the last characters will be replaced with the +truncate_string+ - # (defaults to "..."). + # Truncates a given +text+ after a given :length if +text+ is longer than :length + # (defaults to 30). The last characters will be replaced with the :omission (defaults to "..."). # # ==== Examples - # truncate("Once upon a time in a world far far away", 14) - # # => Once upon a... # # truncate("Once upon a time in a world far far away") # # => Once upon a time in a world f... # - # truncate("And they found that many people were sleeping better.", 25, "(clipped)") + # truncate("Once upon a time in a world far far away", :length => 14) + # # => Once upon a... + # + # truncate("And they found that many people were sleeping better.", :length => 25, "(clipped)") # # => And they found that many (clipped) # + # truncate("And they found that many people were sleeping better.", :omission => "... (continued)", :length => 15) + # # => And they found... (continued) + # + # You can still use truncate with the old API that accepts the + # +length+ as its optional second and the +ellipsis+ as its + # optional third parameter: + # truncate("Once upon a time in a world far far away", 14) + # # => Once upon a time in a world f... + # # truncate("And they found that many people were sleeping better.", 15, "... (continued)") # # => And they found... (continued) - def truncate(text, length = 30, truncate_string = "...") + def truncate(text, *args) + options = args.extract_options! + unless args.empty? + ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' + + 'length and omission arguments', caller) + + options[:length] = args[0] || 30 + options[:omission] = args[1] || "..." + end + options.reverse_merge!(:length => 30, :omission => "...") + if text - l = length - truncate_string.chars.length + l = options[:length] - options[:omission].chars.length chars = text.chars - (chars.length > length ? chars[0...l] + truncate_string : text).to_s + (chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s end end else - def truncate(text, length = 30, truncate_string = "...") #:nodoc: + def truncate(text, *args) #:nodoc: + options = args.extract_options! + unless args.empty? + ActiveSupport::Deprecation.warn('truncate takes an option hash instead of separate ' + + 'length and omission arguments', caller) + + options[:length] = args[0] || 30 + options[:omission] = args[1] || "..." + end + options.reverse_merge!(:length => 30, :omission => "...") + if text - l = length - truncate_string.length - (text.length > length ? text[0...l] + truncate_string : text).to_s + l = options[:length].to_i - options[:omission].length + (text.length > options[:length].to_i ? text[0...l] + options[:omission] : text).to_s end end end # Highlights one or more +phrases+ everywhere in +text+ by inserting it into - # a +highlighter+ string. The highlighter can be specialized by passing +highlighter+ + # a :highlighter string. The highlighter can be specialized by passing :highlighter # as a single-quoted string with \1 where the phrase is to be inserted (defaults to # '\1') # @@ -78,52 +107,75 @@ module ActionView # highlight('You searched for: ruby, rails, dhh', 'actionpack') # # => You searched for: ruby, rails, dhh # - # highlight('You searched for: rails', ['for', 'rails'], '\1') + # highlight('You searched for: rails', ['for', 'rails'], :highlighter => '\1') # # => You searched for: rails # - # highlight('You searched for: rails', 'rails', "\1") - # # => You searched for: \1') + # highlight('You searched for: rails', 'rails', :highlighter => '\1') + # # => You searched for: rails + # + # You can still use highlight with the old API that accepts the + # +highlighter+ as its optional third parameter: + # highlight('You searched for: rails', 'rails', '\1') # => You searched for: rails + def highlight(text, phrases, *args) + options = args.extract_options! + unless args.empty? + options[:highlighter] = args[0] || '\1' + end + options.reverse_merge!(:highlighter => '\1') + if text.blank? || phrases.blank? text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') - text.gsub(/(#{match})/i, highlighter) + text.gsub(/(#{match})/i, options[:highlighter]) end end if RUBY_VERSION < '1.9' # Extracts an excerpt from +text+ that matches the first instance of +phrase+. - # The +radius+ expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters - # defined in +radius+ (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+, - # then the +excerpt_string+ will be prepended/appended accordingly. The resulting string will be stripped in any case. - # If the +phrase+ isn't found, nil is returned. + # The :radius option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters + # defined in :radius (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+, + # then the :omission option (which defaults to "...") will be prepended/appended accordingly. The resulting string + # will be stripped in any case. If the +phrase+ isn't found, nil is returned. # # ==== Examples - # excerpt('This is an example', 'an', 5) - # # => "...s is an exam..." + # excerpt('This is an example', 'an', :radius => 5) + # # => ...s is an exam... # - # excerpt('This is an example', 'is', 5) - # # => "This is a..." + # excerpt('This is an example', 'is', :radius => 5) + # # => This is a... # # excerpt('This is an example', 'is') - # # => "This is an example" + # # => This is an example # - # excerpt('This next thing is an example', 'ex', 2) - # # => "...next..." + # excerpt('This next thing is an example', 'ex', :radius => 2) + # # => ...next... # - # excerpt('This is also an example', 'an', 8, ' ') - # # => " is also an example" - def excerpt(text, phrase, radius = 100, excerpt_string = "...") + # excerpt('This is also an example', 'an', :radius => 8, :omission => ' ') + # # => is also an example + # + # You can still use excerpt with the old API that accepts the + # +radius+ as its optional third and the +ellipsis+ as its + # optional forth parameter: + # excerpt('This is an example', 'an', 5) # => ...s is an exam... + # excerpt('This is also an example', 'an', 8, ' ') # => is also an example + def excerpt(text, phrase, *args) + options = args.extract_options! + unless args.empty? + options[:radius] = args[0] || 100 + options[:omission] = args[1] || "..." + end + options.reverse_merge!(:radius => 100, :omission => "...") + if text && phrase phrase = Regexp.escape(phrase) if found_pos = text.chars =~ /(#{phrase})/i - start_pos = [ found_pos - radius, 0 ].max - end_pos = [ [ found_pos + phrase.chars.length + radius - 1, 0].max, text.chars.length ].min + start_pos = [ found_pos - options[:radius], 0 ].max + end_pos = [ [ found_pos + phrase.chars.length + options[:radius] - 1, 0].max, text.chars.length ].min - prefix = start_pos > 0 ? excerpt_string : "" - postfix = end_pos < text.chars.length - 1 ? excerpt_string : "" + prefix = start_pos > 0 ? options[:omission] : "" + postfix = end_pos < text.chars.length - 1 ? options[:omission] : "" prefix + text.chars[start_pos..end_pos].strip + postfix else @@ -132,16 +184,23 @@ module ActionView end end else - def excerpt(text, phrase, radius = 100, excerpt_string = "...") #:nodoc: + def excerpt(text, phrase, *args) #:nodoc: + options = args.extract_options! + unless args.empty? + options[:radius] = args[0] || 100 + options[:omission] = args[1] || "..." + end + options.reverse_merge!(:radius => 100, :omission => "...") + if text && phrase phrase = Regexp.escape(phrase) if found_pos = text =~ /(#{phrase})/i - start_pos = [ found_pos - radius, 0 ].max - end_pos = [ [ found_pos + phrase.length + radius - 1, 0].max, text.length ].min + start_pos = [ found_pos - options[:radius], 0 ].max + end_pos = [ [ found_pos + phrase.length + options[:radius] - 1, 0].max, text.length ].min - prefix = start_pos > 0 ? excerpt_string : "" - postfix = end_pos < text.length - 1 ? excerpt_string : "" + prefix = start_pos > 0 ? options[:omission] : "" + postfix = end_pos < text.length - 1 ? options[:omission] : "" prefix + text[start_pos..end_pos].strip + postfix else @@ -176,20 +235,31 @@ module ActionView # (which is 80 by default). # # ==== Examples - # word_wrap('Once upon a time', 4) - # # => Once\nupon\na\ntime - # - # word_wrap('Once upon a time', 8) - # # => Once upon\na time # # word_wrap('Once upon a time') # # => Once upon a time # - # word_wrap('Once upon a time', 1) + # word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...') + # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\n a successor to the throne turned out to be more trouble than anyone could have\n imagined... + # + # word_wrap('Once upon a time', :line_width => 8) + # # => Once upon\na time + # + # word_wrap('Once upon a time', :line_width => 1) # # => Once\nupon\na\ntime - def word_wrap(text, line_width = 80) + # + # You can still use word_wrap with the old API that accepts the + # +line_width+ as its optional second parameter: + # word_wrap('Once upon a time', 8) # => Once upon\na time + def word_wrap(text, *args) + options = args.extract_options! + unless args.blank? + options[:line_width] = args[0] || 80 + end + options.reverse_merge!(:line_width => 80) + text.split("\n").collect do |line| - line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line + line.length > options[:line_width] ? line.gsub(/(.{1,#{options[:line_width]}})(\s+|$)/, "\\1\n").strip : line end * "\n" end @@ -336,12 +406,32 @@ module ActionView # # => "Welcome to my new blog at http://www.m.... # Please e-mail me at me@email.com." # - def auto_link(text, link = :all, href_options = {}, &block) + # + # You can still use auto_link with the old API that accepts the + # +link+ as its optional second parameter and the +html_options+ hash + # as its optional third parameter: + # post_body = "Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com." + # auto_link(post_body, :urls) # => Once upon\na time + # # => "Welcome to my new blog at http://www.myblog.com. + # Please e-mail me at me@email.com." + # + # auto_link(post_body, :all, :target => "_blank") # => Once upon\na time + # # => "Welcome to my new blog at http://www.myblog.com. + # Please e-mail me at me@email.com." + def auto_link(text, *args, &block)#link = :all, href_options = {}, &block) return '' if text.blank? - case link - when :all then auto_link_email_addresses(auto_link_urls(text, href_options, &block), &block) - when :email_addresses then auto_link_email_addresses(text, &block) - when :urls then auto_link_urls(text, href_options, &block) + + options = args.size == 2 ? {} : args.extract_options! # this is necessary because the old auto_link API has a Hash as its last parameter + unless args.empty? + options[:link] = args[0] || :all + options[:html] = args[1] || {} + end + options.reverse_merge!(:link => :all, :html => {}) + + case options[:link].to_sym + when :all then auto_link_email_addresses(auto_link_urls(text, options[:html], &block), &block) + when :email_addresses then auto_link_email_addresses(text, &block) + when :urls then auto_link_urls(text, options[:html], &block) end end @@ -468,7 +558,7 @@ module ActionView [-\w]+ # subdomain or domain (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port - (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$])))*)* # path + (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:'][^\s$]))+)?)* # path (?:\?[\w\+@%&=.;-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) @@ -477,8 +567,8 @@ module ActionView # Turns all urls into clickable links. If a block is given, each url # is yielded and the result is used as the link text. - def auto_link_urls(text, href_options = {}) - extra_options = tag_options(href_options.stringify_keys) || "" + def auto_link_urls(text, html_options = {}) + extra_options = tag_options(html_options.stringify_keys) || "" text.gsub(AUTO_LINK_RE) do all, a, b, c, d = $&, $1, $2, $3, $4 if a =~ / 12) + assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12) end def test_truncate_should_use_default_length_of_30 @@ -44,23 +44,29 @@ class TextHelperTest < ActionView::TestCase assert_equal str[0...27] + "...", truncate(str) end + def test_truncate_with_options_hash + assert_equal "This is a string that wil[...]", truncate("This is a string that will go longer then the default truncate length of 30", :omission => "[...]") + assert_equal "Hello W...", truncate("Hello World!", :length => 10) + assert_equal "Hello[...]", truncate("Hello World!", :omission => "[...]", :length => 10) + end + if RUBY_VERSION < '1.9.0' def test_truncate_multibyte with_kcode 'none' do - assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10) + assert_equal "\354\225\210\353\205\225\355...", truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) end with_kcode 'u' do assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...", - truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", 10) + truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244", :length => 10) end end else def test_truncate_multibyte assert_equal "\354\225\210\353\205\225\355...", - truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", 10) + truncate("\354\225\210\353\205\225\355\225\230\354\204\270\354\232\224", :length => 10) assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), - truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), 10) + truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10) end end @@ -88,7 +94,7 @@ class TextHelperTest < ActionView::TestCase assert_equal ' ', highlight(' ', 'blank text is returned verbatim') end - def test_highlighter_with_regexp + def test_highlight_with_regexp assert_equal( "This is a beautiful! morning", highlight("This is a beautiful! morning", "beautiful!") @@ -105,10 +111,17 @@ class TextHelperTest < ActionView::TestCase ) end - def test_highlighting_multiple_phrases_in_one_pass + def test_highlight_with_multiple_phrases_in_one_pass assert_equal %(wow em), highlight('wow em', %w(wow em), '\1') end + def test_highlight_with_options_hash + assert_equal( + "This is a beautiful morning, but also a beautiful day", + highlight("This is a beautiful morning, but also a beautiful day", "beautiful", :highlighter => '\1') + ) + end + def test_excerpt assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", 5)) assert_equal("This is a...", excerpt("This is a beautiful morning", "this", 5)) @@ -138,6 +151,16 @@ class TextHelperTest < ActionView::TestCase assert_equal('...is a beautiful? mor...', excerpt('This is a beautiful? morning', 'beautiful', 5)) end + def test_excerpt_with_options_hash + assert_equal("...is a beautiful morn...", excerpt("This is a beautiful morning", "beautiful", :radius => 5)) + assert_equal("[...]is a beautiful morn[...]", excerpt("This is a beautiful morning", "beautiful", :omission => "[...]",:radius => 5)) + assert_equal( + "This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome tempera[...]", + excerpt("This is the ultimate supercalifragilisticexpialidoceous very looooooooooooooooooong looooooooooooong beautiful morning with amazing sunshine and awesome temperatures. So what are you gonna do about it?", "very", + :omission => "[...]") + ) + end + if RUBY_VERSION < '1.9' def test_excerpt_with_utf8 with_kcode('u') do @@ -162,6 +185,10 @@ class TextHelperTest < ActionView::TestCase assert_equal("my very very\nvery long\nstring\n\nwith another\nline", word_wrap("my very very very long string\n\nwith another line", 15)) end + def test_word_wrap_with_options_hash + assert_equal("my very very\nvery long\nstring", word_wrap("my very very very long string", :line_width => 15)) + end + def test_pluralization assert_equal("1 count", pluralize(1, "count")) assert_equal("2 counts", pluralize(2, "count")) @@ -285,7 +312,13 @@ class TextHelperTest < ActionView::TestCase url = "http://api.rubyonrails.com/Foo.html" email = "fantabulous@shiznadel.ic" - assert_equal %(

#{url[0...7]}...
#{email[0...7]}...

), auto_link("

#{url}
#{email}

") { |url| truncate(url, 10) } + assert_equal %(

#{url[0...7]}...
#{email[0...7]}...

), auto_link("

#{url}
#{email}

") { |url| truncate(url, :length => 10) } + end + + def test_auto_link_with_options_hash + assert_equal 'Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.', + auto_link("Welcome to my new blog at http://www.myblog.com/. Please e-mail me at me@email.com.", + :link => :all, :html => { :class => "menu", :target => "_blank" }) end def test_cycle_class -- cgit v1.2.3