diff options
91 files changed, 743 insertions, 471 deletions
diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index 6e2c441e53..f0b9368874 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,5 +1,7 @@ *Rails 3.0.0 [beta 4] (June 8th, 2010)* +* subject is automatically looked up on I18n using mailer_name and action_name as scope as in t(".subject") [JK] + * Changed encoding behaviour of mail, so updated tests in actionmailer and bumped mail version to 2.2.1 [ML] * Added ability to pass Proc objects to the defaults hash [ML] diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index f0b2604a66..fff44e50c1 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -209,7 +209,7 @@ module ActionMailer #:nodoc: # # <%= image_tag attachments['photo.png'].url -%> # - # As we are using ActionView's +image_tag+ method, you can pass in any other options you want: + # As we are using Action View's +image_tag+ method, you can pass in any other options you want: # # <h1>Please Don't Cringe</h1> # @@ -657,7 +657,7 @@ module ActionMailer #:nodoc: def default_i18n_subject #:nodoc: mailer_scope = self.class.mailer_name.gsub('/', '.') - I18n.t(:subject, :scope => [:actionmailer, mailer_scope, action_name], :default => action_name.humanize) + I18n.t(:subject, :scope => [mailer_scope, action_name], :default => action_name.humanize) end def collect_responses_and_parts_order(headers) #:nodoc: @@ -739,7 +739,7 @@ module ActionMailer #:nodoc: end # This module will complain if the user tries to set default_url_options - # directly instead of through the config object. In ActionMailer's Railtie, + # directly instead of through the config object. In Action Mailer's Railtie, # we include the url_helpers of the router, which will override this module extend DeprecatedUrlOptions diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb index d4874c6dbf..b91eed592a 100644 --- a/actionmailer/lib/action_mailer/test_case.rb +++ b/actionmailer/lib/action_mailer/test_case.rb @@ -8,55 +8,69 @@ module ActionMailer end class TestCase < ActiveSupport::TestCase - include TestHelper + module Behavior + extend ActiveSupport::Concern - setup :initialize_test_deliveries - setup :set_expected_mail + include TestHelper - class << self - def tests(mailer) - write_inheritable_attribute(:mailer_class, mailer) - end + module ClassMethods + def tests(mailer) + write_inheritable_attribute(:mailer_class, mailer) + end - def mailer_class - if mailer = read_inheritable_attribute(:mailer_class) - mailer - else - tests determine_default_mailer(name) + def mailer_class + if mailer = read_inheritable_attribute(:mailer_class) + mailer + else + tests determine_default_mailer(name) + end end - end - def determine_default_mailer(name) - name.sub(/Test$/, '').constantize - rescue NameError => e - raise NonInferrableMailerError.new(name) + def determine_default_mailer(name) + name.sub(/Test$/, '').constantize + rescue NameError => e + raise NonInferrableMailerError.new(name) + end end - end - protected - def initialize_test_deliveries - ActionMailer::Base.delivery_method = :test - ActionMailer::Base.perform_deliveries = true - ActionMailer::Base.deliveries.clear - end + module InstanceMethods - def set_expected_mail - @expected = Mail.new - @expected.content_type ["text", "plain", { "charset" => charset }] - @expected.mime_version = '1.0' - end + protected - private - def charset - "UTF-8" - end + def initialize_test_deliveries + ActionMailer::Base.delivery_method = :test + ActionMailer::Base.perform_deliveries = true + ActionMailer::Base.deliveries.clear + end + + def set_expected_mail + @expected = Mail.new + @expected.content_type ["text", "plain", { "charset" => charset }] + @expected.mime_version = '1.0' + end + + private + + def charset + "UTF-8" + end + + def encode(subject) + Mail::Encodings.q_value_encode(subject, charset) + end - def encode(subject) - Mail::Encodings.q_value_encode(subject, charset) + def read_fixture(action) + IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action)) + end end - def read_fixture(action) - IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action)) + included do + setup :initialize_test_deliveries + setup :set_expected_mail end + end + + include Behavior + end end diff --git a/actionmailer/lib/action_mailer/test_helper.rb b/actionmailer/lib/action_mailer/test_helper.rb index 3a1612442f..5beab87ad2 100644 --- a/actionmailer/lib/action_mailer/test_helper.rb +++ b/actionmailer/lib/action_mailer/test_helper.rb @@ -1,5 +1,7 @@ module ActionMailer module TestHelper + extend ActiveSupport::Concern + # Asserts that the number of emails sent matches the given number. # # def test_emails @@ -57,11 +59,3 @@ module ActionMailer end end end - -module Test - module Unit - class TestCase - include ActionMailer::TestHelper - end - end -end diff --git a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb index 7343eb28b3..21e5918ecb 100644 --- a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb +++ b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb @@ -5,7 +5,7 @@ class <%= class_name %> < ActionMailer::Base # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # - # en.actionmailer.<%= file_name %>.<%= action %>.subject + # en.<%= file_name %>.<%= action %>.subject # def <%= action %> @greeting = "Hi" diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index b9226196fd..88e38a583b 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -325,7 +325,7 @@ class BaseTest < ActiveSupport::TestCase email = BaseMailer.welcome(:subject => nil) assert_equal "Welcome", email.subject - I18n.backend.store_translations('en', :actionmailer => {:base_mailer => {:welcome => {:subject => "New Subject!"}}}) + I18n.backend.store_translations('en', :base_mailer => {:welcome => {:subject => "New Subject!"}}) email = BaseMailer.welcome(:subject => nil) assert_equal "New Subject!", email.subject end diff --git a/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb index e8e8fcedc9..527b37218a 100644 --- a/actionmailer/test/old_base/mail_service_test.rb +++ b/actionmailer/test/old_base/mail_service_test.rb @@ -1113,6 +1113,8 @@ class InheritableTemplateRootTest < ActiveSupport::TestCase end class MethodNamingTest < ActiveSupport::TestCase + include ActionMailer::TestHelper + class TestMailer < ActionMailer::Base def send body 'foo' diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 562b1dba3d..967bd76025 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,7 +1,10 @@ -Rails 3.0.0 [Release Candidate] (unreleased)* +*Rails 3.0.0 [Release Candidate] (unreleased)* + +* Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 [Guillermo Álvarez] * Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino] + *Rails 3.0.0 [beta 4] (June 8th, 2010)* * Remove middleware laziness [José Valim] diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 02ff908c1c..8e95315252 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |s| s.add_dependency('i18n', '~> 0.4.1') s.add_dependency('rack', '~> 1.1.0') s.add_dependency('rack-test', '~> 0.5.4') - s.add_dependency('rack-mount', '~> 0.6.3') + s.add_dependency('rack-mount', '~> 0.6.4') s.add_dependency('tzinfo', '~> 0.3.16') s.add_dependency('erubis', '~> 2.6.5') end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index c14393dda7..1bd4572a47 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -67,7 +67,7 @@ end require 'action_view' require 'action_controller/vendor/html-scanner' -# Common ActiveSupport usage in ActionController +# Common Active Support usage in Action Controller require 'active_support/concern' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/load_error' diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb index ba38b186d6..b2c119d7e4 100644 --- a/actionpack/lib/action_controller/metal/instrumentation.rb +++ b/actionpack/lib/action_controller/metal/instrumentation.rb @@ -2,7 +2,7 @@ require 'abstract_controller/logger' module ActionController # Adds instrumentation to several ends in ActionController::Base. It also provides - # some hooks related with process_action, this allows an ORM like ActiveRecord + # some hooks related with process_action, this allows an ORM like Active Record # and/or DataMapper to plug in ActionController and show related information. # # Check ActiveRecord::Railties::ControllerRuntime for an example. diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 87e8dd5010..d69ba39728 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -45,7 +45,16 @@ module ActionDispatch # * <tt>:value</tt> - The cookie's value or list of values (as an array). # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root # of the application. - # * <tt>:domain</tt> - The domain for which this cookie applies. + # * <tt>:domain</tt> - The domain for which this cookie applies so you can + # restrict to the domain level. If you use a schema like www.example.com + # and want to share session with user.example.com set <tt>:domain</tt> + # to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with + # <tt>:all</tt> again when deleting keys. + # + # :domain => nil # Does not sets cookie domain. (default) + # :domain => :all # Allow the cookie for the top most level + # domain and subdomains. + # # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object. # * <tt>:secure</tt> - Whether this cookie is a only transmitted to HTTPS servers. # Default is +false+. @@ -54,22 +63,34 @@ module ActionDispatch class Cookies HTTP_HEADER = "Set-Cookie".freeze TOKEN_KEY = "action_dispatch.secret_token".freeze - + # Raised when storing more than 4K of session data. class CookieOverflow < StandardError; end class CookieJar < Hash #:nodoc: + + # This regular expression is used to split the levels of a domain + # So www.example.co.uk gives: + # $1 => www. + # $2 => example + # $3 => co.uk + DOMAIN_REGEXP = /^(.*\.)*(.*)\.(...|...\...|....|..\...|..)$/ + def self.build(request) secret = request.env[TOKEN_KEY] - new(secret).tap do |hash| + host = request.env["HTTP_HOST"] + + new(secret, host).tap do |hash| hash.update(request.cookies) end end - def initialize(secret=nil) + def initialize(secret = nil, host = nil) @secret = secret @set_cookies = {} @delete_cookies = {} + @host = host + super() end @@ -78,6 +99,15 @@ module ActionDispatch super(name.to_s) end + def handle_options(options) #:nodoc: + options[:path] ||= "/" + + if options[:domain] == :all + @host =~ DOMAIN_REGEXP + options[:domain] = ".#{$2}.#{$3}" + end + end + # Sets the cookie named +name+. The second argument may be the very cookie # value, or a hash of options as documented above. def []=(key, options) @@ -91,7 +121,8 @@ module ActionDispatch value = super(key.to_s, value) - options[:path] ||= "/" + handle_options(options) + @set_cookies[key] = options @delete_cookies.delete(key) value @@ -102,7 +133,9 @@ module ActionDispatch # an options hash to delete cookies with extra data such as a <tt>:path</tt>. def delete(key, options = {}) options.symbolize_keys! - options[:path] ||= "/" + + handle_options(options) + value = super(key.to_s) @delete_cookies[key] = options value diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 5fa1b5619b..4d06ca0d89 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/array/wrap' +require 'active_support/ordered_options' module ActionView #:nodoc: class NonConcattingString < ActiveSupport::SafeBuffer diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb index 61d2e702a7..88efd4b34f 100644 --- a/actionpack/lib/action_view/context.rb +++ b/actionpack/lib/action_view/context.rb @@ -3,8 +3,8 @@ module ActionView # holds compiled template code end - # ActionView contexts are supplied to ActionController - # to render template. The default ActionView context + # Action View contexts are supplied to Action Controller + # to render template. The default Action View context # is ActionView::Base. # # In order to work with ActionController, a Context @@ -21,7 +21,7 @@ module ActionView # options<Hash>:: See _render_template_with_layout in ActionView::Base # partial<Boolean>:: Whether or not the template to render is a partial # - # An ActionView context can also mix in ActionView's + # An Action View context can also mix in Action View's # helpers. In order to mix in helpers, a context must # implement: # diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb index 38e56d8bff..b322bbad34 100644 --- a/actionpack/lib/action_view/helpers/number_helper.rb +++ b/actionpack/lib/action_view/helpers/number_helper.rb @@ -96,7 +96,7 @@ module ActionView # number_to_currency(1234567890.50) # => $1,234,567,890.50 # number_to_currency(1234567890.506) # => $1,234,567,890.51 # number_to_currency(1234567890.506, :precision => 3) # => $1,234,567,890.506 - # number_to_currency(1234567890.506, :locale => :fr) # => 1,234,567,890.506 € + # number_to_currency(1234567890.506, :locale => :fr) # => 1 234 567 890,506 € # # number_to_currency(1234567890.50, :unit => "£", :separator => ",", :delimiter => "") # # => £1234567890,50 @@ -134,6 +134,7 @@ module ActionView # format in the +options+ hash. # # ==== Options + # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale). # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3). # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+) # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to "."). @@ -145,6 +146,7 @@ module ActionView # number_to_percentage(100, :precision => 0) # => 100% # number_to_percentage(1000, :delimiter => '.', :separator => ',') # => 1.000,000% # number_to_percentage(302.24398923423, :precision => 5) # => 302.24399% + # number_to_percentage(1000, :locale => :fr) # => 1 000,000% def number_to_percentage(number, options = {}) return nil if number.nil? @@ -171,6 +173,7 @@ module ActionView # customize the format in the +options+ hash. # # ==== Options + # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale). # * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to ","). # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to "."). # @@ -179,6 +182,7 @@ module ActionView # number_with_delimiter(12345678.05) # => 12,345,678.05 # number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678 # number_with_delimiter(12345678, :separator => ",") # => 12,345,678 + # number_with_delimiter(12345678.05, :locale => :fr) # => 12 345 678,05 # number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",") # # => 98 765 432,98 # @@ -223,6 +227,7 @@ module ActionView # You can customize the format in the +options+ hash. # # ==== Options + # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale). # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3). # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +false+) # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to "."). @@ -237,6 +242,7 @@ module ActionView # number_with_precision(111.2345, :significant => true) # => 111 # number_with_precision(111.2345, :precision => 1, :significant => true) # => 100 # number_with_precision(13, :precision => 5, :significant => true) # => 13.000 + # number_with_precision(111.234, :locale => :fr) # => 111,234 # number_with_precision(13, :precision => 5, :significant => true, strip_insignificant_zeros => true) # # => 13 # number_with_precision(389.32314, :precision => 4, :significant => true) # => 389.3 @@ -309,6 +315,7 @@ module ActionView # See <tt>number_to_human</tt> if you want to pretty-print a generic number. # # ==== Options + # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale). # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3). # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+) # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to "."). @@ -395,6 +402,7 @@ module ActionView # a wide range of unit quantifiers, even fractional ones (centi, deci, mili, etc). # # ==== Options + # * <tt>:locale</tt> - Sets the locale to be used for formatting (defaults to current locale). # * <tt>:precision</tt> - Sets the precision of the number (defaults to 3). # * <tt>:significant</tt> - If +true+, precision will be the # of significant_digits. If +false+, the # of fractional digits (defaults to +true+) # * <tt>:separator</tt> - Sets the separator between the fractional and integer digits (defaults to "."). diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index 28e40f8560..f173523f6a 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -4,7 +4,7 @@ require 'action_view/helpers/tag_helper' module ActionView module Helpers #:nodoc: # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements. - # These helper methods extend ActionView making them callable within your template files. + # These helper methods extend Action View making them callable within your template files. module SanitizeHelper # This +sanitize+ helper will html encode all tags and strip all attributes that aren't specifically allowed. # It also strips href/src tags with invalid protocols, like javascript: especially. It does its best to counter any diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index c8533c217b..ccc9156777 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -6,7 +6,7 @@ module ActionView module Helpers #:nodoc: # The TextHelper module provides a set of methods for filtering, formatting # and transforming strings, which can reduce the amount of inline Ruby code in - # your views. These helper methods extend ActionView making them callable + # your views. These helper methods extend Action View making them callable # within your template files. module TextHelper # The preferred method of outputting text in your views is to use the @@ -39,6 +39,7 @@ module ActionView # for a total length not exceeding <tt>:length</tt>. # # Pass a <tt>:separator</tt> to truncate +text+ at a natural break. + # Pass a <tt>:safe</tt> value as "true" to not to escape the content. # # ==== Examples # @@ -54,6 +55,15 @@ module ActionView # truncate("And they found that many people were sleeping better.", :length => 25, :omission => '... (continued)') # # => "And they f... (continued)" # + # truncate("<p>Once upon a time in a world far far away</p>") + # # => "<p>Once upon a time i..." + # + # truncate("<p>Once upon a time in a world far far away</p>", :safe => true) + # # => "<p>Once upon a time in a wo..." + # + # truncate("<p>Once upon a time in a world far far away</p>".html_safe) + # # => "<p>Once upon a time in a wo..." + # # You can still use <tt>truncate</tt> with the old API that accepts the # +length+ as its optional second and the +ellipsis+ as its # optional third parameter: @@ -74,7 +84,7 @@ module ActionView options.reverse_merge!(:length => 30) - text = sanitize(text) unless text.html_safe? || options[:safe] + text = h(text) unless text.html_safe? || options[:safe] text.truncate(options.delete(:length), options) if text end @@ -106,7 +116,7 @@ module ActionView end options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>') - text = sanitize(text) unless text.html_safe? || options[:safe] + text = h(text) unless text.html_safe? || options[:safe] if text.blank? || phrases.blank? text else @@ -244,7 +254,7 @@ module ActionView def simple_format(text, html_options={}, options={}) text = '' if text.nil? start_tag = tag('p', html_options, true) - text = sanitize(text) unless text.html_safe? || options[:safe] + text = h(text) unless text.html_safe? || options[:safe] text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br @@ -503,7 +513,7 @@ module ActionView text.html_safe else display_text = (block_given?) ? yield(text) : text - display_text = sanitize(display_text) unless options[:safe] + display_text = h(display_text) unless options[:safe] mail_to text, display_text, html_options end end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index 4198013f57..4d35296932 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -56,7 +56,7 @@ module ActionView :identifier => template.identifier, :layout => layout.try(:virtual_path)) do content = template.render(self, locals) { |*name| _layout_for(*name) } - @_content_for[:layout] = content + @_content_for[:layout] = content if layout content = _render_layout(layout, locals) if layout content diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 01c8fd90a5..5c636cbab8 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -248,7 +248,7 @@ class FlashIntegrationTest < ActionController::IntegrationTest with_routing do |set| set.draw do |map| match ':action', :to => ActionDispatch::Session::CookieStore.new( - FlashIntegrationTest::TestController, :key => SessionKey, :secret => SessionSecret + FlashIntegrationTest::TestController, :key => FlashIntegrationTest::SessionKey, :secret => FlashIntegrationTest::SessionSecret ) end yield diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 75a96d6497..c9a6e5dd45 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -122,7 +122,7 @@ class HelperTest < ActiveSupport::TestCase def test_helper_proxy methods = AllHelpersController.helpers.methods.map(&:to_s) - # ActionView + # Action View assert methods.include?('pluralize') # abc_helper.rb diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 2b1f2a27df..e3ed097c67 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -275,7 +275,7 @@ class TestController < ActionController::Base render :action => "hello", :layout => "layouts/builder" end - # :move: test this in ActionView + # :move: test this in Action View def builder_partial_test render :action => "hello_world_container" end diff --git a/actionpack/test/controller/cookie_test.rb b/actionpack/test/dispatch/cookies_test.rb index f65eda5c69..b04c1a42c0 100644 --- a/actionpack/test/controller/cookie_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -1,6 +1,6 @@ require 'abstract_unit' -class CookieTest < ActionController::TestCase +class CookiesTest < ActionController::TestCase class TestController < ActionController::Base def authenticate cookies["user_name"] = "david" @@ -79,6 +79,16 @@ class CookieTest < ActionController::TestCase cookies[:user_name] = { :value => "david", :expires => Time.utc(2005, 10, 10,5) } head :ok end + + def set_cookie_with_domain + cookies[:user_name] = {:value => "rizwanreza", :domain => :all} + head :ok + end + + def delete_cookie_with_domain + cookies.delete(:user_name, :domain => :all) + head :ok + end end tests TestController @@ -216,6 +226,18 @@ class CookieTest < ActionController::TestCase } end + def test_cookie_with_all_domain_option + get :set_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=rizwanreza; domain=.nextangle.com; path=/" + end + + def test_deleting_cookie_with_all_domain_option + get :delete_cookie_with_domain + assert_response :success + assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" + end + private def assert_cookie_header(expected) header = @response.headers["Set-Cookie"] @@ -225,4 +247,4 @@ class CookieTest < ActionController::TestCase assert_equal expected.split("\n"), header end end -end +end
\ No newline at end of file diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 21d11ff31c..b4380f7818 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -39,7 +39,7 @@ class CookieStoreTest < ActionController::IntegrationTest session[:foo] = 'bye!' * 1024 head :ok end - + def rescue_action(e) raise end end @@ -185,6 +185,35 @@ class CookieStoreTest < ActionController::IntegrationTest end end + def test_session_store_with_explicit_domain + with_test_route_set(:domain => "example.es") do + get '/set_session_value' + assert_match /domain=example\.es/, headers['Set-Cookie'] + headers['Set-Cookie'] + end + end + + def test_session_store_without_domain + with_test_route_set do + get '/set_session_value' + assert_no_match /domain\=/, headers['Set-Cookie'] + end + end + + def test_session_store_with_nil_domain + with_test_route_set(:domain => nil) do + get '/set_session_value' + assert_no_match /domain\=/, headers['Set-Cookie'] + end + end + + def test_session_store_with_all_domains + with_test_route_set(:domain => :all) do + get '/set_session_value' + assert_match /domain=\.example\.com/, headers['Set-Cookie'] + end + end + private # Overwrite get to send SessionSecret in env hash diff --git a/actionpack/test/fixtures/layouts/yield_with_render_inline_inside.erb b/actionpack/test/fixtures/layouts/yield_with_render_inline_inside.erb new file mode 100644 index 0000000000..7298d79690 --- /dev/null +++ b/actionpack/test/fixtures/layouts/yield_with_render_inline_inside.erb @@ -0,0 +1,2 @@ +<%= render :inline => 'welcome' %> +<%= yield %> diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index aca96e0a24..059dcedad8 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -229,6 +229,12 @@ module RenderTestCases @view.render(:file => "test/hello_world.erb", :layout => "layouts/yield") end + def test_render_with_layout_which_has_render_inline + assert_equal %(welcome\nHello world!\n), + @view.render(:file => "test/hello_world.erb", :layout => "layouts/yield_with_render_inline_inside") + end + + # TODO: Move to deprecated_tests.rb def test_render_with_nested_layout_deprecated assert_deprecated do diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 17fc8b6edd..b0a4c2a9cc 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -40,15 +40,15 @@ class TextHelperTest < ActionView::TestCase assert simple_format("<b> test with html tags </b>").html_safe? end - def test_simple_format_should_sanitize_unsafe_input - assert_equal "<p><b> test with unsafe string </b></p>", simple_format("<b> test with unsafe string </b><script>code!</script>") + def test_simple_format_should_escape_unsafe_input + assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>") end - def test_simple_format_should_not_sanitize_input_if_safe_option + def test_simple_format_should_not_escape_input_if_safe_option assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, :safe => true) end - def test_simple_format_should_not_sanitize_safe_input + def test_simple_format_should_not_escape_safe_input assert_equal "<p><b> test with safe string </b></p>", simple_format("<b> test with safe string </b>".html_safe) end @@ -61,17 +61,16 @@ class TextHelperTest < ActionView::TestCase assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12) end - def test_truncate_should_sanitize_unsafe_input - assert_equal "Hello World!", truncate("Hello <script>code!</script>World!", :length => 12) - assert_equal "Hello Wor...", truncate("Hello <script>code!</script>World!!", :length => 12) + def test_truncate_should_escape_unsafe_input + assert_equal "Hello <...", truncate("Hello <script>code!</script>World!!", :length => 12) end - def test_truncate_should_not_sanitize_input_if_safe_option + def test_truncate_should_not_escape_input_if_safe_option assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!", :length => 12, :safe => true) assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!", :length => 12, :safe => true) end - def test_truncate_should_not_sanitize_safe_input + def test_truncate_should_not_escape_safe_input assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!".html_safe, :length => 12) assert_equal "Hello <sc...", truncate("Hello <script>code!</script>World!!".html_safe, :length => 12) end @@ -139,21 +138,21 @@ class TextHelperTest < ActionView::TestCase assert_equal ' ', highlight(' ', 'blank text is returned verbatim') end - def test_highlight_should_sanitize_unsafe_input + def test_highlight_should_escape_unsafe_input assert_equal( - "This is a <strong class=\"highlight\">beautiful</strong> morning", + "This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>", highlight("This is a beautiful morning<script>code!</script>", "beautiful") ) end - def test_highlight_should_not_sanitize_input_if_safe_option + def test_highlight_should_not_escape_input_if_safe_option assert_equal( "This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>", highlight("This is a beautiful morning<script>code!</script>", "beautiful", :safe => true) ) end - def test_highlight_should_not_sanitize_safe_input + def test_highlight_should_not_escape_safe_input assert_equal( "This is a <strong class=\"highlight\">beautiful</strong> morning<script>code!</script>", highlight("This is a beautiful morning<script>code!</script>".html_safe, "beautiful") @@ -190,23 +189,23 @@ class TextHelperTest < ActionView::TestCase def test_highlight_with_html assert_equal( - "<p>This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>", + "<p>This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>", highlight("<p>This is a beautiful morning, but also a beautiful day</p>", "beautiful") ) assert_equal( - "<p>This is a <em><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>", + "<p>This is a <em><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>", highlight("<p>This is a <em>beautiful</em> morning, but also a beautiful day</p>", "beautiful") ) assert_equal( - "<p>This is a <em class=\"error\"><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> <span class=\"last\">day</span></p>", + "<p>This is a <em class="error"><strong class=\"highlight\">beautiful</strong></em> morning, but also a <strong class=\"highlight\">beautiful</strong> <span class="last">day</span></p>", highlight("<p>This is a <em class=\"error\">beautiful</em> morning, but also a beautiful <span class=\"last\">day</span></p>", "beautiful") ) assert_equal( - "<p class=\"beautiful\">This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>", + "<p class="<strong class=\"highlight\">beautiful</strong>">This is a <strong class=\"highlight\">beautiful</strong> morning, but also a <strong class=\"highlight\">beautiful</strong> day</p>", highlight("<p class=\"beautiful\">This is a beautiful morning, but also a beautiful day</p>", "beautiful") ) assert_equal( - "<p>This is a <strong class=\"highlight\">beautiful</strong> <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>", + "<p>This is a <strong class=\"highlight\">beautiful</strong> <a href="http://example.com/<strong class=\"highlight\">beautiful</strong>#top?what=<strong class=\"highlight\">beautiful</strong>%20morning&when=now+then">morning</a>, but also a <strong class=\"highlight\">beautiful</strong> day</p>", highlight("<p>This is a beautiful <a href=\"http://example.com/beautiful\#top?what=beautiful%20morning&when=now+then\">morning</a>, but also a beautiful day</p>", "beautiful") ) end diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 7d0cdb5251..817640b178 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -4,12 +4,13 @@ require 'active_support/core_ext/class/inheritable_attributes' module ActiveModel class MissingAttributeError < NoMethodError end - + # == Active Model Attribute Methods + # # <tt>ActiveModel::AttributeMethods</tt> provides a way to add prefixes and suffixes # to your methods as well as handling the creation of Active Record like class methods # such as +table_name+. # - # The requirements to implement ActiveModel::AttributeMethods are: + # The requirements to implement ActiveModel::AttributeMethods are to: # # * <tt>include ActiveModel::AttributeMethods</tt> in your object # * Call each Attribute Method module method you want to add, such as @@ -45,24 +46,26 @@ module ActiveModel # end # end # - # Please notice that whenever you include ActiveModel::AttributeMethods in your class, - # it requires you to implement a <tt>attributes</tt> methods which returns a hash with - # each attribute name in your model as hash key and the attribute value as hash value. - # Hash keys must be a string. + # Notice that whenever you include ActiveModel::AttributeMethods in your class, + # it requires you to implement a <tt>attributes</tt> methods which returns a hash + # with each attribute name in your model as hash key and the attribute value as + # hash value. + # + # Hash keys must be strings. # module AttributeMethods extend ActiveSupport::Concern module ClassMethods - # Defines an "attribute" method (like +inheritance_column+ or - # +table_name+). A new (class) method will be created with the - # given name. If a value is specified, the new method will - # return that value (as a string). Otherwise, the given block - # will be used to compute the value of the method. + # Defines an "attribute" method (like +inheritance_column+ or +table_name+). + # A new (class) method will be created with the given name. If a value is + # specified, the new method will return that value (as a string). + # Otherwise, the given block will be used to compute the value of the + # method. # - # The original method will be aliased, with the new name being - # prefixed with "original_". This allows the new method to - # access the original value. + # The original method will be aliased, with the new name being prefixed + # with "original_". This allows the new method to access the original + # value. # # Example: # @@ -115,8 +118,8 @@ module ActiveModel # # #{prefix}attribute(#{attr}, *args, &block) # - # An <tt>#{prefix}attribute</tt> instance method must exist and accept at least - # the +attr+ argument. + # An instance method <tt>#{prefix}attribute</tt> must exist and accept + # at least the +attr+ argument. # # For example: # @@ -341,14 +344,17 @@ module ActiveModel end end - # Allows access to the object attributes, which are held in the <tt>@attributes</tt> hash, as though they - # were first-class methods. So a Person class with a name attribute can use Person#name and - # Person#name= and never directly use the attributes hash -- except for multiple assigns with - # ActiveRecord#attributes=. A Milestone class can also ask Milestone#completed? to test that - # the completed attribute is not +nil+ or 0. + # Allows access to the object attributes, which are held in the + # <tt>@attributes</tt> hash, as though they were first-class methods. So a + # Person class with a name attribute can use Person#name and Person#name= + # and never directly use the attributes hash -- except for multiple assigns + # with ActiveRecord#attributes=. A Milestone class can also ask + # Milestone#completed? to test that the completed attribute is not +nil+ + # or 0. # - # It's also possible to instantiate related objects, so a Client class belonging to the clients - # table with a +master_id+ foreign key can instantiate master through Client#master. + # It's also possible to instantiate related objects, so a Client class + # belonging to the clients table with a +master_id+ foreign key can + # instantiate master through Client#master. def method_missing(method_id, *args, &block) method_name = method_id.to_s if match = match_attribute_method?(method_name) @@ -367,7 +373,7 @@ module ActiveModel return true elsif !include_private_methods && super(method, true) # If we're here then we haven't found among non-private methods - # but found among all methods. Which means that given method is private. + # but found among all methods. Which means that the given method is private. return false elsif match_attribute_method?(method.to_s) return true diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index ad12600d7c..257644b3fa 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -2,7 +2,7 @@ require 'active_support/core_ext/array/wrap' require 'active_support/callbacks' module ActiveModel - # == Active Model Callbacks + # == Active Model Call Backs # # Provides an interface for any class to have Active Record like callbacks. # @@ -41,7 +41,7 @@ module ActiveModel # # Your code here # end # - # You can choose not to have all three callbacks by passing an hash to the + # You can choose not to have all three callbacks by passing a hash to the # define_model_callbacks method. # # define_model_callbacks :create, :only => :after, :before @@ -55,13 +55,13 @@ module ActiveModel end end - # define_model_callbacks accepts all options define_callbacks does, in case you - # want to overwrite a default. Besides that, it also accepts an :only option, + # define_model_callbacks accepts the same options define_callbacks does, in case + # you want to overwrite a default. Besides that, it also accepts an :only option, # where you can choose if you want all types (before, around or after) or just some. # # define_model_callbacks :initializer, :only => :after # - # Note, the <tt>:only => <type></tt> hash will apply to all callbacks defined on + # Note, the <tt>:only => <type></tt> hash will apply to all call backs defined on # that method call. To get around this you can call the define_model_callbacks # method as many times as you need. # @@ -73,7 +73,7 @@ module ActiveModel # # You can pass in a class to before_<type>, after_<type> and around_<type>, in which # case the call back will call that class's <action>_<type> method passing the object - # that the callback is being called on. + # that the call back is being called on. # # class MyModel # extend ActiveModel::Callbacks @@ -84,7 +84,7 @@ module ActiveModel # # class AnotherClass # def self.before_create( obj ) - # # obj is the MyModel instance that the callback is being called on + # # obj is the MyModel instance that the call back is being called on # end # end # diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb index 23724b2d95..2a1650faa9 100644 --- a/activemodel/lib/active_model/conversion.rb +++ b/activemodel/lib/active_model/conversion.rb @@ -1,5 +1,7 @@ module ActiveModel - # Handle default conversions: to_model, to_key and to_param. + # == Active Model Conversions + # + # Handles default conversions: to_model, to_key and to_param. # # == Example # @@ -20,17 +22,19 @@ module ActiveModel # cm.to_param #=> nil # module Conversion - # If your object is already designed to implement all of the Active Model you can use - # the default to_model implementation, which simply returns self. + # If your object is already designed to implement all of the Active Model + # you can use the default to_model implementation, which simply returns + # self. # - # If your model does not act like an Active Model object, then you should define - # <tt>:to_model</tt> yourself returning a proxy object that wraps your object - # with Active Model compliant methods. + # If your model does not act like an Active Model object, then you should + # define <tt>:to_model</tt> yourself returning a proxy object that wraps + # your object with Active Model compliant methods. def to_model self end - # Returns an Enumerable of all (primary) key attributes or nil if persisted? is false + # Returns an Enumerable of all (primary) key attributes or nil if + # persisted? is false def to_key persisted? ? [id] : nil end diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index bbcc345e4b..a82ce1bee0 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -4,16 +4,21 @@ require 'active_support/hash_with_indifferent_access' require 'active_support/core_ext/object/duplicable' module ActiveModel - # <tt>ActiveModel::Dirty</tt> provides a way to track changes in your - # object in the same way as ActiveRecord does. + # == Active Model Call Backs + # + # Provides a way to track changes in your object in the same way as + # Active Record does. # - # The requirements to implement ActiveModel::Dirty are: + # The requirements to implement ActiveModel::Dirty are to: # # * <tt>include ActiveModel::Dirty</tt> in your object - # * Call <tt>define_attribute_methods</tt> passing each method you want to track - # * Call <tt>attr_name_will_change!</tt> before each change to the tracked attribute + # * Call <tt>define_attribute_methods</tt> passing each method you want to + # track + # * Call <tt>attr_name_will_change!</tt> before each change to the tracked + # attribute # - # If you wish to also track previous changes on save or update, you need to add + # If you wish to also track previous changes on save or update, you need to + # add # # @previously_changed = changes # diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 5c076d9d2f..9efb683547 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -6,6 +6,8 @@ require 'active_support/core_ext/object/blank' require 'active_support/ordered_hash' module ActiveModel + # == Active Model Errors + # # Provides a modified +OrderedHash+ that you can include in your object # for handling error messages and interacting with Action Pack helpers. # @@ -74,7 +76,8 @@ module ActiveModel alias_method :get, :[] alias_method :set, :[]= - # When passed a symbol or a name of a method, returns an array of errors for the method. + # When passed a symbol or a name of a method, returns an array of errors + # for the method. # # p.errors[:name] #=> ["can not be nil"] # p.errors['name'] #=> ["can not be nil"] @@ -234,15 +237,20 @@ module ActiveModel full_messages end - # Translates an error message in its default scope (<tt>activemodel.errors.messages</tt>). - # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there, - # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the - # default message (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name, + # Translates an error message in its default scope + # (<tt>activemodel.errors.messages</tt>). + # + # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, + # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not + # there also, it returns the translation of the default message + # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name, # translated attribute name and the value are available for interpolation. # - # When using inheritance in your models, it will check all the inherited models too, but only if the model itself - # hasn't been found. Say you have <tt>class Admin < User; end</tt> and you wanted the translation for the <tt>:blank</tt> - # error +message+ for the <tt>title</tt> +attribute+, it looks for these translations: + # When using inheritance in your models, it will check all the inherited + # models too, but only if the model itself hasn't been found. Say you have + # <tt>class Admin < User; end</tt> and you wanted the translation for + # the <tt>:blank</tt> error +message+ for the <tt>title</tt> +attribute+, + # it looks for these translations: # # <ol> # <li><tt>activemodel.errors.models.admin.attributes.title.blank</tt></li> diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb index 13ddb622d1..a5977bf3cc 100644 --- a/activemodel/lib/active_model/lint.rb +++ b/activemodel/lib/active_model/lint.rb @@ -1,5 +1,7 @@ -# You can test whether an object is compliant with the ActiveModel API by -# including ActiveModel::Lint::Tests in your TestCase. It will included +# == Active Model Lint Tests +# +# You can test whether an object is compliant with the Active Model API by +# including <tt>ActiveModel::Lint::Tests</tt> in your TestCase. It will include # tests that tell you whether your object is fully compliant, or if not, # which aspects of the API are not implemented. # diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml index 602a530dc0..44425b4a28 100644 --- a/activemodel/lib/active_model/locale/en.yml +++ b/activemodel/lib/active_model/locale/en.yml @@ -1,6 +1,6 @@ en: errors: - # The default format use in full error messages. + # The default format to use in full error messages. format: "%{attribute} %{message}" # The values :model, :attribute and :value are always available for interpolation diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 8cdd3d2fe8..ca1e9f0ee8 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -1,7 +1,6 @@ require 'active_support/inflector' module ActiveModel - class Name < String attr_reader :singular, :plural, :element, :collection, :partial_path alias_method :cache_key, :collection @@ -36,8 +35,9 @@ module ActiveModel end end - # ActiveModel::Naming is a module that creates a +model_name+ method on your - # object. + # == Active Model Naming + # + # Creates a +model_name+ method on your object. # # To implement, just extend ActiveModel::Naming in your object: # @@ -49,7 +49,7 @@ module ActiveModel # BookCover.model_name.human #=> "Book cover" # # Providing the functionality that ActiveModel::Naming provides in your object - # is required to pass the ActiveModel Lint test. So either extending the provided + # is required to pass the Active Model Lint test. So either extending the provided # method below, or rolling your own is required.. module Naming # Returns an ActiveModel::Name object for module. It can be diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index d7e3ca100e..14f8bf72dc 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -9,6 +9,8 @@ module ActiveModel extend ActiveSupport::Concern module ClassMethods + # == Active Model Observers Activation + # # Activates the observers assigned. Examples: # # # Calls PersonObserver.instance @@ -89,6 +91,8 @@ module ActiveModel end end + # == Active Model Observers + # # Observer classes respond to lifecycle callbacks to implement trigger-like # behavior outside the original class. This is a great way to reduce the # clutter that normally comes when the model class is burdened with diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index 1c48d4613a..5670ec74cb 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -2,6 +2,8 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/slice' module ActiveModel + # == Active Model Serialization + # # Provides a basic serialization to a serializable_hash for your object. # # A minimal implementation could be: diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb index 90305978c4..918cd0ab76 100644 --- a/activemodel/lib/active_model/serializers/json.rb +++ b/activemodel/lib/active_model/serializers/json.rb @@ -2,6 +2,7 @@ require 'active_support/json' require 'active_support/core_ext/class/attribute' module ActiveModel + # == Active Model JSON Serializer module Serializers module JSON extend ActiveSupport::Concern @@ -14,8 +15,8 @@ module ActiveModel self.include_root_in_json = true end - # Returns a JSON string representing the model. Some configuration is - # available through +options+. + # Returns a JSON string representing the model. Some configuration can be + # passed through +options+. # # The option <tt>ActiveModel::Base.include_root_in_json</tt> controls the # top-level behavior of to_json. It is true by default. When it is <tt>true</tt>, diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index 934af2b8a8..ed64434b8f 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -5,6 +5,7 @@ require 'active_support/core_ext/hash/conversions' require 'active_support/core_ext/hash/slice' module ActiveModel + # == Active Model XML Serializer module Serializers module Xml extend ActiveSupport::Concern @@ -131,6 +132,8 @@ module ActiveModel end end + # Returns XML representing the model. Configuration can be + # passed through +options+. def to_xml(options = {}, &block) Serializer.new(self, options).serialize(&block) end diff --git a/activemodel/lib/active_model/translation.rb b/activemodel/lib/active_model/translation.rb index 15d0c7ddb1..0554677296 100644 --- a/activemodel/lib/active_model/translation.rb +++ b/activemodel/lib/active_model/translation.rb @@ -1,9 +1,11 @@ require 'active_support/core_ext/hash/reverse_merge' module ActiveModel - - # ActiveModel::Translation provides integration between your object and - # the Rails internationalization (i18n) framework. + + # == Active Model Translation + # + # Provides integration between your object and the Rails internationalization + # (i18n) framework. # # A minimal implementation could be: # @@ -26,14 +28,16 @@ module ActiveModel :activemodel end - # When localizing a string, goes through the lookup returned by this method. - # Used in ActiveModel::Name#human, ActiveModel::Errors#full_messages and + # When localizing a string, it goes through the lookup returned by this + # method, which is used in ActiveModel::Name#human, + # ActiveModel::Errors#full_messages and # ActiveModel::Translation#human_attribute_name. def lookup_ancestors self.ancestors.select { |x| x.respond_to?(:model_name) } end - # Transforms attributes names into a more human format, such as "First name" instead of "first_name". + # Transforms attribute names into a more human format, such as "First name" + # instead of "first_name". # # Person.human_attribute_name("first_name") # => "First name" # diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 173891bcda..36d89c2492 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -5,7 +5,9 @@ require 'active_support/core_ext/hash/keys' require 'active_model/errors' module ActiveModel - + + # == Active Model Validations + # # Provides a full validation framework to your objects. # # A minimal implementation could be: @@ -38,7 +40,7 @@ module ActiveModel # # Note that ActiveModel::Validations automatically adds an +errors+ method # to your instances initialized with a new ActiveModel::Errors object, so - # there is no need for you to add this manually. + # there is no need for you to do this manually. # module Validations extend ActiveSupport::Concern @@ -71,14 +73,14 @@ module ActiveModel # end # # Options: - # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, - # other options <tt>:create</tt>, <tt>:update</tt>). + # * <tt>:on</tt> - Specifies when this validation is active (default is + # <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>). # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+. # * <tt>:allow_blank</tt> - Skip validation if attribute is blank. - # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should - # occur (e.g. <tt>:if => :allow_validation</tt>, or - # <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The - # method, proc or string should return or evaluate to a true or false value. + # * <tt>:if</tt> - Specifies a method, proc or string to call to determine + # if the validation should occur (e.g. <tt>:if => :allow_validation</tt>, + # or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The method, + # proc or string should return or evaluate to a true or false value. # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # not occur (e.g. <tt>:unless => :skip_validation</tt>, or # <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The @@ -104,7 +106,7 @@ module ActiveModel # end # end # - # Or with a block which is passed the current record to be validated: + # Or with a block which is passed with the current record to be validated: # # class Comment # include ActiveModel::Validations @@ -127,7 +129,8 @@ module ActiveModel set_callback(:validate, *args, &block) end - # List all validators that being used to validate the model using +validates_with+ method. + # List all validators that are being used to validate the model using + # +validates_with+ method. def validators _validators.values.flatten.uniq end @@ -155,9 +158,9 @@ module ActiveModel @errors ||= Errors.new(self) end - # Runs all the specified validations and returns true if no errors were added otherwise false. - # Context can optionally be supplied to define which callbacks to test against (the context is - # defined on the validations using :on). + # Runs all the specified validations and returns true if no errors were added + # otherwise false. Context can optionally be supplied to define which callbacks + # to test against (the context is defined on the validations using :on). def valid?(context = nil) current_context, self.validation_context = validation_context, context errors.clear @@ -167,14 +170,17 @@ module ActiveModel self.validation_context = current_context end - # Performs the opposite of <tt>valid?</tt>. Returns true if errors were added, false otherwise. + # Performs the opposite of <tt>valid?</tt>. Returns true if errors were added, + # false otherwise. def invalid?(context = nil) !valid?(context) end - # Hook method defining how an attribute value should be retrieved. By default this is assumed - # to be an instance named after the attribute. Override this method in subclasses should you - # need to retrieve the value for a given attribute differently e.g. + # Hook method defining how an attribute value should be retrieved. By default + # this is assumed to be an instance named after the attribute. Override this + # method in subclasses should you need to retrieve the value for a given + # attribute differently: + # # class MyClass # include ActiveModel::Validations # diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb index 114e9091bc..689c617177 100644 --- a/activemodel/lib/active_model/validator.rb +++ b/activemodel/lib/active_model/validator.rb @@ -3,6 +3,9 @@ require "active_support/core_ext/module/anonymous" require 'active_support/core_ext/object/blank' module ActiveModel #:nodoc: + + # == Active Model Validator + # # A simple base class that can be used along with # +ActiveModel::Validations::ClassMethods.validates_with+ # @@ -127,7 +130,7 @@ module ActiveModel #:nodoc: # in the options hash invoking the validate_each method passing in the # record, attribute and value. # - # All ActiveModel validations are built on top of this Validator. + # All Active Model validations are built on top of this Validator. class EachValidator < Validator attr_reader :attributes diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index 53acd62f47..f69576b240 100755 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -1,18 +1,18 @@ #!/usr/bin/env ruby -KU TIMES = (ENV['N'] || 10000).to_i - require 'rubygems' + gem 'addressable', '~>2.0' gem 'faker', '~>0.3.1' gem 'rbench', '~>0.2.3' + require 'addressable/uri' require 'faker' require 'rbench' -__DIR__ = File.dirname(__FILE__) -$:.unshift "#{__DIR__}/../lib" -require 'active_record' +require File.expand_path("../../../load_paths", __FILE__) +require "active_record" conn = { :adapter => 'mysql', :database => 'activerecord_unittest', @@ -55,7 +55,7 @@ class Exhibit < ActiveRecord::Base def self.feel(exhibits) exhibits.each { |e| e.feel } end end -sqlfile = "#{__DIR__}/performance.sql" +sqlfile = File.expand_path("../performance.sql", __FILE__) if File.exists?(sqlfile) mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 } diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 1f5217191c..f13c250ca4 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -6,7 +6,7 @@ module ActiveRecord module AssociationPreload #:nodoc: extend ActiveSupport::Concern - # Implements the details of eager loading of ActiveRecord associations. + # Implements the details of eager loading of Active Record associations. # Application developers should not use this module directly. # # ActiveRecord::Base is extended with this module. The source code in @@ -18,7 +18,7 @@ module ActiveRecord # The first one is by using table joins. This was only strategy available # prior to Rails 2.1. Suppose that you have an Author model with columns # 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using - # this strategy, ActiveRecord would try to retrieve all data for an author + # this strategy, Active Record would try to retrieve all data for an author # and all of its books via a single query: # # SELECT * FROM authors @@ -31,7 +31,7 @@ module ActiveRecord # 'books' table is useful; the joined 'authors' data is just redundant, and # processing this redundant data takes memory and CPU time. The problem # quickly becomes worse and worse as the level of eager loading increases - # (i.e. if ActiveRecord is to eager load the associations' associations as + # (i.e. if Active Record is to eager load the associations' associations as # well). # # The second strategy is to use multiple database queries, one for each @@ -45,7 +45,7 @@ module ActiveRecord module ClassMethods protected - # Eager loads the named associations for the given ActiveRecord record(s). + # Eager loads the named associations for the given Active Record record(s). # # In this description, 'association name' shall refer to the name passed # to an association creation method. For example, a model that specifies @@ -80,7 +80,7 @@ module ActiveRecord # { :author => :avatar } # [ :books, { :author => :avatar } ] # - # +preload_options+ contains options that will be passed to ActiveRecord#find + # +preload_options+ contains options that will be passed to ActiveRecord::Base#find # (which is called under the hood for preloading records). But it is passed # only one level deep in the +associations+ argument, i.e. it's not passed # to the child associations when +associations+ is a Hash. @@ -166,7 +166,7 @@ module ActiveRecord end end - # Given a collection of ActiveRecord objects, constructs a Hash which maps + # Given a collection of Active Record objects, constructs a Hash which maps # the objects' IDs to the relevant objects. Returns a 2-tuple # <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash, # and +ids+ is an Array of record IDs. diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index c1d06ff68d..680893a048 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -711,7 +711,7 @@ module ActiveRecord # # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the # inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default, - # +ActiveRecord+ doesn't do know anything about these inverse relationships and so no object loading optimisation is possible. For example: + # Active Record doesn't know anything about these inverse relationships and so no object loading optimisation is possible. For example: # # d = Dungeon.first # t = d.traps.first @@ -721,7 +721,7 @@ module ActiveRecord # # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to the same object data from the database, but are # actually different in-memory copies of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell - # +ActiveRecord+ about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to: + # Active Record about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to: # # class Dungeon < ActiveRecord::Base # has_many :traps, :inverse_of => :dungeon diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 5b68ca2edb..d9903243ce 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -388,7 +388,7 @@ module ActiveRecord begin if !loaded? if @target.is_a?(Array) && @target.any? - @target = find_target.map { |f| i = @target.index(f); i ? @target.delete_at(i) : f } + @target + @target = find_target + @target.find_all {|t| t.new_record? } else @target = find_target end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 277ab785da..ce7259e96b 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -14,6 +14,7 @@ require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/blank' require 'arel' @@ -647,29 +648,14 @@ module ActiveRecord #:nodoc: reset_table_name end + # Returns a quoted version of the table name, used to construct SQL statements. def quoted_table_name @quoted_table_name ||= connection.quote_table_name(table_name) end + # Computes the table name, (re)sets it internally, and returns it. def reset_table_name #:nodoc: - base = base_class - - name = - # STI subclasses always use their superclass' table. - unless self == base - base.table_name - else - # Nested classes are prefixed with singular parent table name. - if parent < ActiveRecord::Base && !parent.abstract_class? - contained = parent.table_name - contained = contained.singularize if parent.pluralize_table_names - contained << '_' - end - name = "#{full_table_name_prefix}#{contained}#{undecorated_table_name(base.name)}#{table_name_suffix}" - end - - set_table_name(name) - name + self.table_name = compute_table_name end def full_table_name_prefix #:nodoc: @@ -1001,6 +987,23 @@ module ActiveRecord #:nodoc: table_name end + # Computes and returns a table name according to default conventions. + def compute_table_name + base = base_class + if self == base + # Nested classes are prefixed with singular parent table name. + if parent < ActiveRecord::Base && !parent.abstract_class? + contained = parent.table_name + contained = contained.singularize if parent.pluralize_table_names + contained << '_' + end + "#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}" + else + # STI subclasses always use their superclass' table. + base.table_name + end + end + # Enables dynamic finders like <tt>find_by_user_name(user_name)</tt> and <tt>find_by_user_name_and_password(user_name, password)</tt> # that are turned into <tt>where(:user_name => user_name).first</tt> and <tt>where(:user_name => user_name, :password => :password).first</tt> # respectively. Also works for <tt>all</tt> by using <tt>find_all_by_amount(50)</tt> that is turned into <tt>where(:amount => 50).all</tt>. @@ -1068,19 +1071,6 @@ module ActiveRecord #:nodoc: attribute_names.all? { |name| column_methods_hash.include?(name.to_sym) } end - def attribute_condition(quoted_column_name, argument) - case argument - when nil then "#{quoted_column_name} IS ?" - when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope then "#{quoted_column_name} IN (?)" - when Range then if argument.exclude_end? - "#{quoted_column_name} >= ? AND #{quoted_column_name} < ?" - else - "#{quoted_column_name} BETWEEN ? AND ?" - end - else "#{quoted_column_name} = ?" - end - end - protected # Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash. # method_name may be <tt>:find</tt> or <tt>:create</tt>. <tt>:find</tt> parameter is <tt>Relation</tt> while diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 454d3e60e3..979ed52f4a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -9,7 +9,7 @@ module ActiveRecord end module ConnectionAdapters - # Connection pool base class for managing ActiveRecord database + # Connection pool base class for managing Active Record database # connections. # # == Introduction @@ -30,12 +30,12 @@ module ActiveRecord # Connections can be obtained and used from a connection pool in several # ways: # - # 1. Simply use ActiveRecord::Base.connection as with ActiveRecord 2.1 and + # 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and # earlier (pre-connection-pooling). Eventually, when you're done with # the connection(s) and wish it to be returned to the pool, you call # ActiveRecord::Base.clear_active_connections!. This will be the - # default behavior for ActiveRecord when used in conjunction with - # ActionPack's request handling cycle. + # default behavior for Active Record when used in conjunction with + # Action Pack's request handling cycle. # 2. Manually check out a connection from the pool with # ActiveRecord::Base.connection_pool.checkout. You are responsible for # returning this connection to the pool when finished by calling @@ -265,7 +265,7 @@ module ActiveRecord end # ConnectionHandler is a collection of ConnectionPool objects. It is used - # for keeping separate connection pools for ActiveRecord models that connect + # for keeping separate connection pools for Active Record models that connect # to different databases. # # For example, suppose that you have 5 models, with the following hierarchy: @@ -285,7 +285,7 @@ module ActiveRecord # is not the same as the one used by Book/ScaryBook/GoodBook. # # Normally there is only a single ConnectionHandler instance, accessible via - # ActiveRecord::Base.connection_handler. ActiveRecord models use this to + # ActiveRecord::Base.connection_handler. Active Record models use this to # determine that connection pool that they should use. class ConnectionHandler def initialize(pools = {}) 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 db17bb348a..23c42d670b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -103,7 +103,7 @@ module ActiveRecord connection_handler.retrieve_connection(self) end - # Returns true if +ActiveRecord+ is connected. + # Returns true if Active Record is connected. def connected? connection_handler.connected?(self) end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index fecd4d590e..4ee9fee4a9 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -15,7 +15,7 @@ require 'active_record/connection_adapters/abstract/database_limits' module ActiveRecord module ConnectionAdapters # :nodoc: - # ActiveRecord supports multiple database systems. AbstractAdapter and + # Active Record supports multiple database systems. AbstractAdapter and # related classes form the abstraction layer which makes this possible. # An AbstractAdapter represents a connection to a database, and provides an # abstract interface for database-specific functionality such as establishing @@ -59,7 +59,7 @@ module ActiveRecord end # Can this adapter determine the primary key for tables not attached - # to an ActiveRecord class, such as join tables? Backend specific, as + # to an Active Record class, such as join tables? Backend specific, as # the abstract adapter always returns +false+. def supports_primary_key? false diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 34aaff2b49..bb8850f134 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -224,7 +224,7 @@ module ActiveRecord if @connection.respond_to?(:status) @connection.status == PGconn::CONNECTION_OK else - # We're asking the driver, not ActiveRecord, so use @connection.query instead of #query + # We're asking the driver, not Active Record, so use @connection.query instead of #query @connection.query 'SELECT 1' true end @@ -258,7 +258,7 @@ module ActiveRecord true end - # Does PostgreSQL support finding primary key on non-ActiveRecord tables? + # Does PostgreSQL support finding primary key on non-Active Record tables? def supports_primary_key? #:nodoc: true end @@ -925,7 +925,7 @@ module ActiveRecord # Use standard-conforming strings if available so we don't have to do the E'...' dance. set_standard_conforming_strings - # If using ActiveRecord's time zone support configure the connection to return + # If using Active Record's time zone support configure the connection to return # TIMESTAMP WITH ZONE types in UTC. execute("SET time zone 'UTC'") if ActiveRecord::Base.default_timezone == :utc end diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index 9d5cb54180..a0e94cbec1 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -4,7 +4,7 @@ en: #created_at: "Created at" #updated_at: "Updated at" - # ActiveRecord models configuration + # Active Record models configuration activerecord: errors: messages: diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb index 39aafa1ec7..ef29422824 100644 --- a/activerecord/test/cases/defaults_test.rb +++ b/activerecord/test/cases/defaults_test.rb @@ -43,7 +43,7 @@ if current_adapter?(:MysqlAdapter) class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase # ActiveRecord::Base#create! (and #save and other related methods) will # open a new transaction. When in transactional fixtures mode, this will - # cause ActiveRecord to create a new savepoint. However, since MySQL doesn't + # cause Active Record to create a new savepoint. However, since MySQL doesn't # support DDL transactions, creating a table will result in any created # savepoints to be automatically released. This in turn causes the savepoint # release code in AbstractAdapter#transaction to fail. diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index a87683bd97..685b11cb03 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -466,20 +466,6 @@ module NestedAttributesOnACollectionAssociationTests assert_equal 'Grace OMalley', @child_1.reload.name end - def test_should_not_overwrite_unsaved_updates_when_loading_association - @pirate.reload - @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }]) - @pirate.send(@association_name).send(:load_target) - assert_equal 'Grace OMalley', @pirate.send(@association_name).target.find { |r| r.id == @child_1.id }.name - end - - def test_should_preserve_order_when_not_overwriting_unsaved_updates - @pirate.reload - @pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }]) - @pirate.send(@association_name).send(:load_target) - assert_equal @pirate.send(@association_name).target.first.id, @child_1.id - end - def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models @child_1.stubs(:id).returns('ABC1X') @child_2.stubs(:id).returns('ABC2X') diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index d89c8cf381..f1dbe32c6e 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -1,7 +1,7 @@ class Pirate < ActiveRecord::Base belongs_to :parrot, :validate => true belongs_to :non_validated_parrot, :class_name => 'Parrot' - has_and_belongs_to_many :parrots, :validate => true, :order => 'parrots.id ASC' + has_and_belongs_to_many :parrots, :validate => true has_and_belongs_to_many :non_validated_parrots, :class_name => 'Parrot' has_and_belongs_to_many :parrots_with_method_callbacks, :class_name => "Parrot", :before_add => :log_before_add, @@ -21,7 +21,7 @@ class Pirate < ActiveRecord::Base has_one :ship has_one :update_only_ship, :class_name => 'Ship' has_one :non_validated_ship, :class_name => 'Ship' - has_many :birds, :order => 'birds.id ASC' + has_many :birds has_many :birds_with_method_callbacks, :class_name => "Bird", :before_add => :log_before_add, :after_add => :log_after_add, diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index e3728c83eb..521e2b6f5d 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -212,7 +212,7 @@ module ActiveResource # # is requested with invalid values, the response is: # # # # Response (422): - # # <errors type="array"><error>First cannot be empty</error></errors> + # # <errors><error>First cannot be empty</error></errors> # # or # # {"errors":["First cannot be empty"]} # # diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb index 35c3f4c6ef..4d036d73cc 100644 --- a/activeresource/test/cases/base_test.rb +++ b/activeresource/test/cases/base_test.rb @@ -52,7 +52,7 @@ class BaseTest < Test::Unit::TestCase :children => [{:name => 'Natacha'}]}, {:name => 'Milena', :children => []}]}]}.to_xml(:root => 'customer') - # - resource with yaml array of strings; for ActiveRecords using serialize :bar, Array + # - resource with yaml array of strings; for ARs using serialize :bar, Array @marty = <<-eof.strip <?xml version=\"1.0\" encoding=\"UTF-8\"?> <person> diff --git a/activeresource/test/cases/finder_test.rb b/activeresource/test/cases/finder_test.rb index 535b6f4198..1f52868966 100644 --- a/activeresource/test/cases/finder_test.rb +++ b/activeresource/test/cases/finder_test.rb @@ -50,7 +50,7 @@ class FinderTest < Test::Unit::TestCase :children => [{:name => 'Natacha'}]}, {:name => 'Milena', :children => []}]}]}.to_xml(:root => 'customer') - # - resource with yaml array of strings; for ActiveRecords using serialize :bar, Array + # - resource with yaml array of strings; for ARs using serialize :bar, Array @marty = <<-eof.strip <?xml version=\"1.0\" encoding=\"UTF-8\"?> <person> diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 6566d3fa06..13ec3c3775 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,10 @@ +*Rails 3.0.0 [Release Candidate] (unreleased)* + +* ActiveSupport::OrderedHash#merge and #merge! accept a block. #4838 [Paul Mucur, fxn] + +* Date#since, #ago, #beginning_of_day, #end_of_day, and #xmlschema honor now the user time zone if set. [Geoff Buesing] + + *Rails 3.0.0 [beta 4] (June 8th, 2010)* * Extracted String#truncate from TextHelper#truncate [DHH] diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index fc225e77a2..84f6f29572 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/file/atomic' +require 'active_support/core_ext/string/conversions' module ActiveSupport module Cache diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index ba15043bde..3ff33eea72 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' @@ -383,21 +384,12 @@ module ActiveSupport # key. See #define_callbacks for more information. # def __define_runner(symbol) #:nodoc: - send("_update_#{symbol}_superclass_callbacks") body = send("_#{symbol}_callbacks").compile(nil) silence_warnings do undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def _run_#{symbol}_callbacks(key = nil, &blk) - @_initialized_#{symbol}_callbacks ||= begin - if self.class.send("_update_#{symbol}_superclass_callbacks") - self.class.__define_runner(#{symbol.inspect}) - return _run_#{symbol}_callbacks(key, &blk) - end - true - end - if key name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks" @@ -432,16 +424,15 @@ module ActiveSupport # CallbackChain. # def __update_callbacks(name, filters = [], block = nil) #:nodoc: - send("_update_#{name}_superclass_callbacks") - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block - chain = send("_#{name}_callbacks") - yield chain, type, filters, options if block_given? - - __define_runner(name) + ([self] + self.descendents).each do |target| + chain = target.send("_#{name}_callbacks") + yield chain, type, filters, options + target.__define_runner(name) + end end # Set callbacks for a previously defined callback. @@ -454,11 +445,13 @@ module ActiveSupport # Use skip_callback to skip any defined one. # # When creating or skipping callbacks, you can specify conditions that - # are always the same for a given key. For instance, in ActionPack, + # are always the same for a given key. For instance, in Action Pack, # we convert :only and :except conditions into per-key conditions. # # before_filter :authenticate, :except => "index" + # # becomes + # # dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}} # # Per-Key conditions are evaluated only once per use of a given key. @@ -471,14 +464,18 @@ module ActiveSupport # is a speed improvement for ActionPack. # def set_callback(name, *filter_list, &block) + mapped = nil + __update_callbacks(name, filter_list, block) do |chain, type, filters, options| - filters.map! do |filter| - removed = chain.delete_if {|c| c.matches?(type, filter) } - send("_removed_#{name}_callbacks").push(*removed) + mapped ||= filters.map do |filter| Callback.new(chain, filter, type, options.dup, self) end - options[:prepend] ? chain.unshift(*filters) : chain.push(*filters) + filters.each do |filter| + chain.delete_if {|c| c.matches?(type, filter) } + end + + options[:prepend] ? chain.unshift(*mapped) : chain.push(*mapped) end end @@ -496,7 +493,6 @@ module ActiveSupport end chain.delete(filter) - send("_removed_#{name}_callbacks") << filter end end end @@ -505,53 +501,76 @@ module ActiveSupport # def reset_callbacks(symbol) callbacks = send("_#{symbol}_callbacks") + + self.descendents.each do |target| + chain = target.send("_#{symbol}_callbacks") + callbacks.each { |c| chain.delete(c) } + target.__define_runner(symbol) + end + callbacks.clear - send("_removed_#{symbol}_callbacks").concat(callbacks) __define_runner(symbol) end - # Define callbacks types. - # - # ==== Example + # Defines callbacks types: # # define_callbacks :validate # - # ==== Options + # This macro accepts the following options: # # * <tt>:terminator</tt> - Indicates when a before filter is considered # to be halted. # # define_callbacks :validate, :terminator => "result == false" # - # In the example above, if any before validate callbacks returns false, - # other callbacks are not executed. Defaults to "false". + # In the example above, if any before validate callbacks returns +false+, + # other callbacks are not executed. Defaults to "false", meaning no value + # halts the chain. # # * <tt>:rescuable</tt> - By default, after filters are not executed if - # the given block or an before_filter raises an error. Supply :rescuable => true - # to change this behavior. + # the given block or a before filter raises an error. Set this option to + # true to change this behavior. # - # * <tt>:scope</tt> - Show which methods should be executed when a class - # is given as callback: + # * <tt>:scope</tt> - Indicates which methods should be executed when a class + # is given as callback. Defaults to <tt>[:kind]</tt>. # - # define_callbacks :filters, :scope => [ :kind ] + # class Audit + # def before(caller) + # puts 'Audit: before is called' + # end # - # When a class is given: + # def before_save(caller) + # puts 'Audit: before_save is called' + # end + # end # - # before_filter MyFilter + # class Account + # include ActiveSupport::Callbacks # - # It will call the type of the filter in the given class, which in this - # case, is "before". + # define_callbacks :save + # set_callback :save, :before, Audit.new # - # If, for instance, you supply the given scope: + # def save + # run_callbacks :save do + # puts 'save in main' + # end + # end + # end # - # define_callbacks :validate, :scope => [ :kind, :name ] + # In the above case whenever you save an account the method <tt>Audit#before</tt> will + # be called. On the other hand # - # It will call "#{kind}_#{name}" in the given class. So "before_validate" - # will be called in the class below: + # define_callbacks :save, :scope => [:kind, :name] # - # before_validate MyValidation + # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling + # <tt>"#{kind}_#{name}"</tt> on the given instance. In this case "kind" is "before" and + # "name" is "save". # - # Defaults to :kind. + # A declaration like + # + # define_callbacks :save, :scope => [:name] + # + # would call <tt>Audit#save</tt>. # def define_callbacks(*callbacks) config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} @@ -559,39 +578,6 @@ module ActiveSupport extlib_inheritable_reader("_#{callback}_callbacks") do CallbackChain.new(callback, config) end - - extlib_inheritable_reader("_removed_#{callback}_callbacks") do - [] - end - - class_eval <<-METHOD, __FILE__, __LINE__ + 1 - def self._#{callback}_superclass_callbacks - if superclass.respond_to?(:_#{callback}_callbacks) - superclass._#{callback}_callbacks + superclass._#{callback}_superclass_callbacks - else - [] - end - end - - def self._update_#{callback}_superclass_callbacks - changed, index = false, 0 - - callbacks = (_#{callback}_superclass_callbacks - - _#{callback}_callbacks) - _removed_#{callback}_callbacks - - callbacks.each do |callback| - if new_index = _#{callback}_callbacks.index(callback) - index = new_index + 1 - else - changed = true - _#{callback}_callbacks.insert(index, callback) - index = index + 1 - end - end - changed - end - METHOD - __define_runner(callback) end end diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 579079d4f1..28fec5394f 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -57,19 +57,19 @@ class Date # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) # and then subtracts the specified number of seconds def ago(seconds) - to_time.since(-seconds) + to_time_in_current_zone.since(-seconds) end # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) # and then adds the specified number of seconds def since(seconds) - to_time.since(seconds) + to_time_in_current_zone.since(seconds) end alias :in :since # Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00) def beginning_of_day - to_time + to_time_in_current_zone end alias :midnight :beginning_of_day alias :at_midnight :beginning_of_day @@ -77,7 +77,7 @@ class Date # Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59) def end_of_day - to_time.end_of_day + to_time_in_current_zone.end_of_day end def plus_with_duration(other) #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 13ef703f49..ba17b0a06b 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -81,6 +81,16 @@ class Date def to_time(form = :local) ::Time.send("#{form}_time", year, month, day) end + + # Converts Date to a TimeWithZone in the current zone if Time.zone_default is set, + # otherwise converts Date to a Time via Date#to_time + def to_time_in_current_zone + if ::Time.zone_default + ::Time.zone.local(year, month, day) + else + to_time + end + end # Converts a Date instance to a DateTime, where the time is set to the beginning of the day # and UTC offset is set to 0. @@ -94,6 +104,6 @@ class Date end if RUBY_VERSION < '1.9' def xmlschema - to_time.xmlschema + to_time_in_current_zone.xmlschema end end diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index 207801d3a7..89729df258 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -17,7 +17,6 @@ class Hash # Replaces the hash without the given keys. def except!(*keys) - keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) keys.each { |key| delete(key) } self end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index d48da11bf3..e8210dfe37 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -153,7 +153,7 @@ module ActiveSupport #:nodoc: nesting.each do |namespace| begin - return Dependencies.load_missing_constant namespace.constantize, const_name + return Dependencies.load_missing_constant Inflector.constantize(namespace), const_name rescue NoMethodError then raise rescue NameError => e error ||= e diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index 04ff316a44..c1f6330c6c 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -22,7 +22,7 @@ module ActiveSupport if name.is_a?(Module) @backend = name else - require "active_support/json/backends/#{name.to_s.downcase}.rb" + require "active_support/json/backends/#{name.to_s.downcase}" @backend = ActiveSupport::JSON::Backends::const_get(name) end @parse_error = @backend::ParseError diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 1eb6e8748b..d6ccb4bac1 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -34,7 +34,6 @@ module ActiveSupport #:nodoc: # # ActiveSupport::Multibyte.proxy_class = CharsForUTF32 class Chars - attr_reader :wrapped_string alias to_s wrapped_string alias to_str wrapped_string @@ -45,14 +44,16 @@ module ActiveSupport #:nodoc: @wrapped_string = string @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen? end - - undef <=> else def initialize(string) #:nodoc: @wrapped_string = string end end + def <=>(other) + @wrapped_string <=> other + end + # Forward all undefined methods to the wrapped string. def method_missing(method, *args, &block) if method.to_s =~ /!$/ diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index e1a2866863..91de722748 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -23,6 +23,17 @@ module ActiveSupport # Hash is ordered in Ruby 1.9! if RUBY_VERSION < '1.9' + + # In MRI the Hash class is core and written in C. In particular, methods are + # programmed with explicit C function calls and polymorphism is not honored. + # + # For example, []= is crucial in this implementation to maintain the @keys + # array but hash.c invokes rb_hash_aset() originally. This prevents method + # reuse through inheritance and forces us to reimplement stuff. + # + # For instance, we cannot use the inherited #merge! because albeit the algorithm + # itself would work, our []= is not being called at all by the C code. + def initialize(*args, &block) super @keys = [] @@ -130,12 +141,16 @@ module ActiveSupport end def merge!(other_hash) - other_hash.each {|k,v| self[k] = v } + if block_given? + other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } + else + other_hash.each { |k, v| self[k] = v } + end self end - def merge(other_hash) - dup.merge!(other_hash) + def merge(other_hash, &block) + dup.merge!(other_hash, &block) end # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 863d66a91d..a58f22ee5d 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -11,7 +11,7 @@ module ActiveSupport # Parse an XML Document string or IO into a simple hash # # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, - # and uses the defaults from ActiveSupport + # and uses the defaults from Active Support. # # data:: # XML Document string or IO to parse diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 3e003b3ed4..59c168d33d 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -286,19 +286,59 @@ class DateExtCalculationsTest < ActiveSupport::TestCase def test_since assert_equal Time.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45) end + + def test_since_when_zone_default_is_set + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,21,0,0,45), Date.new(2005,2,21).since(45) + assert_equal zone, Date.new(2005,2,21).since(45).time_zone + end + end + end def test_ago assert_equal Time.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45) end + + def test_ago_when_zone_default_is_set + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,20,23,59,15), Date.new(2005,2,21).ago(45) + assert_equal zone, Date.new(2005,2,21).ago(45).time_zone + end + end + end def test_beginning_of_day assert_equal Time.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day end + + def test_beginning_of_day_when_zone_default_is_set + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,21,0,0,0), Date.new(2005,2,21).beginning_of_day + assert_equal zone, Date.new(2005,2,21).beginning_of_day.time_zone + end + end + end def test_end_of_day assert_equal Time.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day end - + + def test_end_of_day_when_zone_default_is_set + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + with_env_tz 'UTC' do + with_tz_default zone do + assert_equal zone.local(2005,2,21,23,59,59,999999.999), Date.new(2005,2,21).end_of_day + assert_equal zone, Date.new(2005,2,21).end_of_day.time_zone + end + end + end + def test_xmlschema with_env_tz 'US/Eastern' do assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) @@ -310,6 +350,15 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end end end + + def test_xmlschema_when_zone_default_is_set + with_env_tz 'UTC' do + with_tz_default ActiveSupport::TimeZone['Eastern Time (US & Canada)'] do # UTC -5 + assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) + assert_match(/^1980-06-28T00:00:00-04:?00$/, Date.new(1980, 6, 28).xmlschema) + end + end + end def test_today Date.stubs(:current).returns(Date.new(2000, 1, 1)) diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index b2a9731578..7b2c10908f 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -940,13 +940,14 @@ class HashToXmlTest < Test::Unit::TestCase end def test_expansion_count_is_limited - expected = { - 'ActiveSupport::XmlMini_REXML' => 'RuntimeError', - 'ActiveSupport::XmlMini_Nokogiri' => 'Nokogiri::XML::SyntaxError', - 'ActiveSupport::XmlMini_NokogiriSAX' => 'RuntimeError', - 'ActiveSupport::XmlMini_LibXML' => 'LibXML::XML::Error', - 'ActiveSupport::XmlMini_LibXMLSAX' => 'LibXML::XML::Error', - }[ActiveSupport::XmlMini.backend.name].constantize + expected = + case ActiveSupport::XmlMini.backend.name + when 'ActiveSupport::XmlMini_REXML'; RuntimeError + when 'ActiveSupport::XmlMini_Nokogiri'; Nokogiri::XML::SyntaxError + when 'ActiveSupport::XmlMini_NokogiriSAX'; RuntimeError + when 'ActiveSupport::XmlMini_LibXML'; LibXML::XML::Error + when 'ActiveSupport::XmlMini_LibXMLSAX'; LibXML::XML::Error + end assert_raise expected do attack_xml = <<-EOT diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index d070206d44..0f36f5204d 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -141,10 +141,32 @@ class OrderedHashTest < Test::Unit::TestCase merged = @ordered_hash.merge other_hash assert_equal merged.length, @ordered_hash.length + other_hash.length assert_equal @keys + ['purple', 'violet'], merged.keys + end + + def test_merge_with_block + hash = ActiveSupport::OrderedHash.new + hash[:a] = 0 + hash[:b] = 0 + merged = hash.merge(:b => 2, :c => 7) do |key, old_value, new_value| + new_value + 1 + end + + assert_equal 0, merged[:a] + assert_equal 3, merged[:b] + assert_equal 7, merged[:c] + end + + def test_merge_bang_with_block + hash = ActiveSupport::OrderedHash.new + hash[:a] = 0 + hash[:b] = 0 + hash.merge!(:a => 1, :c => 7) do |key, old_value, new_value| + new_value + 3 + end - @ordered_hash.merge! other_hash - assert_equal @ordered_hash, merged - assert_equal @ordered_hash.keys, merged.keys + assert_equal 4, hash[:a] + assert_equal 0, hash[:b] + assert_equal 7, hash[:c] end def test_shift @@ -152,7 +174,7 @@ class OrderedHashTest < Test::Unit::TestCase assert_equal [@keys.first, @values.first], pair assert !@ordered_hash.keys.include?(pair.first) end - + def test_keys original = @ordered_hash.keys.dup @ordered_hash.keys.pop diff --git a/ci/ci_build.rb b/ci/ci_build.rb index cd62381d9c..ee6c357519 100755 --- a/ci/ci_build.rb +++ b/ci/ci_build.rb @@ -19,7 +19,7 @@ puts "[CruiseControl] Rails build" build_results = {} # Install required version of bundler. -bundler_install_cmd = "sudo gem install bundler --no-ri --no-rdoc" +bundler_install_cmd = "gem install bundler --no-ri --no-rdoc" puts "Running command: #{bundler_install_cmd}" build_results[:install_bundler] = system bundler_install_cmd @@ -27,7 +27,7 @@ cd root_dir do puts puts "[CruiseControl] Bundling RubyGems" puts - build_results[:bundle] = system 'sudo rm -rf ~/.bundle; env CI=1 bundle install' + build_results[:bundle] = system 'rm -rf ~/.bundle; env CI=1 bundle install' end cd "#{root_dir}/activesupport" do diff --git a/ci/site_config.rb b/ci/site_config.rb index ac7f658237..c08dac8e84 100644 --- a/ci/site_config.rb +++ b/ci/site_config.rb @@ -6,8 +6,8 @@ # EMAIL NOTIFICATION # ------------------ -# CruiseControl.rb can notify you about build status via email. It uses ActionMailer component of Ruby on Rails -# framework. Obviously, ActionMailer needs to know how to send out email messages. +# CruiseControl.rb can notify you about build status via email. It uses the Action Mailer component of Ruby on Rails +# framework. Obviously, Action Mailer needs to know how to send out email messages. # If you have an SMTP server on your network, and it needs no authentication, write this in your site_config.rb: # ActionMailer::Base.smtp_settings = { diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile index d81e461e63..e32898129f 100644 --- a/railties/guides/source/active_record_basics.textile +++ b/railties/guides/source/active_record_basics.textile @@ -32,16 +32,16 @@ Active Record gives us several mechanisms, the most important being the ability * Validate models before they get persisted to the database * Perform database operations in an object-oriented fashion. -h3. Convention over Configuration in ActiveRecord +h3. Convention over Configuration in Active Record -When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicit configuration would be needed only in those cases where you can't follow the conventions for any reason. +When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating Active Record models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicit configuration would be needed only in those cases where you can't follow the conventions for any reason. h4. Naming Conventions -By default, ActiveRecord uses some naming conventions to find out how the mapping between models and database tables should be created. Rails will pluralize your class names to find the respective database table. So, for a class +Book+, you should have a database table called *books*. The Rails pluralization mechanisms are very powerful, being capable to pluralize (and singularize) both regular and irregular words. When using class names composed of two or more words, the model class name should follow the Ruby conventions, using the camelCase form, while the table name must contain the words separated by underscores. Examples: +By default, Active Record uses some naming conventions to find out how the mapping between models and database tables should be created. Rails will pluralize your class names to find the respective database table. So, for a class +Book+, you should have a database table called *books*. The Rails pluralization mechanisms are very powerful, being capable to pluralize (and singularize) both regular and irregular words. When using class names composed of two or more words, the model class name should follow the Ruby conventions, using the camelCase form, while the table name must contain the words separated by underscores. Examples: -* Database Table - Plural with underscores separating words i.e. (book_clubs) -* Model Class - Singular with the first letter of each word capitalized i.e. (BookClub) +* Database Table - Plural with underscores separating words (e.g., book_clubs) +* Model Class - Singular with the first letter of each word capitalized (e.g., BookClub) |_.Model / Class |_.Table / Schema | |Post |posts| @@ -53,30 +53,32 @@ By default, ActiveRecord uses some naming conventions to find out how the mappin h4. Schema Conventions -ActiveRecord uses naming conventions for the columns in database tables, depending on the purpose of these columns. +Active Record uses naming conventions for the columns in database tables, depending on the purpose of these columns. -* *Foreign keys* - These fields should be named following the pattern table_id i.e. (item_id, order_id). These are the fields that ActiveRecord will look for when you create associations between your models. -* *Primary keys* - By default, ActiveRecord will use an integer column named "id" as the table's primary key. When using "Rails Migrations":migrations.html to create your tables, this column will be automatically created. +* *Foreign keys* - These fields should be named following the pattern table_id (e.g., item_id, order_id). These are the fields that Active Record will look for when you create associations between your models. +* *Primary keys* - By default, Active Record will use an integer column named "id" as the table's primary key. When using "Rails Migrations":migrations.html to create your tables, this column will be automatically created. -There are also some optional column names that will create additional features to ActiveRecord instances: +There are also some optional column names that will create additional features to Active Record instances: -* *created_at / created_on* - ActiveRecord will store the current date and time to this field when creating the record. -* *updated_at / updated_on* - ActiveRecord will store the current date and times to this field when updating the record. +* *created_at* - Automatically gets set to the current date and time when the record is first created. +* *created_on* - Automatically gets set to the current date when the record is first created. +* *updated_at* - Automatically gets set to the current date and time whenever the record is updated. +* *updated_on* - Automatically gets set to the current date whenever the record is updated. * *lock_version* - Adds "optimistic locking":http://api.rubyonrails.com/classes/ActiveRecord/Locking.html to a model. * *type* - Specifies that the model uses "Single Table Inheritance":http://api.rubyonrails.com/classes/ActiveRecord/Base.html * *(table_name)_count* - Used to cache the number of belonging objects on associations. For example, a +comments_count+ column in a +Post+ class that has many instances of +Comment+ will cache the number of existent comments for each post. -NOTE: While these column names are optional they are in fact reserved by ActiveRecord. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling. +NOTE: While these column names are optional they are in fact reserved by Active Record. Steer clear of reserved keywords unless you want the extra functionality. For example, "type" is a reserved keyword used to designate a table using Single Table Inheritance. If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling. -h3. Creating ActiveRecord Models +h3. Creating Active Record Models -It's very easy to create ActiveRecord models. All you have to do is to subclass the ActiveRecord::Base class and you're good to go: +It's very easy to create Active Record models. All you have to do is to subclass the +ActiveRecord::Base+ class and you're good to go: <ruby> class Product < ActiveRecord::Base; end </ruby> -This will create a +Product+ model, mapped to a *products* table at the database. By doing this you'll also have the ability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the *products* table was created using a SQL sentence like: +This will create a +Product+ model, mapped to a *products* table at the database. By doing this you'll also have the ability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the *products* table was created using an SQL sentence like: <sql> CREATE TABLE products ( @@ -99,12 +101,15 @@ h3. Overriding the Naming Conventions What if you need to follow a different naming convention or need to use your Rails application with a legacy database? No problem, you can easily override the default conventions. You can use the +ActiveRecord::Base.set_table_name+ method to specify the table name that should be used: + <ruby> class Product < ActiveRecord::Base set_table_name "PRODUCT" end </ruby> + If you do so, you will have to define manually the class name that is hosting the fixtures (class_name.yml) using the +set_fixture_class+ method in your test definition: + <ruby> class FunnyJoke < ActiveSupport::TestCase set_fixture_class :funny_jokes => 'Joke' @@ -113,7 +118,8 @@ class FunnyJoke < ActiveSupport::TestCase end </ruby> -It's also possible to override the column that should be used as the table's primary key. Use the +ActiveRecord::Base.set_primary_key+ method for that: +It's also possible to override the column that should be used as the table's primary key using the +ActiveRecord::Base.set_primary_key+ method: + <ruby> class Product < ActiveRecord::Base set_primary_key "product_id" @@ -122,7 +128,7 @@ end h3. Reading and Writing Data -CRUD is an acronym for the four verbs we use to operate on data: Create, Read, Update, Delete. Active Record automatically creates methods to allow an application to read and manipulate data stored within its tables. +CRUD is an acronym for the four verbs we use to operate on data: *C*reate, *R*ead, *U*pdate and *D*elete. Active Record automatically creates methods to allow an application to read and manipulate data stored within its tables. h4. Create @@ -155,7 +161,7 @@ Finally, passing a block to either create or new will return a new User object: h4. Read -ActiveRecord provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by ActiveRecord. +Active Record provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by Active Record. <ruby> # return all records @@ -163,7 +169,7 @@ ActiveRecord provides a rich API for accessing data within a database. Below are </ruby> <ruby> - # return first record + # return the first record user = User.first </ruby> diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index e18dbc9c42..73f3ebafbe 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -86,7 +86,7 @@ Using <tt>Model.find(primary_key)</tt>, you can retrieve the object correspondin <ruby> # Find the client with primary key (id) 10. client = Client.find(10) -=> #<Client id: 10, name: => "Ryan"> +=> #<Client id: 10, first_name: => "Ryan"> </ruby> SQL equivalent of the above is: @@ -103,7 +103,7 @@ h5. +first+ <ruby> client = Client.first -=> #<Client id: 1, name: => "Lifo"> +=> #<Client id: 1, first_name: => "Lifo"> </ruby> SQL equivalent of the above is: @@ -120,7 +120,7 @@ h5. +last+ <ruby> client = Client.last -=> #<Client id: 221, name: => "Russel"> +=> #<Client id: 221, first_name: => "Russel"> </ruby> SQL equivalent of the above is: @@ -140,7 +140,7 @@ h5. Using Multiple Primary Keys <ruby> # Find the clients with primary keys 1 and 10. client = Client.find(1, 10) # Or even Client.find([1, 10]) -=> [#<Client id: 1, name: => "Lifo">, #<Client id: 10, name: => "Ryan">] +=> [#<Client id: 1, first_name: => "Lifo">, #<Client id: 10, first_name: => "Ryan">] </ruby> SQL equivalent of the above is: @@ -227,7 +227,7 @@ h4. Pure String Conditions If you'd like to add conditions to your find, you could just specify them in there, just like +Client.where("orders_count = '2'")+. This will find all clients where the +orders_count+ field's value is 2. -WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.where("name LIKE '%#{params[:name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array. +WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.where("first_name LIKE '%#{params[:first_name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array. h4. Array Conditions @@ -544,7 +544,7 @@ In order to use optimistic locking, the table needs to have a column called +loc c1 = Client.find(1) c2 = Client.find(1) -c1.name = "Michael" +c1.first_name = "Michael" c1.save c2.name = "should fail" @@ -773,21 +773,21 @@ Even though Active Record lets you specify conditions on the eager loaded associ h3. Dynamic Finders -For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your +Client+ model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+. +For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +first_name+ on your +Client+ model for example, you get +find_by_first_name+ and +find_all_by_first_name+ for free from Active Record. If you have also have a +locked+ field on the +Client+ model, you also get +find_by_locked+ and +find_all_by_locked+. You can do +find_last_by_*+ methods too which will find the last record matching your argument. You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic finders to get them to raise an +ActiveRecord::RecordNotFound+ error if they do not return any records, like +Client.find_by_name!("Ryan")+ -If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+. +If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_first_name_and_locked("Ryan", true)+. -There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+: +There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_first_name(params[:first_name])+. Using this will firstly perform a find and then create if the find returns +nil+. The SQL looks like this for +Client.find_or_create_by_first_name("Ryan")+: <sql> -SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1 +SELECT * FROM clients WHERE (clients.first_name = 'Ryan') LIMIT 1 BEGIN -INSERT INTO clients (name, updated_at, created_at, orders_count, locked) +INSERT INTO clients (first_name, updated_at, created_at, orders_count, locked) VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0') COMMIT </sql> @@ -795,10 +795,10 @@ COMMIT +find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similar to calling +new+ with the arguments you passed in. For example: <ruby> -client = Client.find_or_initialize_by_name('Ryan') +client = Client.find_or_initialize_by_first_name('Ryan') </ruby> -will either assign an existing client object with the name "Ryan" to the client local variable, or initialize a new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it. +will either assign an existing client object with the name "Ryan" to the client local variable, or initialize a new object similar to calling +Client.new(:first_name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it. h3. Finding by SQL @@ -869,7 +869,7 @@ SELECT count(*) AS count_all FROM clients WHERE (first_name = 'Ryan') You can also use the +includes+ or +joins+ methods for this to do something a little more complex: <ruby> -Client.count.where("clients.first_name = 'Ryan' AND orders.status = 'received'").includes("orders") +Client.where("clients.first_name = 'Ryan' AND orders.status = 'received'").includes("orders").count </ruby> Which will execute: diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index 06d1a801c8..1c17609b0a 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -2885,11 +2885,9 @@ Date.new(2010, 1, 31).change(:month => 2) # => ArgumentError: invalid date </ruby> -h5. Named Times +h5. Timestamps -WARNING: The following methods do not take into account the user time zone. They return timestamps in localtime. - -INFO: The following methods return a +Time+ object if possible, otherwise a +DateTime+. +INFO: The following methods return a +Time+ object if possible, otherwise a +DateTime+. If set, they honor the user time zone. h6. +beginning_of_day+, +end_of_day+ @@ -2907,7 +2905,25 @@ date = Date.new(2010, 6, 7) date.end_of_day # => Sun Jun 06 23:59:59 +0200 2010 </ruby> -+beginning_of_day+ is aliased to +at_beginning_of_day+, +midnight+, +at_midnight+ ++beginning_of_day+ is aliased to +at_beginning_of_day+, +midnight+, +at_midnight+. + +h6. +ago+, +since+ + +The method +ago+ receives a number of seconds as argument and returns a timestamp those many seconds ago from midnight: + +<ruby> +date = Date.current # => Fri, 11 Jun 2010 +date.ago(1) # => Thu, 10 Jun 2010 23:59:59 EDT -04:00 +</ruby> + +Similarly, +since+ moves forward: + +<ruby> +date = Date.current # => Fri, 11 Jun 2010 +date.since(1) # => Fri, 11 Jun 2010 00:00:01 EDT -04:00 +</ruby> + +h5. Other Time Computations h4(#date-conversions). Conversions diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index ab72b48034..86655746e4 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -135,7 +135,7 @@ h4. Configuring Action Controller * +config.action_controller.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments. -WARNING: Threadsafe operation in incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. +WARNING: Threadsafe operation is incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+. * +config.action_controller.param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active. @@ -167,9 +167,9 @@ The caching code adds two additional settings: The Active Record session store can also be configured: -* +ActiveRecord::SessionStore::Session.table_name+ sets the name of the table uses to store sessions. Defaults to +sessions+. +* +ActiveRecord::SessionStore::Session.table_name+ sets the name of the table used to store sessions. Defaults to +sessions+. -* +ActiveRecord::SessionStore::Session.primary_key+ sets the name of the ID column uses in the sessions table. Defaults to +session_id+. +* +ActiveRecord::SessionStore::Session.primary_key+ sets the name of the ID column used in the sessions table. Defaults to +session_id+. * +ActiveRecord::SessionStore::Session.data_column_name+ sets the name of the column which stores marshaled session data. Defaults to +data+. @@ -235,9 +235,9 @@ h4. Configuring Active Support There are a few configuration options available in Active Support: -* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to _true_. +* +config.active_support.escape_html_entities_in_json+ enables or disables the escaping of HTML entities in JSON serialization. Defaults to +true+. -* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to _false_. +* +config.active_support.use_standard_json_time_format+ enables or disables serializing dates to ISO 8601 format. Defaults to +false+. * +ActiveSupport::BufferedLogger.silencer+ is set to +false+ to disable the ability to silence logging in a block. The default is +true+. @@ -247,7 +247,7 @@ There are a few configuration options available in Active Support: h3. Using Initializers -After it loads the framework plus any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded. +After loading the framework and any gems and plugins in your application, Rails turns to loading initializers. An initializer is any file of Ruby code stored under +config/initializers+ in your application. You can use initializers to hold configuration settings that should be made after all of the frameworks and plugins are loaded. NOTE: You can use subfolders to organize your initializers if you like, because Rails will look into the whole file hierarchy from the +initializers+ folder on down. diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile index 58ae115ba7..bc3a2d0f0b 100644 --- a/railties/guides/source/initialization.textile +++ b/railties/guides/source/initialization.textile @@ -102,7 +102,7 @@ Now with Rails 3 we have a Gemfile which defines the basics our application need <ruby> source 'http://rubygems.org' - gem 'rails', '3.0.0.beta1' + gem 'rails', '3.0.0.beta4' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' @@ -132,33 +132,33 @@ Now with Rails 3 we have a Gemfile which defines the basics our application need Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This is until you run +bundle pack+. This command freezes all the gems required by your application into _vendor/cache_. The gems installed by default are: * abstract-1.0.0.gem -* actionmailer-3.0.0.beta1.gem -* actionpack-3.0.0.beta1.gem -* activemodel-3.0.0.beta1.gem -* activerecord-3.0.0.beta1.gem -* activeresource-3.0.0.beta1.gem -* activesupport-3.0.0.beta1.gem -* arel-0.3.3.gem +* actionmailer-3.0.0.beta4.gem +* actionpack-3.0.0.beta4.gem +* activemodel-3.0.0.beta4.gem +* activerecord-3.0.0.beta4.gem +* activeresource-3.0.0.beta4.gem +* activesupport-3.0.0.beta4.gem +* arel-0.4.0.gem * builder-2.1.2.gem -* bundler-0.9.14.gem +* bundler-0.9.26.gem * erubis-2.6.5.gem -* i18n-0.3.6.gem -* mail-2.1.5.3.gem -* memcache-client-1.8.1.gem +* i18n-0.4.1.gem +* mail-2.2.4.gem +* memcache-client-1.8.3.gem * mime-types-1.16.gem * polyglot-0.3.1.gem * rack-1.1.0.gem -* rack-mount-0.6.3.gem -* rack-test-0.5.3.gem -* rails-3.0.0.beta1.gem -* railties-3.0.0.beta1.gem +* rack-mount-0.6.4.gem +* rack-test-0.5.4.gem +* rails-3.0.0.beta4.gem +* railties-3.0.0.beta4.gem * rake-0.8.7.gem -* sqlite3-ruby-1.2.5.gem +* sqlite3-ruby-1.3.0.gem * text-format-1.0.0.gem * text-hyphen-1.0.0.gem -* thor-0.13.4.gem -* treetop-1.4.5.gem -* tzinfo-0.3.18.gem +* thor-0.13.6.gem +* treetop-1.4.8.gem +* tzinfo-0.3.22.gem TODO: Prettify when it becomes more stable. diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile index fc7c1e2625..a12434a95b 100644 --- a/railties/guides/source/plugins.textile +++ b/railties/guides/source/plugins.textile @@ -205,7 +205,7 @@ def load_schema ActiveRecord::Base.establish_connection(config[db_adapter]) load(File.dirname(__FILE__) + "/schema.rb") - require File.dirname(__FILE__) + '/../rails/init.rb' + require File.dirname(__FILE__) + '/../rails/init' end </ruby> @@ -218,7 +218,7 @@ Once you have these files in place, you can write your first test to ensure that <ruby> # vendor/plugins/yaffle/test/yaffle_test.rb -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' class YaffleTest < Test::Unit::TestCase load_schema @@ -284,7 +284,7 @@ In this example you will add a method to String named +to_squawk+. To begin, cr <ruby> # vendor/plugins/yaffle/test/core_ext_test.rb -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' class CoreExtTest < Test::Unit::TestCase def test_to_squawk_prepends_the_word_squawk @@ -311,7 +311,7 @@ NoMethodError: undefined method `to_squawk' for "Hello World":String Great - now you are ready to start development. -Then in +lib/yaffle.rb+ require +lib/core_ext.rb+: +Then in +lib/yaffle.rb+ require +lib/core_ext+: <ruby> # vendor/plugins/yaffle/lib/yaffle.rb @@ -380,7 +380,7 @@ To begin, set up your files so that you have: * *vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' class ActsAsYaffleTest < Test::Unit::TestCase end @@ -436,7 +436,7 @@ To start out, write a failing test that shows the behavior you'd like: * *vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' class Hickwall < ActiveRecord::Base acts_as_yaffle @@ -489,7 +489,7 @@ To start out, write a failing test that shows the behavior you'd like: * *vendor/plugins/yaffle/test/acts_as_yaffle_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' class Hickwall < ActiveRecord::Base acts_as_yaffle @@ -579,7 +579,7 @@ As always, start with a test: * *vendor/plugins/yaffle/test/woodpecker_test.rb:* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' class WoodpeckerTest < Test::Unit::TestCase load_schema @@ -633,7 +633,7 @@ You can test your plugin's controller as you would test any other controller: * *vendor/plugins/yaffle/test/woodpeckers_controller_test.rb:* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' require 'woodpeckers_controller' require 'action_controller/test_process' @@ -693,7 +693,7 @@ You can test your plugin's helper as you would test any other helper: * *vendor/plugins/yaffle/test/woodpeckers_helper_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' include WoodpeckersHelper class WoodpeckersHelperTest < Test::Unit::TestCase @@ -816,7 +816,7 @@ This section will describe how to create a simple generator that adds a file. F * *vendor/plugins/yaffle/test/definition_generator_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' require 'rails_generator' require 'rails_generator/scripts/generate' @@ -990,7 +990,7 @@ To start, add the following test method: * *vendor/plugins/yaffle/test/route_generator_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' require 'rails_generator' require 'rails_generator/scripts/generate' require 'rails_generator/scripts/destroy' @@ -1205,7 +1205,7 @@ This example will demonstrate how to use one of the built-in generator methods n * *vendor/plugins/yaffle/test/yaffle_migration_generator_test.rb* <ruby> -require File.dirname(__FILE__) + '/test_helper.rb' +require File.dirname(__FILE__) + '/test_helper' require 'rails_generator' require 'rails_generator/scripts/generate' diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile index cf733bd6f8..3f6bb66ee5 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -32,7 +32,7 @@ the request is dispatched to the +patients+ controller's +show+ action with <tt> h4. Generating URLs from Code -You can also generate routes. If your application contains this code: +You can also generate URLs. If your application contains this code: <ruby> @patient = Patient.find(17) @@ -204,7 +204,7 @@ In each of these cases, the named routes remain the same as if you did not use + |GET |photos/new |new | photos_path | |POST |photos |create | photos_path | |GET |photos/1 |show | photo_path(id) | -|GET |photos/1/edit |edit | dmin_photo_path(id) | +|GET |photos/1/edit |edit | edit_photo_path(id) | |PUT |photos/1 |update | photo_path(id) | |DELETE |photos/1 |destroy | photo_path(id) | @@ -308,7 +308,7 @@ You are not limited to the seven routes that RESTful routing creates by default. h5. Adding Member Routes -To add a member route, just add +member+ block into resource block: +To add a member route, just add a +member+ block into the resource block: <ruby> resources :photos do @@ -318,9 +318,9 @@ resources :photos do end </ruby> -This will recognize +/photos/1/preview+ with GET, and route to the +preview+ action of +PhotosController+. It will also create the +preview_photo_url+ and +preview_photo_path+ helpers. +This will recognize +/photos/1/preview+ with GET, and route to the +preview+ action of +PhotosController+. It will also create the +preview_photo_url+ and +preview_photo_path+ helpers. -Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use +get+, +put+, +post+, or +delete+ here. If you don't have multiple +member+ routes, you can also passing +:on+ to a route. +Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use +get+, +put+, +post+, or +delete+ here. If you don't have multiple +member+ routes, you can also pass +:on+ to a route, eliminating the block: <ruby> resources :photos do @@ -340,9 +340,9 @@ resources :photos do end </ruby> -This will enable Rails to recognize URLs such as +/photos/search+ with GET, and route to the +search+ action of +PhotosController+. It will also create the +search_photos_url+ and +search_photos_path+ route helpers. +This will enable Rails to recognize URLs such as +/photos/search+ with GET, and route to the +search+ action of +PhotosController+. It will also create the +search_photos_url+ and +search_photos_path+ route helpers. -Just as with member routes, you can pass +:on+ to a route. +Just as with member routes, you can pass +:on+ to a route: <ruby> resources :photos do @@ -384,7 +384,7 @@ An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of h4. Static Segments -You can specify static segments when creating a route. +You can specify static segments when creating a route: <ruby> match ':controller/:action/:id/with_user/:user_id' @@ -575,7 +575,7 @@ resources :photos, :constraints => {:id => /[A-Z][A-Z][0-9]+/} This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, the router would no longer match +/photos/1+ to this route. Instead, +/photos/RR27+ would match. -You can specify a single constraint to a apply to a number of routes by using the block form: +You can specify a single constraint to apply to a number of routes by using the block form: <ruby> constraints(:id => /[A-Z][A-Z][0-9]+/) do diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index be486ef2ac..ed66f493e6 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -1,3 +1,5 @@ +require 'rails/ruby_version_check' + require 'pathname' require 'active_support' @@ -8,7 +10,6 @@ require 'rails/application' require 'rails/version' require 'rails/deprecation' require 'rails/log_subscriber' -require 'rails/ruby_version_check' require 'active_support/railtie' require 'action_dispatch/railtie' @@ -66,7 +67,7 @@ module Rails def backtrace_cleaner @@backtrace_cleaner ||= begin - # Relies on ActiveSupport, so we have to lazy load to postpone definition until AS has been loaded + # Relies on Active Support, so we have to lazy load to postpone definition until AS has been loaded require 'rails/backtrace_cleaner' Rails::BacktraceCleaner.new end diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index f4990bfb4c..41aecea355 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -327,7 +327,7 @@ module Rails paths = [] namespaces.each do |namespace| pieces = namespace.split(":") - paths << pieces.dup.push(pieces.last).join("/") unless pieces.uniq.size == 1 + paths << pieces.dup.push(pieces.last).join("/") paths << pieces.join("/") end paths.uniq! diff --git a/railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory b/railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/app/mailers/.empty_directory diff --git a/railties/lib/rails/log_subscriber.rb b/railties/lib/rails/log_subscriber.rb index 9a74fee745..a30701d4d5 100644 --- a/railties/lib/rails/log_subscriber.rb +++ b/railties/lib/rails/log_subscriber.rb @@ -6,7 +6,7 @@ module Rails # on initialization with solely purpose of logging. The log subscriber dispatches # notifications to a regirested object based on its given namespace. # - # An example would be ActiveRecord log subscriber responsible for logging queries: + # An example would be Active Record log subscriber responsible for logging queries: # # module ActiveRecord # class Railtie diff --git a/railties/lib/rails/log_subscriber/test_helper.rb b/railties/lib/rails/log_subscriber/test_helper.rb index 02f5079462..9b7b0738cd 100644 --- a/railties/lib/rails/log_subscriber/test_helper.rb +++ b/railties/lib/rails/log_subscriber/test_helper.rb @@ -3,7 +3,7 @@ require 'rails/log_subscriber' module Rails class LogSubscriber # Provides some helpers to deal with testing log subscribers by setting up - # notifications. Take for instance ActiveRecord subscriber tests: + # notifications. Take for instance Active Record subscriber tests: # # class SyncLogSubscriberTest < ActiveSupport::TestCase # include Rails::LogSubscriber::TestHelper diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index 1dba6e1538..ad776933f2 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -10,7 +10,7 @@ module Rails # Every major component of Rails (Action Mailer, Action Controller, # Action View, Active Record and Active Resource) are all Railties, so each of # them is responsible to set their own initialization. This makes, for example, - # Rails absent of any ActiveRecord hook, allowing any other ORM framework to hook in. + # Rails absent of any Active Record hook, allowing any other ORM framework to hook in. # # Developing a Rails extension does _not_ require any implementation of # Railtie, but if you need to interact with the Rails framework during diff --git a/railties/lib/rails/ruby_version_check.rb b/railties/lib/rails/ruby_version_check.rb index 3b37c41c29..e8d1d1e039 100644 --- a/railties/lib/rails/ruby_version_check.rb +++ b/railties/lib/rails/ruby_version_check.rb @@ -1,16 +1,24 @@ -min_release = "1.8.7" -ruby_release = "#{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" -if ruby_release < min_release +if RUBY_VERSION < '1.8.7' + desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" abort <<-end_message - Rails requires Ruby version #{min_release} or later. - You're running #{ruby_release}; please upgrade to continue. + Rails 3 requires Ruby 1.8.7 or 1.9.2. + + You're running + #{desc} + + Please upgrade to continue. end_message -elsif RUBY_VERSION == '1.9.1' - abort <<-EOS - - Rails 3 does not work with Ruby 1.9.1. Please upgrade to 1.9.2. +elsif RUBY_VERSION > '1.9' and RUBY_VERSION < '1.9.2' + $stderr.puts <<-end_message + + Rails 3 doesn't officially support Ruby 1.9.1 since recent stable + releases have segfaulted the test suite. Please upgrade to Ruby 1.9.2 + before Rails 3 is released! - EOS + You're running + #{RUBY_DESCRIPTION} + + end_message end diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake index 4bddf7600a..ecd513c2c8 100644 --- a/railties/lib/rails/test_unit/testing.rake +++ b/railties/lib/rails/test_unit/testing.rake @@ -58,7 +58,7 @@ def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago) end -# Recreated here from ActiveSupport because :uncommitted needs it before Rails is available +# Recreated here from Active Support because :uncommitted needs it before Rails is available module Kernel def silence_stderr old_stderr = STDERR.dup diff --git a/railties/test/application/initializers/notifications_test.rb b/railties/test/application/initializers/notifications_test.rb index 276950c78c..fc8548af1f 100644 --- a/railties/test/application/initializers/notifications_test.rb +++ b/railties/test/application/initializers/notifications_test.rb @@ -37,7 +37,7 @@ module ApplicationTests ActiveRecord::Base.logger = logger = MockLogger.new - # Mimic ActiveRecord notifications + # Mimic Active Record notifications instrument "sql.active_record", :name => "SQL", :sql => "SHOW tables" wait diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index ffc5636467..6a3b5de9de 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -9,6 +9,7 @@ DEFAULT_APP_FILES = %w( config.ru app/controllers app/helpers + app/mailers app/models app/views/layouts config/environments diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb index 850b45ff74..450dec7716 100644 --- a/railties/test/generators/mailer_generator_test.rb +++ b/railties/test/generators/mailer_generator_test.rb @@ -17,8 +17,8 @@ class MailerGeneratorTest < Rails::Generators::TestCase def test_mailer_with_i18n_helper run_generator assert_file "app/mailers/notifier.rb" do |mailer| - assert_match /en\.actionmailer\.notifier\.foo\.subject/, mailer - assert_match /en\.actionmailer\.notifier\.bar\.subject/, mailer + assert_match /en\.notifier\.foo\.subject/, mailer + assert_match /en\.notifier\.bar\.subject/, mailer end end |