aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml12
-rw-r--r--Gemfile16
-rw-r--r--actionpack/CHANGELOG6
-rw-r--r--actionpack/actionpack.gemspec3
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb3
-rw-r--r--actionpack/lib/action_controller/base.rb8
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb94
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb41
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb2
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb36
-rw-r--r--actionpack/lib/sprockets/assets.rake26
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb19
-rw-r--r--actionpack/test/dispatch/request_test.rb6
-rw-r--r--actionpack/test/fixtures/test/_200.html.erb1
-rw-r--r--actionpack/test/template/render_test.rb9
-rw-r--r--actionpack/test/template/url_helper_test.rb8
-rw-r--r--activemodel/README.rdoc27
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb33
-rw-r--r--activemodel/lib/active_model/dirty.rb2
-rw-r--r--activemodel/lib/active_model/serialization.rb46
-rw-r--r--activemodel/lib/active_model/serializers/xml.rb40
-rw-r--r--activemodel/test/cases/serialization_test.rb88
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb52
-rw-r--r--activerecord/CHANGELOG19
-rw-r--r--activerecord/lib/active_record/base.rb25
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb5
-rw-r--r--activerecord/lib/active_record/migration.rb2
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake3
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/serialization.rb40
-rw-r--r--activerecord/lib/active_record/serializers/xml_serializer.rb42
-rw-r--r--activerecord/test/cases/adapter_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb14
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb12
-rw-r--r--activerecord/test/cases/base_test.rb11
-rw-r--r--activerecord/test/cases/connection_management_test.rb34
-rw-r--r--activerecord/test/cases/finder_test.rb8
-rw-r--r--activerecord/test/cases/named_scope_test.rb20
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb5
-rw-r--r--activerecord/test/cases/serialization_test.rb142
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb134
-rw-r--r--activerecord/test/fixtures/pirates.yml4
-rw-r--r--activesupport/CHANGELOG3
-rw-r--r--activesupport/lib/active_support/core_ext/module/delegation.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb8
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb1
-rw-r--r--activesupport/lib/active_support/inflector/methods.rb6
-rw-r--r--activesupport/lib/active_support/multibyte/chars.rb3
-rw-r--r--activesupport/lib/active_support/multibyte/unicode.rb1
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb2
-rw-r--r--activesupport/test/json/decoding_test.rb2
-rwxr-xr-xci/travis.rb142
-rw-r--r--load_paths.rb8
-rw-r--r--railties/guides/source/active_record_querying.textile1
-rw-r--r--railties/guides/source/active_support_core_extensions.textile4
-rw-r--r--railties/guides/source/asset_pipeline.textile237
-rw-r--r--railties/guides/source/caching_with_rails.textile12
-rw-r--r--railties/guides/source/command_line.textile4
-rw-r--r--railties/guides/source/configuring.textile6
-rw-r--r--railties/guides/source/form_helpers.textile15
-rw-r--r--railties/guides/source/migrations.textile41
-rw-r--r--railties/guides/source/plugins.textile6
-rw-r--r--railties/guides/source/rails_application_templates.textile12
-rw-r--r--railties/guides/source/routing.textile108
-rw-r--r--railties/lib/rails/application.rb2
-rw-r--r--railties/lib/rails/commands.rb2
-rw-r--r--railties/lib/rails/engine.rb56
-rw-r--r--railties/lib/rails/generators/actions.rb5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb4
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec2
-rwxr-xr-xrailties/lib/rails/generators/rails/plugin_new/templates/Rakefile7
-rw-r--r--railties/lib/rails/paths.rb18
-rw-r--r--railties/lib/rails/tasks/documentation.rake6
-rw-r--r--railties/lib/rails/tasks/framework.rake6
-rw-r--r--railties/lib/rails/tasks/routes.rake12
-rw-r--r--railties/lib/rails/test_unit/testing.rake4
-rw-r--r--railties/test/application/rake_test.rb88
-rw-r--r--railties/test/generators/actions_test.rb7
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb2
85 files changed, 1380 insertions, 630 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000..d33c6a3c86
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,12 @@
+script: 'ci/travis.rb'
+notifications:
+ disabled: true
+rvm:
+ - 1.8.7
+ - 1.9.2
+env:
+ - "GEM=railties"
+ - "GEM=ap,am,amo,ares,as"
+ - "GEM=ar:mysql"
+ - "GEM=ar:mysql2"
+ - "GEM=ar:sqlite3" \ No newline at end of file
diff --git a/Gemfile b/Gemfile
index e1a8b5637e..882fbedfe6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -31,13 +31,19 @@ gem "memcache-client", ">= 1.8.5"
platforms :mri_18 do
gem "system_timer"
- gem "ruby-debug", ">= 0.10.3"
+ gem "ruby-debug", ">= 0.10.3" unless ENV['TRAVIS']
gem "json"
end
platforms :mri_19 do
# TODO: Remove the conditional when ruby-debug19 supports Ruby >= 1.9.3
- gem "ruby-debug19", :require => "ruby-debug" if RUBY_VERSION < "1.9.3"
+ gem "ruby-debug19", :require => "ruby-debug" unless RUBY_VERSION > "1.9.2" || ENV['TRAVIS']
+end
+
+platforms :mri do
+ group :test do
+ gem "ruby-prof" if RUBY_VERSION < "1.9.3"
+ end
end
platforms :ruby do
@@ -48,15 +54,11 @@ platforms :ruby do
gem "yajl-ruby"
gem "nokogiri", ">= 1.4.5"
- group :test do
- gem "ruby-prof" if RUBY_VERSION < "1.9.3"
-
- end
# AR
gem "sqlite3", "~> 1.3.3"
group :db do
- gem "pg", ">= 0.11.0"
+ gem "pg", ">= 0.11.0" unless ENV['TRAVIS'] # once pg is on travis this can be removed
gem "mysql", ">= 2.8.1"
gem "mysql2", ">= 0.3.6"
end
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index b0f7d0bc11..9888be07a9 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -30,6 +30,8 @@
*Rails 3.1.0 (unreleased)*
+* The submit form helper does not generate an id "object_name_id" anymore. [fbrusatti]
+
* Make sure respond_with with :js tries to render a template in all cases [José Valim]
* json_escape will now return a SafeBuffer string if it receives SafeBuffer string [tenderlove]
@@ -101,11 +103,11 @@
For example if you have this route:
- map '*pages' => 'pages#show'
+ match '*pages' => 'pages#show'
by requesting '/foo/bar.json', your `params[:pages]` will be equals to "foo/bar" with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `:format => false` like this:
- map '*pages' => 'pages#show', :format => false
+ match '*pages' => 'pages#show', :format => false
* Added Base.http_basic_authenticate_with to do simple http basic authentication with a single class method call [DHH]
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 88d892eb16..15d104fd82 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
s.add_dependency('rack-test', '~> 0.6.0')
s.add_dependency('rack-mount', '~> 0.8.1')
s.add_dependency('sprockets', '= 2.0.0.beta.10')
- s.add_dependency('tzinfo', '~> 0.3.29')
s.add_dependency('erubis', '~> 2.7.0')
+
+ s.add_development_dependency('tzinfo', '~> 0.3.29')
end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index d6f75bded0..10aa34c76b 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -139,7 +139,7 @@ module AbstractController
#
# end
#
- # This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
+ # This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
# be rendered directly, without wrapping a layout around the rendered view.
#
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
@@ -167,6 +167,7 @@ module AbstractController
included do
class_attribute :_layout_conditions
+ remove_possible_method :_layout_conditions
delegate :_layout_conditions, :to => :'self.class'
self._layout_conditions = {}
_write_layout_method
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index d14c5f940b..ce56d8bc71 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -212,16 +212,16 @@ module ActionController
# also include them at the bottom.
AbstractController::Callbacks,
+ # Append rescue at the bottom to wrap as much as possible.
+ Rescue,
+
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
Instrumentation,
# Params wrapper should come before instrumentation so they are
# properly showed in logs
- ParamsWrapper,
-
- # The same with rescue, append it at the end to wrap as much as possible.
- Rescue
+ ParamsWrapper
]
MODULES.each do |mod|
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 980c658ab7..5c48a60469 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -98,7 +98,8 @@ module ActionDispatch
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
def valid_accept_header
- xhr? || (accept && accept !~ BROWSER_LIKE_ACCEPTS)
+ (xhr? && (accept || content_mime_type)) ||
+ (accept && accept !~ BROWSER_LIKE_ACCEPTS)
end
def use_accept_header
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 65895590bf..8d071b2061 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -659,13 +659,13 @@ module ActionDispatch
#
# This generates the following routes:
#
- # admin_posts GET /admin/posts(.:format) {:action=>"index", :controller=>"admin/posts"}
- # admin_posts POST /admin/posts(.:format) {:action=>"create", :controller=>"admin/posts"}
- # new_admin_post GET /admin/posts/new(.:format) {:action=>"new", :controller=>"admin/posts"}
- # edit_admin_post GET /admin/posts/:id/edit(.:format) {:action=>"edit", :controller=>"admin/posts"}
- # admin_post GET /admin/posts/:id(.:format) {:action=>"show", :controller=>"admin/posts"}
- # admin_post PUT /admin/posts/:id(.:format) {:action=>"update", :controller=>"admin/posts"}
- # admin_post DELETE /admin/posts/:id(.:format) {:action=>"destroy", :controller=>"admin/posts"}
+ # admin_posts GET /admin/posts(.:format) admin/posts#index
+ # admin_posts POST /admin/posts(.:format) admin/posts#create
+ # new_admin_post GET /admin/posts/new(.:format) admin/posts#new
+ # edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit
+ # admin_post GET /admin/posts/:id(.:format) admin/posts#show
+ # admin_post PUT /admin/posts/:id(.:format) admin/posts#update
+ # admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
#
# === Options
#
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 6a724749f4..e850c258ce 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -8,8 +8,8 @@ module ActionView
module Helpers
# = Action View Date Helpers
#
- # The Date Helper primarily creates select/option tags for different kinds of dates and date elements. All of the
- # select-type methods share a number of common options that are as follows:
+ # The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
+ # elements. All of the select-type methods share a number of common options that are as follows:
#
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
# would give birthday[month] instead of date[month] if passed to the <tt>select_month</tt> method.
@@ -18,7 +18,7 @@ module ActionView
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
# of "date[month]".
module DateHelper
- # Reports the approximate distance in time between two Time or Date objects or integers as seconds.
+ # Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
# Set <tt>include_seconds</tt> to true if you want more detailed approximations when distance < 1 min, 29 secs.
# Distances are reported based on the following table:
#
@@ -176,37 +176,37 @@ module ActionView
# NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
#
# ==== Examples
- # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute.
- # date_select("post", "written_on")
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
+ # date_select("article", "written_on")
#
- # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute,
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
# # with the year in the year drop down box starting at 1995.
- # date_select("post", "written_on", :start_year => 1995)
+ # date_select("article", "written_on", :start_year => 1995)
#
- # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute,
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
# # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
# # and without a day select box.
- # date_select("post", "written_on", :start_year => 1995, :use_month_numbers => true,
+ # date_select("article", "written_on", :start_year => 1995, :use_month_numbers => true,
# :discard_day => true, :include_blank => true)
#
- # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
# # with the fields ordered as day, month, year rather than month, day, year.
- # date_select("post", "written_on", :order => [:day, :month, :year])
+ # date_select("article", "written_on", :order => [:day, :month, :year])
#
# # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
# # lacking a year field.
# date_select("user", "birthday", :order => [:month, :day])
#
- # # Generates a date select that when POSTed is stored in the post variable, in the written_on attribute
+ # # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
# # which is initially set to the date 3 days from the current date
- # date_select("post", "written_on", :default => 3.days.from_now)
+ # date_select("article", "written_on", :default => 3.days.from_now)
#
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
# # that will have a default day of 20.
# date_select("credit_card", "bill_due", :default => { :day => 20 })
#
# # Generates a date select with custom prompts.
- # date_select("post", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' })
+ # date_select("article", "written_on", :prompt => { :day => 'Select day', :month => 'Select month', :year => 'Select year' })
#
# The selects are prepared for multi-parameter assignment to an Active Record object.
#
@@ -228,20 +228,20 @@ module ActionView
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
# ==== Examples
- # # Creates a time select tag that, when POSTed, will be stored in the post variable in the sunrise attribute.
- # time_select("post", "sunrise")
+ # # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
+ # time_select("article", "sunrise")
#
- # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the post variables in
+ # # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
# # the sunrise attribute.
- # time_select("post", "start_time", :include_seconds => true)
+ # time_select("article", "start_time", :include_seconds => true)
#
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
# time_select 'game', 'game_time', {:minute_step => 15}
#
# # Creates a time select tag with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
- # time_select("post", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
- # time_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours
- # time_select("post", "written_on", :prompt => true) # generic prompts for all
+ # time_select("article", "written_on", :prompt => {:hour => 'Choose hour', :minute => 'Choose minute', :second => 'Choose seconds'})
+ # time_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
+ # time_select("article", "written_on", :prompt => true) # generic prompts for all
#
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
# time_select 'game', 'game_time', {:ampm => true}
@@ -261,36 +261,36 @@ module ActionView
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
# ==== Examples
- # # Generates a datetime select that, when POSTed, will be stored in the post variable in the written_on
+ # # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
# # attribute.
- # datetime_select("post", "written_on")
+ # datetime_select("article", "written_on")
#
# # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
- # # post variable in the written_on attribute.
- # datetime_select("post", "written_on", :start_year => 1995)
+ # # article variable in the written_on attribute.
+ # datetime_select("article", "written_on", :start_year => 1995)
#
# # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
# # be stored in the trip variable in the departing attribute.
# datetime_select("trip", "departing", :default => 3.days.from_now)
#
# # Generate a datetime select with hours in the AM/PM format
- # datetime_select("post", "written_on", :ampm => true)
+ # datetime_select("article", "written_on", :ampm => true)
#
- # # Generates a datetime select that discards the type that, when POSTed, will be stored in the post variable
+ # # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
# # as the written_on attribute.
- # datetime_select("post", "written_on", :discard_type => true)
+ # datetime_select("article", "written_on", :discard_type => true)
#
# # Generates a datetime select with a custom prompt. Use <tt>:prompt => true</tt> for generic prompts.
- # datetime_select("post", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
- # datetime_select("post", "written_on", :prompt => {:hour => true}) # generic prompt for hours
- # datetime_select("post", "written_on", :prompt => true) # generic prompts for all
+ # datetime_select("article", "written_on", :prompt => {:day => 'Choose day', :month => 'Choose month', :year => 'Choose year'})
+ # datetime_select("article", "written_on", :prompt => {:hour => true}) # generic prompt for hours
+ # datetime_select("article", "written_on", :prompt => true) # generic prompts for all
#
# The selects are prepared for multi-parameter assignment to an Active Record object.
def datetime_select(object_name, method, options = {}, html_options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
end
- # Returns a set of html select-tags (one for year, month, day, hour, and minute) pre-selected with the
+ # Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
# +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
# an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
# supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
@@ -343,15 +343,15 @@ module ActionView
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
- # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not supply a Symbol,
- # it will be appended onto the <tt>:order</tt> passed in.
+ # symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
+ # If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
#
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
#
# ==== Examples
- # my_date = Time.today + 6.days
+ # my_date = Time.now + 6.days
#
- # # Generates a date select that defaults to the date in my_date (six days afteri today).
+ # # Generates a date select that defaults to the date in my_date (six days after today).
# select_date(my_date)
#
# # Generates a date select that defaults to today (no specified date).
@@ -422,7 +422,7 @@ module ActionView
end
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
- # The <tt>second</tt> can also be substituted for a second number.
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
#
# ==== Examples
@@ -440,7 +440,7 @@ module ActionView
#
# # Generates a select field for seconds with a custom prompt. Use <tt>:prompt => true</tt> for a
# # generic prompt.
- # select_minute(14, :prompt => 'Choose seconds')
+ # select_second(14, :prompt => 'Choose seconds')
#
def select_second(datetime, options = {}, html_options = {})
DateTimeSelector.new(datetime, options, html_options).select_second
@@ -448,21 +448,21 @@ module ActionView
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
- # selected. The <tt>minute</tt> can also be substituted for a minute number.
+ # selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
#
# ==== Examples
# my_time = Time.now + 6.hours
#
- # # Generates a select field for minutes that defaults to the minutes for the time in my_tiime.
+ # # Generates a select field for minutes that defaults to the minutes for the time in my_time.
# select_minute(my_time)
#
# # Generates a select field for minutes that defaults to the number given.
# select_minute(14)
#
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
- # # that is named 'stride' rather than 'second'.
- # select_minute(my_time, :field_name => 'stride')
+ # # that is named 'moment' rather than 'minute'.
+ # select_minute(my_time, :field_name => 'moment')
#
# # Generates a select field for minutes with a custom prompt. Use <tt>:prompt => true</tt> for a
# # generic prompt.
@@ -473,7 +473,7 @@ module ActionView
end
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
- # The <tt>hour</tt> can also be substituted for a hour number.
+ # The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
# Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
#
# ==== Examples
@@ -485,8 +485,8 @@ module ActionView
# # Generates a select field for hours that defaults to the number given.
# select_hour(13)
#
- # # Generates a select field for hours that defaults to the minutes for the time in my_time
- # # that is named 'stride' rather than 'second'.
+ # # Generates a select field for hours that defaults to the hour for the time in my_time
+ # # that is named 'stride' rather than 'hour'.
# select_hour(my_time, :field_name => 'stride')
#
# # Generates a select field for hours with a custom prompt. Use <tt>:prompt => true</tt> for a
@@ -501,11 +501,11 @@ module ActionView
end
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
- # The <tt>date</tt> can also be substituted for a hour number.
+ # The <tt>date</tt> can also be substituted for a day number.
# Override the field name using the <tt>:field_name</tt> option, 'day' by default.
#
# ==== Examples
- # my_date = Time.today + 2.days
+ # my_date = Time.now + 2.days
#
# # Generates a select field for days that defaults to the day for the date in my_date.
# select_day(my_time)
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 3a30263b49..724fe7a518 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -49,7 +49,7 @@ module ActionView
# <label for="person_last_name">Last name</label>:
# <input id="person_last_name" name="person[last_name]" size="30" type="text" /><br />
#
- # <input id="person_submit" name="commit" type="submit" value="Create Person" />
+ # <input name="commit" type="submit" value="Create Person" />
# </form>
#
# As you see, the HTML reflects knowledge about the resource in several spots,
@@ -80,7 +80,7 @@ module ActionView
# <label for="person_last_name">Last name</label>:
# <input id="person_last_name" name="person[last_name]" size="30" type="text" value="Smith" /><br />
#
- # <input id="person_submit" name="commit" type="submit" value="Update Person" />
+ # <input name="commit" type="submit" value="Update Person" />
# </form>
#
# Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
@@ -233,7 +233,7 @@ module ActionView
# <% end %>
#
# If your resource has associations defined, for example, you want to add comments
- # to the post given that the routes are set correctly:
+ # to the document given that the routes are set correctly:
#
# <%= form_for([@document, @comment]) do |f| %>
# ...
@@ -859,7 +859,28 @@ module ActionView
InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
end
- # Returns a text_field of type "search".
+ # Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
+ # assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
+ # some browsers.
+ #
+ # ==== Examples
+ #
+ # search_field(:user, :name)
+ # # => <input id="user_name" name="user[name]" size="30" type="search" />
+ # search_field(:user, :name, :autosave => false)
+ # # => <input autosave="false" id="user_name" name="user[name]" size="30" type="search" />
+ # search_field(:user, :name, :results => 3)
+ # # => <input id="user_name" name="user[name]" results="3" size="30" type="search" />
+ # # Assume request.host returns "www.example.com"
+ # search_field(:user, :name, :autosave => true)
+ # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" size="30" type="search" />
+ # search_field(:user, :name, :onsearch => true)
+ # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
+ # search_field(:user, :name, :autosave => false, :onsearch => true)
+ # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" />
+ # search_field(:user, :name, :autosave => true, :onsearch => true)
+ # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
+ #
def search_field(object_name, method, options = {})
options = options.stringify_keys
@@ -878,17 +899,29 @@ module ActionView
end
# Returns a text_field of type "tel".
+ #
+ # telephone_field("user", "phone")
+ # # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
+ #
def telephone_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options)
end
alias phone_field telephone_field
# Returns a text_field of type "url".
+ #
+ # url_field("user", "homepage")
+ # # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
+ #
def url_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options)
end
# Returns a text_field of type "email".
+ #
+ # email_field("user", "address")
+ # # => <input id="user_address" size="30" name="user[address]" type="email" />
+ #
def email_field(object_name, method, options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options)
end
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index fe0288521f..ec6c2c8db3 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -1,3 +1,5 @@
+# encoding: utf-8
+
require 'active_support/core_ext/big_decimal/conversions'
require 'active_support/core_ext/float/rounding'
require 'active_support/core_ext/object/blank'
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index a351fbc04f..51c784493e 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -12,8 +12,7 @@ module ActionView
#
# <%= render :partial => "account" %>
#
- # This would render "advertiser/_account.html.erb" and pass the instance variable @account in as a local variable
- # +account+ to the template for display.
+ # This would render "advertiser/_account.html.erb".
#
# In another template for Advertiser#buy, we could have:
#
@@ -28,32 +27,24 @@ module ActionView
#
# == The :as and :object options
#
- # By default <tt>ActionView::Partials::PartialRenderer</tt> has its object in a local variable with the same
- # name as the template. So, given
+ # By default <tt>ActionView::Partials::PartialRenderer</tt> doesn't have any local variables.
+ # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
#
- # <%= render :partial => "contract" %>
+ # <%= render :partial => "account", :object => @buyer %>
#
- # within contract we'll get <tt>@contract</tt> in the local variable +contract+, as if we had written
+ # would provide the +@buyer+ object to the partial, available under the local variable +account+ and is
+ # equivalent to:
#
- # <%= render :partial => "contract", :locals => { :contract => @contract } %>
+ # <%= render :partial => "account", :locals => { :account => @buyer } %>
#
# With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
- # wanted it to be +agreement+ instead of +contract+ we'd do:
+ # wanted it to be +user+ instead of +account+ we'd do:
#
- # <%= render :partial => "contract", :as => 'agreement' %>
+ # <%= render :partial => "account", :object => @buyer, :as => 'user' %>
#
- # The <tt>:object</tt> option can be used to directly specify which object is rendered into the partial;
- # useful when the template's object is elsewhere, in a different ivar or in a local variable for instance.
+ # This is equivalent to
#
- # Revisiting a previous example we could have written this code:
- #
- # <%= render :partial => "account", :object => @buyer %>
- #
- # <% @advertisements.each do |ad| %>
- # <%= render :partial => "ad", :object => ad %>
- # <% end %>
- #
- # The <tt>:object</tt> and <tt>:as</tt> options can be used together.
+ # <%= render :partial => "account", :locals => { :user => @buyer } %>
#
# == Rendering a collection of partials
#
@@ -300,6 +291,11 @@ module ActionView
else
paths.map! { |path| retrieve_variable(path).unshift(path) }
end
+ if String === partial && @variable.to_s !~ /^[a-z_][a-zA-Z_0-9]*$/
+ raise ArgumentError.new("The partial name (#{partial}) is not a valid Ruby identifier; " +
+ "make sure your partial name starts with a letter or underscore, " +
+ "and is followed by any combinations of letters, numbers, or underscores.")
+ end
self
end
diff --git a/actionpack/lib/sprockets/assets.rake b/actionpack/lib/sprockets/assets.rake
index 0236350576..a68f0e84f8 100644
--- a/actionpack/lib/sprockets/assets.rake
+++ b/actionpack/lib/sprockets/assets.rake
@@ -1,26 +1,22 @@
namespace :assets do
+ # Ensures the RAILS_GROUPS environment variable is set
+ task :ensure_env do
+ ENV["RAILS_GROUPS"] ||= "assets"
+ end
+
desc "Compile all the assets named in config.assets.precompile"
- task :precompile do
- if ENV["RAILS_GROUPS"].to_s.empty?
- ENV["RAILS_GROUPS"] = "assets"
- Kernel.exec $0, *ARGV
- else
- Rake::Task["environment"].invoke
- Sprockets::Helpers::RailsHelper
+ task :precompile => :ensure_env do
+ Rake::Task["environment"].invoke
+ Sprockets::Helpers::RailsHelper
- assets = Rails.application.config.assets.precompile
- Rails.application.assets.precompile(*assets)
- end
+ assets = Rails.application.config.assets.precompile
+ Rails.application.assets.precompile(*assets)
end
desc "Remove compiled assets"
task :clean => :environment do
assets = Rails.application.config.assets
public_asset_path = Rails.public_path + assets.prefix
- file_list = FileList.new("#{public_asset_path}/**/*")
- file_list.each do |file|
- rm_rf file
- rm_rf "#{file}.gz"
- end
+ rm_rf public_asset_path, :secure => true
end
end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 80c4fa2ee5..ccdfcb0b2c 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -6,6 +6,13 @@ module Another
class LogSubscribersController < ActionController::Base
wrap_parameters :person, :include => :name, :format => :json
+ class SpecialException < Exception
+ end
+
+ rescue_from SpecialException do
+ head :status => 406
+ end
+
def show
render :nothing => true
end
@@ -39,6 +46,10 @@ module Another
raise Exception
end
+ def with_rescued_exception
+ raise SpecialException
+ end
+
end
end
@@ -195,6 +206,14 @@ class ACLogSubscriberTest < ActionController::TestCase
assert_match(/Completed 500/, logs.last)
end
+ def test_process_action_with_rescued_exception_includes_http_status_code
+ get :with_rescued_exception
+ wait
+
+ assert_equal 2, logs.size
+ assert_match(/Completed 406/, logs.last)
+ end
+
def logs
@logs ||= @logger.logged(:info)
end
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 25b1b4f745..060bcfb5ec 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -468,6 +468,12 @@ class RequestTest < ActiveSupport::TestCase
assert request.formats.empty?
end
+ test "formats with xhr request" do
+ request = stub_request 'HTTP_X_REQUESTED_WITH' => "XMLHttpRequest"
+ request.expects(:parameters).at_least_once.returns({})
+ assert_equal [Mime::JS], request.formats
+ end
+
test "ignore_accept_header" do
ActionDispatch::Request.ignore_accept_header = true
diff --git a/actionpack/test/fixtures/test/_200.html.erb b/actionpack/test/fixtures/test/_200.html.erb
new file mode 100644
index 0000000000..c9f45675dc
--- /dev/null
+++ b/actionpack/test/fixtures/test/_200.html.erb
@@ -0,0 +1 @@
+<h1>Invalid partial</h1>
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 4187a0ac78..68b2ed45d1 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -98,6 +98,15 @@ module RenderTestCases
assert_equal "only partial", @view.render("test/partial_only", :counter_counter => 5)
end
+ def test_render_partial_with_invalid_name
+ @view.render(:partial => "test/200")
+ flunk "Render did not raise ArgumentError"
+ rescue ArgumentError => e
+ assert_equal "The partial name (test/200) is not a valid Ruby identifier; " +
+ "make sure your partial name starts with a letter or underscore, " +
+ "and is followed by any combinations of letters, numbers, or underscores.", e.message
+ end
+
def test_render_partial_with_errors
@view.render(:partial => "test/raise")
flunk "Render did not raise Template::Error"
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index a70c02a429..78245c1f95 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -386,13 +386,11 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_mail_to_with_javascript
snippet = mail_to("me@domain.com", "My email", :encode => "javascript")
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", snippet
- assert snippet.html_safe?
end
def test_mail_to_with_javascript_unicode
snippet = mail_to("unicode@example.com", "únicode", :encode => "javascript")
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%5c%22%3e%c3%ba%6e%69%63%6f%64%65%3c%5c%2f%61%3e%27%29%3b'))</script>", snippet
- assert snippet.html_safe
end
def test_mail_with_options
@@ -421,6 +419,12 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_dom_equal "<script type=\"text/javascript\">eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%5c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
end
+ def test_mail_to_returns_html_safe_string
+ assert mail_to("david@loudthinking.com").html_safe?
+ assert mail_to("me@domain.com", "My email", :encode => "javascript").html_safe?
+ assert mail_to("me@domain.com", "My email", :encode => "hex").html_safe?
+ end
+
# TODO: button_to looks at this ... why?
def protect_against_forgery?
false
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc
index b5b5edd52a..a0e6b4d9b3 100644
--- a/activemodel/README.rdoc
+++ b/activemodel/README.rdoc
@@ -182,3 +182,30 @@ modules:
p.valid? # => true
{Learn more}[link:classes/ActiveModel/Validator.html]
+
+
+== Download and installation
+
+The latest version of Active Model can be installed with Rubygems:
+
+ % [sudo] gem install activemodel
+
+Source code can be downloaded as part of the Rails project on GitHub
+
+* https://github.com/rails/rails/tree/master/activemodel/
+
+
+== License
+
+Active Model is released under the MIT license.
+
+
+== Support
+
+API documentation is at
+
+* http://api.rubyonrails.org
+
+Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
+
+* https://github.com/rails/rails/issues
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index 6ee5e04267..bdc0eb4a0d 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -314,6 +314,7 @@ module ActiveModel
end
end
end
+ attribute_method_matchers_cache.clear
end
# Removes all the previously dynamically defined methods from the class
@@ -321,6 +322,7 @@ module ActiveModel
generated_attribute_methods.module_eval do
instance_methods.each { |m| undef_method(m) }
end
+ attribute_method_matchers_cache.clear
end
# Returns true if the attribute methods defined have been generated.
@@ -338,6 +340,29 @@ module ActiveModel
end
private
+ # The methods +method_missing+ and +respond_to?+ of this module are
+ # invoked often in a typical rails, both of which invoke the method
+ # +match_attribute_method?+. The latter method iterates through an
+ # array doing regular expression matches, which results in a lot of
+ # object creations. Most of the times it returns a +nil+ match. As the
+ # match result is always the same given a +method_name+, this cache is
+ # used to alleviate the GC, which ultimately also speeds up the app
+ # significantly (in our case our test suite finishes 10% faster with
+ # this cache).
+ def attribute_method_matchers_cache
+ @attribute_method_matchers_cache ||= {}
+ end
+
+ def attribute_method_matcher(method_name)
+ if attribute_method_matchers_cache.key?(method_name)
+ attribute_method_matchers_cache[method_name]
+ else
+ match = nil
+ attribute_method_matchers.detect { |method| match = method.match(method_name) }
+ attribute_method_matchers_cache[method_name] = match
+ end
+ end
+
class AttributeMethodMatcher
attr_reader :prefix, :suffix, :method_missing_target
@@ -411,12 +436,8 @@ module ActiveModel
# Returns a struct representing the matching attribute method.
# The struct's attributes are prefix, base and suffix.
def match_attribute_method?(method_name)
- self.class.attribute_method_matchers.each do |method|
- if (match = method.match(method_name)) && attribute_method?(match.attr_name)
- return match
- end
- end
- nil
+ match = self.class.send(:attribute_method_matcher, method_name)
+ match && attribute_method?(match.attr_name) ? match : nil
end
# prevent method_missing from calling private methods with #send
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 166cccf161..e3e71525fa 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -29,7 +29,7 @@ module ActiveModel
#
# include ActiveModel::Dirty
#
- # define_attribute_methods [:name]
+ # define_attribute_methods = [:name]
#
# def name
# @name
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index 4a174cb62a..9260c5082d 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -69,18 +69,46 @@ module ActiveModel
def serializable_hash(options = nil)
options ||= {}
- only = Array.wrap(options[:only]).map(&:to_s)
- except = Array.wrap(options[:except]).map(&:to_s)
-
attribute_names = attributes.keys.sort
- if only.any?
- attribute_names &= only
- elsif except.any?
- attribute_names -= except
+ if only = options[:only]
+ attribute_names &= Array.wrap(only).map(&:to_s)
+ elsif except = options[:except]
+ attribute_names -= Array.wrap(except).map(&:to_s)
+ end
+
+ method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
+ hash = Hash[(attribute_names + method_names).map { |n| [n, send(n)] }]
+
+ serializable_add_includes(options) do |association, records, opts|
+ hash[association] = if records.is_a?(Enumerable)
+ records.map { |a| a.serializable_hash(opts) }
+ else
+ records.serializable_hash(opts)
+ end
end
- method_names = Array.wrap(options[:methods]).map { |n| n if respond_to?(n.to_s) }.compact
- Hash[(attribute_names + method_names).map { |n| [n, send(n)] }]
+ hash
end
+
+ private
+ # Add associations specified via the <tt>:include</tt> option.
+ #
+ # Expects a block that takes as arguments:
+ # +association+ - name of the association
+ # +records+ - the association record(s) to be serialized
+ # +opts+ - options for the association records
+ def serializable_add_includes(options = {})
+ return unless include = options[:include]
+
+ unless include.is_a?(Hash)
+ include = Hash[Array.wrap(include).map { |n| [n, {}] }]
+ end
+
+ include.each do |association, opts|
+ if records = send(association)
+ yield association, records, opts
+ end
+ end
+ end
end
end
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb
index 9812af43d6..64dda3bcee 100644
--- a/activemodel/lib/active_model/serializers/xml.rb
+++ b/activemodel/lib/active_model/serializers/xml.rb
@@ -101,6 +101,7 @@ module ActiveModel
@builder.tag!(*args) do
add_attributes_and_methods
+ add_includes
add_extra_behavior
add_procs
yield @builder if block_given?
@@ -120,6 +121,45 @@ module ActiveModel
end
end
+ def add_includes
+ @serializable.send(:serializable_add_includes, options) do |association, records, opts|
+ add_associations(association, records, opts)
+ end
+ end
+
+ # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
+ def add_associations(association, records, opts)
+ merged_options = opts.merge(options.slice(:builder, :indent))
+ merged_options[:skip_instruct] = true
+
+ if records.is_a?(Enumerable)
+ tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
+ type = options[:skip_types] ? { } : {:type => "array"}
+ association_name = association.to_s.singularize
+ merged_options[:root] = association_name
+
+ if records.empty?
+ @builder.tag!(tag, type)
+ else
+ @builder.tag!(tag, type) do
+ records.each do |record|
+ if options[:skip_types]
+ record_type = {}
+ else
+ record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
+ record_type = {:type => record_class}
+ end
+
+ record.to_xml merged_options.merge(record_type)
+ end
+ end
+ end
+ else
+ merged_options[:root] = association.to_s
+ records.to_xml(merged_options)
+ end
+ end
+
def add_procs
if procs = options.delete(:procs)
Array.wrap(procs).each do |proc|
diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb
index 8cc1ccb1e7..5122f08eec 100644
--- a/activemodel/test/cases/serialization_test.rb
+++ b/activemodel/test/cases/serialization_test.rb
@@ -1,13 +1,19 @@
require "cases/helper"
+require 'active_support/core_ext/object/instance_variables'
class SerializationTest < ActiveModel::TestCase
class User
include ActiveModel::Serialization
- attr_accessor :name, :email, :gender
+ attr_accessor :name, :email, :gender, :address, :friends
+
+ def initialize(name, email, gender)
+ @name, @email, @gender = name, email, gender
+ @friends = []
+ end
def attributes
- @attributes ||= {'name' => 'nil', 'email' => 'nil', 'gender' => 'nil'}
+ instance_values.except("address", "friends")
end
def foo
@@ -15,11 +21,25 @@ class SerializationTest < ActiveModel::TestCase
end
end
+ class Address
+ include ActiveModel::Serialization
+
+ attr_accessor :street, :city, :state, :zip
+
+ def attributes
+ instance_values
+ end
+ end
+
setup do
- @user = User.new
- @user.name = 'David'
- @user.email = 'david@example.com'
- @user.gender = 'male'
+ @user = User.new('David', 'david@example.com', 'male')
+ @user.address = Address.new
+ @user.address.street = "123 Lane"
+ @user.address.city = "Springfield"
+ @user.address.state = "CA"
+ @user.address.zip = 11111
+ @user.friends = [User.new('Joe', 'joe@example.com', 'male'),
+ User.new('Sue', 'sue@example.com', 'female')]
end
def test_method_serializable_hash_should_work
@@ -42,4 +62,60 @@ class SerializationTest < ActiveModel::TestCase
assert_equal expected , @user.serializable_hash(:methods => [:foo])
end
+ def test_method_serializable_hash_should_work_with_only_and_methods
+ expected = {:foo=>"i_am_foo"}
+ assert_equal expected , @user.serializable_hash(:only => [], :methods => [:foo])
+ end
+
+ def test_method_serializable_hash_should_work_with_except_and_methods
+ expected = {"gender"=>"male", :foo=>"i_am_foo"}
+ assert_equal expected , @user.serializable_hash(:except => [:name, :email], :methods => [:foo])
+ end
+
+ def test_should_not_call_methods_that_dont_respond
+ expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"}
+ assert_equal expected , @user.serializable_hash(:methods => [:bar])
+ end
+
+ def test_include_option_with_singular_association
+ expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com",
+ :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}}
+ assert_equal expected , @user.serializable_hash(:include => :address)
+ end
+
+ def test_include_option_with_plural_association
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
+ assert_equal expected , @user.serializable_hash(:include => :friends)
+ end
+
+ def test_include_option_with_empty_association
+ @user.friends = []
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]}
+ assert_equal expected , @user.serializable_hash(:include => :friends)
+ end
+
+ def test_multiple_includes
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111},
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]}
+ assert_equal expected , @user.serializable_hash(:include => [:address, :friends])
+ end
+
+ def test_include_with_options
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :address=>{"street"=>"123 Lane"}}
+ assert_equal expected , @user.serializable_hash(:include => {:address => {:only => "street"}})
+ end
+
+ def test_nested_include
+ @user.friends.first.friends = [@user]
+ expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David",
+ :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male',
+ :friends => ["email"=>"david@example.com", "gender"=>"male", "name"=>"David"]},
+ {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', :friends => []}]}
+ assert_equal expected , @user.serializable_hash(:include => {:friends => {:include => :friends}})
+ end
end
diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb
index f978191d22..a38ef8e223 100644
--- a/activemodel/test/cases/serializers/xml_serialization_test.rb
+++ b/activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -7,9 +7,11 @@ class Contact
extend ActiveModel::Naming
include ActiveModel::Serializers::Xml
+ attr_accessor :address, :friends
+
def attributes
- instance_values
- end unless method_defined?(:attributes)
+ instance_values.except("address", "friends")
+ end
end
module Admin
@@ -20,6 +22,17 @@ end
class Customer < Struct.new(:name)
end
+class Address
+ extend ActiveModel::Naming
+ include ActiveModel::Serializers::Xml
+
+ attr_accessor :street, :city, :state, :zip
+
+ def attributes
+ instance_values
+ end
+end
+
class XmlSerializationTest < ActiveModel::TestCase
def setup
@contact = Contact.new
@@ -30,6 +43,12 @@ class XmlSerializationTest < ActiveModel::TestCase
customer = Customer.new
customer.name = "John"
@contact.preferences = customer
+ @contact.address = Address.new
+ @contact.address.street = "123 Lane"
+ @contact.address.city = "Springfield"
+ @contact.address.state = "CA"
+ @contact.address.zip = 11111
+ @contact.friends = [Contact.new, Contact.new]
end
test "should serialize default root" do
@@ -138,4 +157,33 @@ class XmlSerializationTest < ActiveModel::TestCase
assert_match %r{<contact type="Contact">}, xml
assert_match %r{<name>aaron stack</name>}, xml
end
+
+ test "include option with singular association" do
+ xml = @contact.to_xml :include => :address, :indent => 0
+ assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true))
+ end
+
+ test "include option with plural association" do
+ xml = @contact.to_xml :include => :friends, :indent => 0
+ assert_match %r{<friends type="array">}, xml
+ assert_match %r{<friend type="Contact">}, xml
+ end
+
+ test "multiple includes" do
+ xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => [ :address, :friends ]
+ assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true))
+ assert_match %r{<friends type="array">}, xml
+ assert_match %r{<friend type="Contact">}, xml
+ end
+
+ test "include with options" do
+ xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => { :address => { :only => :city } }
+ assert xml.include?(%(><address><city>Springfield</city></address>))
+ end
+
+ test "propagates skip_types option to included associations" do
+ xml = @contact.to_xml :include => :friends, :indent => 0, :skip_types => true
+ assert_match %r{<friends>}, xml
+ assert_match %r{<friend>}, xml
+ end
end
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 4113a16c12..d24ca2716e 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,3 +1,22 @@
+*Rails 3.2.0 (unreleased)*
+
+* ActiveRecord::Base.establish_connection now takes a string that contains
+a URI that specifies the connection configuration. For example:
+
+ ActiveRecord::Base.establish_connection 'postgres://localhost/foo'
+
+* Active Record's dynamic finder will now raise the error if you passing in less number of arguments than what you call in method signature.
+
+ So if you were doing this and expecting the second argument to be nil:
+
+ User.find_by_username_and_group("sikachu")
+
+ You'll now get `ArgumentError: wrong number of arguments (1 for 2).` You'll then have to do this:
+
+ User.find_by_username_and_group("sikachu", nil)
+
+ [Prem Sichanugrist]
+
*Rails 3.1.0 (unreleased)*
* ActiveRecord::MacroReflection::AssociationReflection#build_record has a new method signature.
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index c2f725445e..c26119287a 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -1052,20 +1052,15 @@ module ActiveRecord #:nodoc:
# Each dynamic finder using <tt>scoped_by_*</tt> is also defined in the class after it
# is first invoked, so that future attempts to use it do not run through method_missing.
def method_missing(method_id, *arguments, &block)
- if match = DynamicFinderMatch.match(method_id)
+ if match = (DynamicFinderMatch.match(method_id) || DynamicScopeMatch.match(method_id))
attribute_names = match.attribute_names
super unless all_attributes_exists?(attribute_names)
- if match.finder?
- options = arguments.extract_options!
- relation = options.any? ? scoped(options) : scoped
- relation.send :find_by_attributes, match, attribute_names, *arguments, &block
- elsif match.instantiator?
- scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
+ if arguments.size < attribute_names.size
+ method_trace = "#{__FILE__}:#{__LINE__}:in `#{method_id}'"
+ backtrace = [method_trace] + caller
+ raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{attribute_names.size})", backtrace
end
- elsif match = DynamicScopeMatch.match(method_id)
- attribute_names = match.attribute_names
- super unless all_attributes_exists?(attribute_names)
- if match.scope?
+ if match.respond_to?(:scope?) && match.scope?
self.class_eval <<-METHOD, __FILE__, __LINE__ + 1
def self.#{method_id}(*args) # def self.scoped_by_user_name_and_password(*args)
attributes = Hash[[:#{attribute_names.join(',:')}].zip(args)] # attributes = Hash[[:user_name, :password].zip(args)]
@@ -1074,6 +1069,12 @@ module ActiveRecord #:nodoc:
end # end
METHOD
send(method_id, *arguments)
+ elsif match.finder?
+ options = arguments.extract_options!
+ relation = options.any? ? scoped(options) : scoped
+ relation.send :find_by_attributes, match, attribute_names, *arguments, &block
+ elsif match.instantiator?
+ scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
end
else
super
@@ -1737,7 +1738,7 @@ MSG
end
# Returns an <tt>#inspect</tt>-like string for the value of the
- # attribute +attr_name+. String attributes are elided after 50
+ # attribute +attr_name+. String attributes are truncated upto 50
# characters, and Date and Time attributes are returned in the
# <tt>:db</tt> format. Other attributes return the value of
# <tt>#inspect</tt> without modification.
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 bcd3abc08d..123b3654e6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb
@@ -46,6 +46,12 @@ module ActiveRecord
# "database" => "path/to/dbfile"
# )
#
+ # Or a URL:
+ #
+ # ActiveRecord::Base.establish_connection(
+ # "postgres://myuser:mypass@localhost/somedatabase"
+ # )
+ #
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
# may be returned on an error.
def self.establish_connection(spec = nil)
@@ -58,6 +64,8 @@ module ActiveRecord
when Symbol, String
if configuration = configurations[spec.to_s]
establish_connection(configuration)
+ elsif spec.is_a?(String) && hash = connection_url_to_hash(spec)
+ establish_connection(hash)
else
raise AdapterNotSpecified, "#{spec} database is not configured"
end
@@ -81,6 +89,24 @@ module ActiveRecord
end
end
+ def self.connection_url_to_hash(url) # :nodoc:
+ config = URI.parse url
+ adapter = config.scheme
+ adapter = "postgresql" if adapter == "postgres"
+ spec = { :adapter => adapter,
+ :username => config.user,
+ :password => config.password,
+ :port => config.port,
+ :database => config.path.sub(%r{^/},""),
+ :host => config.host }
+ spec.reject!{ |_,value| !value }
+ if config.query
+ options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys
+ spec.merge!(options)
+ end
+ spec
+ end
+
class << self
# Returns the connection currently associated with the class. This can
# also be used to "borrow" the connection to do database work unrelated
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 724b2e6d9c..e2a0f63393 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -157,6 +157,11 @@ module ActiveRecord
end
end
+ def type_cast(value, column) # :nodoc:
+ return super unless BigDecimal === value
+
+ value.to_f
+ end
# DATABASE STATEMENTS ======================================
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index c459846264..507f345ef5 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,3 +1,5 @@
+require "active_support/core_ext/module/delegation"
+require "active_support/core_ext/class/attribute_accessors"
require "active_support/core_ext/array/wrap"
module ActiveRecord
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 53617059d0..2dbebfcaf8 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -383,7 +383,7 @@ module ActiveRecord
attributes_collection = if keys.include?('id') || keys.include?(:id)
Array.wrap(attributes_collection)
else
- attributes_collection.sort_by { |i, _| i.to_i }.map { |_, attributes| attributes }
+ attributes_collection.values
end
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 44e5520230..0ee7e20cf1 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -115,7 +115,8 @@ db_namespace = namespace :db do
end
end
else
- $stderr.puts "#{config['database']} already exists"
+ # Bug with 1.9.2 Calling return within begin still executes else
+ $stderr.puts "#{config['database']} already exists" unless config['adapter'] =~ /sqlite/
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 283115a448..8cef4e5554 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -243,7 +243,7 @@ module ActiveRecord
end
def construct_limited_ids_condition(relation)
- orders = relation.order_values
+ orders = relation.order_values.map { |val| val.presence }.compact
values = @klass.connection.distinct("#{@klass.connection.quote_table_name table_name}.#{primary_key}", orders)
relation = relation.dup
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index e3185a9c5a..5ad40d8cd9 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -10,46 +10,8 @@ module ActiveRecord #:nodoc:
options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
options[:except] |= Array.wrap(self.class.inheritance_column)
- hash = super(options)
-
- serializable_add_includes(options) do |association, records, opts|
- hash[association] = records.is_a?(Enumerable) ?
- records.map { |r| r.serializable_hash(opts) } :
- records.serializable_hash(opts)
- end
-
- hash
+ super(options)
end
-
- private
- # Add associations specified via the <tt>:include</tt> option.
- #
- # Expects a block that takes as arguments:
- # +association+ - name of the association
- # +records+ - the association record(s) to be serialized
- # +opts+ - options for the association records
- def serializable_add_includes(options = {})
- return unless include_associations = options.delete(:include)
-
- include_has_options = include_associations.is_a?(Hash)
- associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
-
- associations.each do |association|
- records = case self.class.reflect_on_association(association).macro
- when :has_many, :has_and_belongs_to_many
- send(association).to_a
- when :has_one, :belongs_to
- send(association)
- end
-
- if records
- association_options = include_has_options ? include_associations[association] : {}
- yield(association, records, association_options)
- end
- end
-
- options[:include] = include_associations
- end
end
end
diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb
index f8e6cf958c..cbfa1ad609 100644
--- a/activerecord/lib/active_record/serializers/xml_serializer.rb
+++ b/activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -182,48 +182,6 @@ module ActiveRecord #:nodoc:
options[:except] |= Array.wrap(@serializable.class.inheritance_column)
end
- def add_extra_behavior
- add_includes
- end
-
- def add_includes
- procs = options.delete(:procs)
- @serializable.send(:serializable_add_includes, options) do |association, records, opts|
- add_associations(association, records, opts)
- end
- options[:procs] = procs
- end
-
- # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
- def add_associations(association, records, opts)
- association_name = association.to_s.singularize
- merged_options = options.merge(opts).merge!(:root => association_name, :skip_instruct => true)
-
- if records.is_a?(Enumerable)
- tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
- type = options[:skip_types] ? { } : {:type => "array"}
-
- if records.empty?
- @builder.tag!(tag, type)
- else
- @builder.tag!(tag, type) do
- records.each do |record|
- if options[:skip_types]
- record_type = {}
- else
- record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
- record_type = {:type => record_class}
- end
-
- record.to_xml merged_options.merge(record_type)
- end
- end
- end
- elsif record = @serializable.send(association)
- record.to_xml(merged_options)
- end
- end
-
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
def compute_type
klass = @serializable.class
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 4c65193d75..3942e7bb41 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -81,12 +81,6 @@ class AdapterTest < ActiveRecord::TestCase
end
end
- if current_adapter?(:PostgreSQLAdapter)
- def test_encoding
- assert_not_nil @connection.encoding
- end
- end
-
def test_table_alias
def @connection.test_table_alias_length() 10; end
class << @connection
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index eee771ecff..2a89430da9 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -13,6 +13,16 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_connect_with_url
+ run_without_connection do |orig|
+ ar_config = ARTest.connection_config['arunit']
+ url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}"
+ klass = Class.new(ActiveRecord::Base)
+ klass.establish_connection(url)
+ assert_equal ar_config['database'], klass.connection.current_database
+ end
+ end
+
def test_mysql_reconnect_attribute_after_connection_with_reconnect_false
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false}))
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
new file mode 100644
index 0000000000..21b97b3b39
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -0,0 +1,14 @@
+require "cases/helper"
+
+module ActiveRecord
+ class PostgresqlConnectionTest < ActiveRecord::TestCase
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_encoding
+ assert_not_nil @connection.encoding
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 0d9db92447..e0152e7ccf 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -67,7 +67,7 @@ module ActiveRecord
def test_type_cast_bigdecimal
bd = BigDecimal.new '10.0'
- assert_equal bd.to_s('F'), @conn.type_cast(bd, nil)
+ assert_equal bd.to_f, @conn.type_cast(bd, nil)
end
def test_type_cast_unknown
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index f1d061133c..c6e451fc57 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1048,4 +1048,16 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_not_equal 0, post.comments.to_a.count
end
end
+
+ def test_join_eager_with_empty_order_should_generate_valid_sql
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
+ Post.includes(:comments).order("").where(:comments => {:body => "Thank you for the welcome"}).first
+ end
+ end
+
+ def test_join_eager_with_nil_order_should_generate_valid_sql
+ assert_nothing_raised(ActiveRecord::StatementInvalid) do
+ Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
+ end
+ end
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 702c9b9dc1..8b4e7dd799 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1098,6 +1098,17 @@ class BasicsTest < ActiveRecord::TestCase
self.table_name = 'numeric_data'
end
+ def test_big_decimal_conditions
+ m = NumericData.new(
+ :bank_balance => 1586.43,
+ :big_bank_balance => BigDecimal("1000234000567.95"),
+ :world_population => 6000000000,
+ :my_house_population => 3
+ )
+ assert m.save
+ assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
+ end
+
def test_numeric_fields
m = NumericData.new(
:bank_balance => 1586.43,
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index a1d1177289..f554ceef35 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -25,6 +25,40 @@ module ActiveRecord
assert ActiveRecord::Base.connection_handler.active_connections?
end
+ class FakeBase < ActiveRecord::Base
+ def self.establish_connection spec
+ String === spec ? super : spec
+ end
+ end
+
+ def test_url_host_no_db
+ spec = FakeBase.establish_connection 'postgres://foo?encoding=utf8'
+ assert_equal({
+ :adapter => "postgresql",
+ :database => "",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_host_db
+ spec = FakeBase.establish_connection 'postgres://foo/bar?encoding=utf8'
+ assert_equal({
+ :adapter => "postgresql",
+ :database => "bar",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_port
+ spec = FakeBase.establish_connection 'postgres://foo:123?encoding=utf8'
+ assert_equal({
+ :adapter => "postgresql",
+ :database => "",
+ :port => 123,
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
def test_app_delegation
manager = ConnectionManagement.new(@app)
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 3d2a03d2b9..5dc5f99582 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -666,6 +666,10 @@ class FinderTest < ActiveRecord::TestCase
assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
end
+ def test_find_by_two_attributes_but_passing_only_one
+ assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
+ end
+
def test_find_last_by_one_attribute
assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
assert_nil Topic.find_last_by_title("A title with no matches")
@@ -947,6 +951,10 @@ class FinderTest < ActiveRecord::TestCase
assert !another.persisted?
end
+ def test_find_or_initialize_from_two_attributes_but_passing_only_one
+ assert_raise(ArgumentError) { Topic.find_or_initialize_by_title_and_author_name("Another topic") }
+ end
+
def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
assert_equal 123, new_customer.balance.amount
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 2afe3c8f32..ed0240cada 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -497,14 +497,24 @@ end
class DynamicScopeTest < ActiveRecord::TestCase
fixtures :posts
+ def setup
+ @test_klass = Class.new(Post) do
+ def self.name; "Post"; end
+ end
+ end
+
def test_dynamic_scope
- assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1)
- assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
+ assert_equal @test_klass.scoped_by_author_id(1).find(1), @test_klass.find(1)
+ assert_equal @test_klass.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, @test_klass.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
end
def test_dynamic_scope_should_create_methods_after_hitting_method_missing
- assert_blank Developer.methods.grep(/scoped_by_created_at/)
- Developer.scoped_by_created_at(nil)
- assert_present Developer.methods.grep(/scoped_by_created_at/)
+ assert_blank @test_klass.methods.grep(/scoped_by_type/)
+ @test_klass.scoped_by_type(nil)
+ assert_present @test_klass.methods.grep(/scoped_by_type/)
+ end
+
+ def test_dynamic_scope_with_less_number_of_arguments
+ assert_raise(ArgumentError){ @test_klass.scoped_by_author_id_and_title(1) }
end
end
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 6568eb1d18..67a9ed6cd8 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -755,6 +755,11 @@ module NestedAttributesOnACollectionAssociationTests
Interest.reflect_on_association(:man).options[:inverse_of] = :interests
end
+ def test_can_use_symbols_as_object_identifier
+ @pirate.attributes = { :parrots_attributes => { :foo => { :name => 'Lovely Day' }, :bar => { :name => 'Blown Away' } } }
+ assert_nothing_raised(NoMethodError) { @pirate.save! }
+ end
+
private
def association_setter
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 677d659f39..382d659289 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -1,13 +1,8 @@
require "cases/helper"
require 'models/contact'
require 'models/topic'
-require 'models/reply'
-require 'models/company'
class SerializationTest < ActiveRecord::TestCase
-
- fixtures :topics, :companies, :accounts
-
FORMATS = [ :xml, :json ]
def setup
@@ -19,8 +14,6 @@ class SerializationTest < ActiveRecord::TestCase
:awesome => false,
:preferences => { :gem => '<strong>ruby</strong>' }
}
-
- @contact = Contact.new(@contact_attributes)
end
def test_serialized_init_with
@@ -29,134 +22,6 @@ class SerializationTest < ActiveRecord::TestCase
assert_equal 'foo', topic.content
end
- def test_to_xml
- xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
- bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
- written_on_in_current_timezone = topics(:first).written_on.xmlschema
- last_read_in_current_timezone = topics(:first).last_read.xmlschema
-
- assert_equal "topic", xml.root.name
- assert_equal "The First Topic" , xml.elements["//title"].text
- assert_equal "David" , xml.elements["//author-name"].text
- assert_match "Have a nice day", xml.elements["//content"].text
-
- assert_equal "1", xml.elements["//id"].text
- assert_equal "integer" , xml.elements["//id"].attributes['type']
-
- assert_equal "1", xml.elements["//replies-count"].text
- assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
-
- assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
- assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
-
- assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
-
- assert_equal nil, xml.elements["//parent-id"].text
- assert_equal "integer", xml.elements["//parent-id"].attributes['type']
- assert_equal "true", xml.elements["//parent-id"].attributes['nil']
-
- if current_adapter?(:SybaseAdapter)
- assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
- assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
- else
- # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
- assert_equal "2004-04-15", xml.elements["//last-read"].text
- assert_equal "date" , xml.elements["//last-read"].attributes['type']
- end
-
- # Oracle and DB2 don't have true boolean or time-only fields
- unless current_adapter?(:OracleAdapter, :DB2Adapter)
- assert_equal "false", xml.elements["//approved"].text
- assert_equal "boolean" , xml.elements["//approved"].attributes['type']
-
- assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
- assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
- end
- end
-
- def test_to_xml_skipping_attributes
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
- assert_equal "<topic>", xml.first(7)
- assert !xml.include?(%(<title>The First Topic</title>))
- assert xml.include?(%(<author-name>David</author-name>))
-
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
- assert !xml.include?(%(<title>The First Topic</title>))
- assert !xml.include?(%(<author-name>David</author-name>))
- end
-
- def test_to_xml_including_has_many_association
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
- assert_equal "<topic>", xml.first(7)
- assert xml.include?(%(<replies type="array"><reply>))
- assert xml.include?(%(<title>The Second Topic of the day</title>))
- end
-
- def test_array_to_xml_including_has_many_association
- xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
- assert xml.include?(%(<replies type="array"><reply>))
- end
-
- def test_array_to_xml_including_methods
- xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
- assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
- assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
- end
-
- def test_array_to_xml_including_has_one_association
- xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
- assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
- end
-
- def test_array_to_xml_including_belongs_to_association
- xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
- end
-
- def test_to_xml_including_belongs_to_association
- xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert !xml.include?("<firm>")
-
- xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert xml.include?("<firm>")
- end
-
- def test_to_xml_including_multiple_associations
- xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
- assert_equal "<firm>", xml.first(6)
- assert xml.include?(%(<account>))
- assert xml.include?(%(<clients type="array"><client>))
- end
-
- def test_to_xml_including_multiple_associations_with_options
- xml = companies(:first_firm).to_xml(
- :indent => 0, :skip_instruct => true,
- :include => { :clients => { :only => :name } }
- )
-
- assert_equal "<firm>", xml.first(6)
- assert xml.include?(%(<client><name>Summit</name></client>))
- assert xml.include?(%(<clients type="array"><client>))
- end
-
- def test_to_xml_including_methods
- xml = Company.new.to_xml(:methods => :arbitrary_method, :skip_instruct => true)
- assert_equal "<company>", xml.first(9)
- assert xml.include?(%(<arbitrary-method>I am Jack's profound disappointment</arbitrary-method>))
- end
-
- def test_to_xml_with_block
- value = "Rockin' the block"
- xml = Company.new.to_xml(:skip_instruct => true) do |_xml|
- _xml.tag! "arbitrary-element", value
- end
- assert_equal "<company>", xml.first(9)
- assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
- end
-
def test_serialize_should_be_reversible
for format in FORMATS
@serialized = Contact.new.send("to_#{format}")
@@ -184,11 +49,4 @@ class SerializationTest < ActiveRecord::TestCase
assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
end
end
-
- def test_serialize_should_xml_skip_instruct_for_included_records
- @contact.alternative = Contact.new(:name => 'Copa Cabana')
- @serialized = @contact.to_xml(:include => [ :alternative ])
- assert_equal @serialized.index('<?xml '), 0
- assert_nil @serialized.index('<?xml ', 1)
- end
end
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
index 756c8a32eb..88751a72f9 100644
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ b/activerecord/test/cases/xml_serialization_test.rb
@@ -5,6 +5,9 @@ require 'models/author'
require 'models/comment'
require 'models/company_in_module'
require 'models/toy'
+require 'models/topic'
+require 'models/reply'
+require 'models/company'
class XmlSerializationTest < ActiveRecord::TestCase
def test_should_serialize_default_root
@@ -50,6 +53,23 @@ class XmlSerializationTest < ActiveRecord::TestCase
end
assert_match %r{<creator>David</creator>}, @xml
end
+
+ def test_to_xml_with_block
+ value = "Rockin' the block"
+ xml = Contact.new.to_xml(:skip_instruct => true) do |_xml|
+ _xml.tag! "arbitrary-element", value
+ end
+ assert_equal "<contact>", xml.first(9)
+ assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
+ end
+
+ def test_should_skip_instruct_for_included_records
+ @contact = Contact.new
+ @contact.alternative = Contact.new(:name => 'Copa Cabana')
+ @xml = @contact.to_xml(:include => [ :alternative ])
+ assert_equal @xml.index('<?xml '), 0
+ assert_nil @xml.index('<?xml ', 1)
+ end
end
class DefaultXmlSerializationTest < ActiveRecord::TestCase
@@ -148,7 +168,63 @@ class NilXmlSerializationTest < ActiveRecord::TestCase
end
class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
- fixtures :authors, :posts, :projects
+ fixtures :topics, :companies, :accounts, :authors, :posts, :projects
+
+ def test_to_xml
+ xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
+ bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
+ written_on_in_current_timezone = topics(:first).written_on.xmlschema
+ last_read_in_current_timezone = topics(:first).last_read.xmlschema
+
+ assert_equal "topic", xml.root.name
+ assert_equal "The First Topic" , xml.elements["//title"].text
+ assert_equal "David" , xml.elements["//author-name"].text
+ assert_match "Have a nice day", xml.elements["//content"].text
+
+ assert_equal "1", xml.elements["//id"].text
+ assert_equal "integer" , xml.elements["//id"].attributes['type']
+
+ assert_equal "1", xml.elements["//replies-count"].text
+ assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
+
+ assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
+ assert_equal "datetime" , xml.elements["//written-on"].attributes['type']
+
+ assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
+
+ assert_equal nil, xml.elements["//parent-id"].text
+ assert_equal "integer", xml.elements["//parent-id"].attributes['type']
+ assert_equal "true", xml.elements["//parent-id"].attributes['nil']
+
+ if current_adapter?(:SybaseAdapter)
+ assert_equal last_read_in_current_timezone, xml.elements["//last-read"].text
+ assert_equal "datetime" , xml.elements["//last-read"].attributes['type']
+ else
+ # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
+ assert_equal "2004-04-15", xml.elements["//last-read"].text
+ assert_equal "date" , xml.elements["//last-read"].attributes['type']
+ end
+
+ # Oracle and DB2 don't have true boolean or time-only fields
+ unless current_adapter?(:OracleAdapter, :DB2Adapter)
+ assert_equal "false", xml.elements["//approved"].text
+ assert_equal "boolean" , xml.elements["//approved"].attributes['type']
+
+ assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
+ assert_equal "datetime" , xml.elements["//bonus-time"].attributes['type']
+ end
+ end
+
+ def test_except_option
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
+ assert_equal "<topic>", xml.first(7)
+ assert !xml.include?(%(<title>The First Topic</title>))
+ assert xml.include?(%(<author-name>David</author-name>))
+
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
+ assert !xml.include?(%(<title>The First Topic</title>))
+ assert !xml.include?(%(<author-name>David</author-name>))
+ end
# to_xml used to mess with the hash the user provided which
# caused the builder to be reused. This meant the document kept
@@ -184,6 +260,39 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
assert_match %r{<hello-post>}, xml
end
+ def test_including_has_many_association
+ xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
+ assert_equal "<topic>", xml.first(7)
+ assert xml.include?(%(<replies type="array"><reply>))
+ assert xml.include?(%(<title>The Second Topic of the day</title>))
+ end
+
+ def test_including_belongs_to_association
+ xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+ assert !xml.include?("<firm>")
+
+ xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+ assert xml.include?("<firm>")
+ end
+
+ def test_including_multiple_associations
+ xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
+ assert_equal "<firm>", xml.first(6)
+ assert xml.include?(%(<account>))
+ assert xml.include?(%(<clients type="array"><client>))
+ end
+
+ def test_including_association_with_options
+ xml = companies(:first_firm).to_xml(
+ :indent => 0, :skip_instruct => true,
+ :include => { :clients => { :only => :name } }
+ )
+
+ assert_equal "<firm>", xml.first(6)
+ assert xml.include?(%(<client><name>Summit</name></client>))
+ assert xml.include?(%(<clients type="array"><client>))
+ end
+
def test_methods_are_called_on_object
xml = authors(:david).to_xml :methods => :label, :indent => 0
assert_match %r{<label>.*</label>}, xml
@@ -265,4 +374,27 @@ class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
assert_equal array.size, array.select { |author| author.has_key? 'firstname' }.size
end
+ def test_array_to_xml_including_has_many_association
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
+ assert xml.include?(%(<replies type="array"><reply>))
+ end
+
+ def test_array_to_xml_including_methods
+ xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
+ assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
+ assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
+ end
+
+ def test_array_to_xml_including_has_one_association
+ xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
+ assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
+ assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
+ end
+
+ def test_array_to_xml_including_belongs_to_association
+ xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
+ assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
+ assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
+ assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
+ end
end
diff --git a/activerecord/test/fixtures/pirates.yml b/activerecord/test/fixtures/pirates.yml
index a47d894249..abb91101da 100644
--- a/activerecord/test/fixtures/pirates.yml
+++ b/activerecord/test/fixtures/pirates.yml
@@ -5,5 +5,5 @@ blackbeard:
redbeard:
catchphrase: "Avast!"
parrot: louis
- created_on: <%= 2.weeks.ago.utc.to_s(:db) %>
- updated_on: <%= 2.weeks.ago.utc.to_s(:db) %>
+ created_on: <%= 2.weeks.ago.to_s(:db) %>
+ updated_on: <%= 2.weeks.ago.to_s(:db) %>
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index 034d03533f..a25720adbf 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,5 +1,8 @@
*Rails 3.2.0 (unreleased)*
+* The definition of blank string for Ruby 1.9 has been extended to Unicode whitespace.
+Also, in 1.8 the ideographic space U+3000 is considered to be whitespace. [Akira Matsuda, Damien Mathieu]
+
* The inflector understands acronyms. [dlee]
* Deprecated ActiveSupport::Memoizable in favor of Ruby memoization pattern [José Valim]
diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb
index 1777a4b32d..41f9a76b13 100644
--- a/activesupport/lib/active_support/core_ext/module/delegation.rb
+++ b/activesupport/lib/active_support/core_ext/module/delegation.rb
@@ -127,10 +127,6 @@ class Module
end
module_eval(<<-EOS, file, line - 5)
- if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}")
- remove_possible_method("#{prefix}#{method}")
- end
-
def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
#{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
rescue NoMethodError # rescue NoMethodError
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index d060ac04d8..fe27f45295 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+require 'active_support/core_ext/string/encoding'
class Object
# An object is blank if it's false, empty, or a whitespace string.
@@ -99,7 +100,12 @@ class String
# " something here ".blank? # => false
#
def blank?
- self !~ NON_WHITESPACE_REGEXP
+ # 1.8 does not takes [:space:] properly
+ if encoding_aware?
+ self !~ /[^[:space:]]/
+ else
+ self !~ NON_WHITESPACE_REGEXP
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 664537eea4..0f8933b658 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require 'date'
require 'active_support/core_ext/time/publicize_conversion_methods'
require 'active_support/core_ext/time/calculations'
diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
index 3d28d33f40..423b5abd20 100644
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -118,8 +118,10 @@ module ActiveSupport
# +titleize+ is also aliased as as +titlecase+.
#
# Examples:
- # "man from the boondocks".titleize # => "Man From The Boondocks"
- # "x-men: the last stand".titleize # => "X Men: The Last Stand"
+ # "man from the boondocks".titleize # => "Man From The Boondocks"
+ # "x-men: the last stand".titleize # => "X Men: The Last Stand"
+ # "TheManWithoutAPast".titleize # => "The Man Without A Past"
+ # "raiders_of_the_lost_ark".titleize # => "Raiders Of The Lost Ark"
def titleize(word)
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
end
diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb
index f9607f1aaf..b78d92f599 100644
--- a/activesupport/lib/active_support/multibyte/chars.rb
+++ b/activesupport/lib/active_support/multibyte/chars.rb
@@ -331,8 +331,7 @@ module ActiveSupport #:nodoc:
# when the storage for a string is limited for some reason.
#
# Example:
- # s = 'こんにちは'
- # s.mb_chars.limit(7) # => "こに"
+ # 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
def limit(limit)
slice(0...translate_offset(limit))
end
diff --git a/activesupport/lib/active_support/multibyte/unicode.rb b/activesupport/lib/active_support/multibyte/unicode.rb
index 513f83e445..754ca9290b 100644
--- a/activesupport/lib/active_support/multibyte/unicode.rb
+++ b/activesupport/lib/active_support/multibyte/unicode.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
module ActiveSupport
module Multibyte
module Unicode
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 4000cc913a..18a86e08f5 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -158,7 +158,7 @@ class StringInflectionsTest < Test::Unit::TestCase
assert_equal Time.local(2005, 2, 27, 23, 50, 19, 275038), "2005-02-27T23:50:19.275038".to_time(:local)
assert_equal DateTime.civil(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time
assert_equal Time.local_time(2039, 2, 27, 23, 50), "2039-02-27 23:50".to_time(:local)
- assert_equal Time.utc(2039, 2, 27, 23, 50), "2039-02-27 22:50 -0100".to_time
+ assert_equal Time.utc(2011, 2, 27, 23, 50), "2011-02-27 22:50 -0100".to_time
assert_nil "".to_time
end
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 6ccffa59b1..201729a6c2 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -1,4 +1,4 @@
-# encoding: UTF-8
+# encoding: utf-8
require 'abstract_unit'
require 'active_support/json'
require 'active_support/time'
diff --git a/ci/travis.rb b/ci/travis.rb
new file mode 100755
index 0000000000..8087c72f90
--- /dev/null
+++ b/ci/travis.rb
@@ -0,0 +1,142 @@
+#!/usr/bin/env ruby
+require 'fileutils'
+include FileUtils
+
+commands = [
+ 'mysql -e "create database activerecord_unittest;"',
+ 'mysql -e "create database activerecord_unittest2;"',
+ 'psql -c "create database activerecord_unittest;" -U postgres',
+ 'psql -c "create database activerecord_unittest2;" -U postgres'
+]
+
+commands.each do |command|
+ system("#{command} > /dev/null 2>&1")
+end
+
+class Build
+ MAP = {
+ 'railties' => 'railties',
+ 'ap' => 'actionpack',
+ 'am' => 'actionmailer',
+ 'amo' => 'activemodel',
+ 'ares' => 'activeresource',
+ 'as' => 'activesupport',
+ 'ar' => 'activerecord'
+ }
+
+ attr_reader :component, :options
+
+ def initialize(component, options = {})
+ @component = component
+ @options = options
+ end
+
+ def run!(options = {})
+ self.options.update(options)
+ Dir.chdir(dir) do
+ announce(heading)
+ ENV['IM'] = identity_map?.inspect
+ rake(*tasks)
+ end
+ end
+
+ def announce(heading)
+ puts "\n\e[1;33m[Travis CI] #{heading}\e[m\n"
+ end
+
+ def heading
+ heading = [gem]
+ heading << "with #{adapter} IM #{identity_map? ? 'enabled' : 'disabled'}" if activerecord?
+ heading << "in isolation" if isolated?
+ heading.join(' ')
+ end
+
+ def tasks
+ if activerecord?
+ ['mysql:rebuild_databases', "#{adapter}:#{'isolated_' if isolated?}test"]
+ else
+ ["test#{':isolated' if isolated?}"]
+ end
+ end
+
+ def key
+ key = [gem]
+ key << adapter if activerecord?
+ key << 'IM' if identity_map?
+ key << 'isolated' if isolated?
+ key.join(':')
+ end
+
+ def activerecord?
+ gem == 'activerecord'
+ end
+
+ def identity_map?
+ options[:identity_map]
+ end
+
+ def isolated?
+ options[:isolated]
+ end
+
+ def gem
+ MAP[component.split(':').first]
+ end
+ alias :dir :gem
+
+ def adapter
+ component.split(':').last
+ end
+
+ def rake(*tasks)
+ tasks.each do |task|
+ cmd = "bundle exec rake #{task}"
+ puts "Running command: #{cmd}"
+ return false unless system(cmd)
+ end
+ true
+ end
+end
+
+results = {}
+
+ENV['GEM'].split(',').each do |gem|
+ [false, true].each do |isolated|
+ next if gem == 'railties' && isolated
+
+ build = Build.new(gem, :isolated => isolated)
+ results[build.key] = build.run!
+
+ if build.activerecord?
+ build.options[:identity_map] = true
+ results[build.key] = build.run!
+ end
+ end
+end
+
+# puts
+# puts "Build environment:"
+# puts " #{`cat /etc/issue`}"
+# puts " #{`uname -a`}"
+# puts " #{`ruby -v`}"
+# puts " #{`mysql --version`}"
+# # puts " #{`pg_config --version`}"
+# puts " SQLite3: #{`sqlite3 -version`}"
+# `gem env`.each_line {|line| print " #{line}"}
+# puts " Bundled gems:"
+# `bundle show`.each_line {|line| print " #{line}"}
+# puts " Local gems:"
+# `gem list`.each_line {|line| print " #{line}"}
+
+failures = results.select { |key, value| value == false }
+
+if failures.empty?
+ puts
+ puts "Rails build finished sucessfully"
+ exit(true)
+else
+ puts
+ puts "Rails build FAILED"
+ puts "Failed components: #{failures.map { |component| component.first }.join(', ')}"
+ exit(false)
+end
diff --git a/load_paths.rb b/load_paths.rb
index 590af69330..17f5ce180d 100644
--- a/load_paths.rb
+++ b/load_paths.rb
@@ -1,8 +1,4 @@
# bust gem prelude
-if defined? Gem
- gem 'bundler'
-else
- require 'rubygems'
-end
+require 'rubygems' unless defined? Gem
require 'bundler'
-Bundler.setup
+Bundler.setup \ No newline at end of file
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 082f9eda7d..8ea06d28aa 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -1016,6 +1016,7 @@ You can specify an exclamation point (<tt>!</tt>) on the end of the dynamic find
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)+.
+WARNING: Up to and including Rails 3.1, when the number of arguments passed to a dynamic finder method is lesser than the number of fields, say <tt>Client.find_by_name_and_locked("Ryan")</tt>, the behavior is to pass +nil+ as the missing argument. This is *unintentional* and this behavior will be changed in Rails 3.2 to throw an +ArgumentError+.
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 first 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")+:
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index a0ed85cf01..b35e04d7e1 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -78,12 +78,14 @@ The following values are considered to be blank in a Rails application:
* +nil+ and +false+,
-* strings composed only of whitespace, i.e. matching +/\A\s*\z/+,
+* strings composed only of whitespace (see note below),
* empty arrays and hashes, and
* any other object that responds to +empty?+ and it is empty.
+INFO: In Ruby 1.9 the predicate for strings uses the Unicode-aware character class <tt>[:space:]</tt>, so for example U+2029 (paragraph separator) is considered to be whitespace. In Ruby 1.8 whitespace is considered to be <tt>\s</tt> together with the ideographic space U+3000.
+
WARNING: Note that numbers are not mentioned, in particular 0 and 0.0 are *not* blank.
For example, this method from +ActionDispatch::Session::AbstractStore+ uses +blank?+ for checking whether a session key is present:
diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile
index 563c1c79ae..5999c78369 100644
--- a/railties/guides/source/asset_pipeline.textile
+++ b/railties/guides/source/asset_pipeline.textile
@@ -11,9 +11,9 @@ By referring to this guide you will be able to:
endprologue.
-h3. What Is The Asset Pipeline?
+h3. What is the Asset Pipeline?
-The asset pipeline provides a framework to concatenate and minify or compress Javascript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, SCSS and ERB.
+The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, SCSS and ERB.
Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets. Rails 3.1 includes the +sprockets-rails+ gem, which depends on the +sprockets+ gem, by default.
@@ -22,7 +22,7 @@ By having this as a core feature of Rails, all developers can benefit from the p
In new Rails 3.1 application the asset pipeline is enable by default. It can be disabled in +application.rb+ by putting this line inside the +Application+ class definition:
<plain>
- config.assets.enabled = false
+config.assets.enabled = false
</plain>
It is recommended that you use the defaults for all new apps.
@@ -30,21 +30,19 @@ It is recommended that you use the defaults for all new apps.
h4. Main Features
-The first is to concatenate of assets. This is important in a production environment to reduce the number of requests that a client browser has to make to render a web page. While Rails already has a feature to concatenate these types of asset--by placing +:cache => true+ at the end of tags such as +javascript_include_tag+ and +stylesheet_link_tag+--, many people do not use it.
+The first feature of the pipeline is to concatenate assets. This is important in a production environment, as it reduces the number of requests that a browser needs to make to render a web page. While Rails already has a feature to concatenate these types of asset--by placing +:cache => true+ at the end of tags such as +javascript_include_tag+ and +stylesheet_link_tag+--, many people do not use it.
-The default behavior in Rails 3.1 and onward is to concatenate all files into one master file each for JS and CSS, however you can separate files or groups of files if required (see below). In production an MD5 fingerprint is inserted into each filename.
+The default behavior in Rails 3.1 and onward is to concatenate all files into one master file each for JS and CSS. However, you can separate files or groups of files if required (see below). In production an MD5 fingerprint is inserted into each filename so that the file is cached by the web browser but can be invalidated if the fingerprint is altered.
-The second feature of the pipeline is to minify or compress. For CSS this usually involves removing whitespace and comments. For Javascript more complex processes can be applied.
+The second feature is to minify or compress. For CSS, this usually involves removing whitespace and comments. For JavaScript, more complex processes can be applied. You can choose from a set of built in options or specify your own.
-You can choose from a set of built in options or specify your own.
+The third feature is the ability to code these assets using another language, or language extension. These include SCSS or Sass for CSS, CoffeeScript for JavaScript, and ERB for both.
-The third feature is the ability to code these assets using another language, or language extension. These include SCSS or Sass for CSS, CoffeeScript for Javascript, and ERB for both.
-
-h4. What is fingerprinting and why should I care?
+h4. What is Fingerprinting and Why Should I Care?
Fingerprinting is a technique where the filenames of content that is static or infrequently updated is altered to be unique to the content contained in the file.
-When a filename is unique and based on its content, http headers can be set to encourage caches everywhere (at ISPs, in browsers) to keep there own copy of the content. When the content is updated, the fingerprint will change and the remote clients will request the new file. This is generally known as _cachebusting_.
+When a filename is unique and based on its content, HTTP headers can be set to encourage caches everywhere (at ISPs, in browsers) to keep their own copy of the content. When the content is updated, the fingerprint will change and the remote clients will request the new file. This is generally known as _cachebusting_.
The most effective technique is to insert a hash of the content into the name, usually at the end. For example a CSS file +global.css+ is hashed and the filename is updated to incorporate the hash.
@@ -62,17 +60,20 @@ Rails old strategy was to append a query string to every asset linked with a bui
This has several disadvantages:
-1. Not all caches will cache content with a query string
-
-"Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in these case 5-20% of requests will not be cached.
-
-2. The filename can change between nodes in multi-server environments.
-
-The query string in Rails is based on the files mtime (mtime is the file modification time). When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request.
+<ol>
+ <li>
+ <strong>Not all caches will cache content with a query string</strong><br>
+ "Steve Souders recommends":http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/, "...avoiding a querystring for cacheable resources". He found that in these case 5-20% of requests will not be cached.
+ </li>
+ <li>
+ <strong>The file name can change between nodes in multi-server environments.</strong><br>
+ The query string in Rails is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request.
+ </li>
+</ol>
-The other problems is that when static assets are deployed with each new release of code, the mtime of *all* these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.
+The other problem is that when static assets are deployed with each new release of code, the mtime of *all* these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.
-Fingerprinting avoids all these problems be ensuring filenames are consistent based on the content.
+Fingerprinting avoids all these problems by ensuring filenames are consistent based on the content.
More reading:
@@ -84,7 +85,7 @@ h3. How to Use the Asset Pipeline
In previous versions of Rails, all assets were located in subdirectories of +public+ such as +images+, +javascripts+ and +stylesheets+. With the asset pipeline, the preferred location for these assets is now the +app/assets+ directory. Files in this directory will be served by the Sprockets middleware included in the sprockets gem.
-This is not to say that assets can (or should) no longer be placed in +public+. They still can be and will be served as static files by the application or web server. You would only use +app/assets+ if you wish your files to undergo some pre-processing before they are served.
+This is not to say that assets can (or should) no longer be placed in +public+; they still can be and will be served as static files by the application or web server. You would only use +app/assets+ if you wish your files to undergo some pre-processing before they are served.
When a scaffold or controller is generated for the application, Rails will also generate a JavaScript file (or CoffeeScript if the +coffee-script+ gem is in the +Gemfile+) and a Cascading Style Sheet file (or SCSS if +sass-rails+ is in the +Gemfile+) file for that controller.
@@ -100,32 +101,72 @@ Assets can be placed inside an application in one of three locations: +app/asset
+vendor/assets+ is for assets that are owned by outside entities, such as code for JavaScript plugins.
-All subdirectories that exists within these three locations will be added to the search path for Sprockets (visible by calling +Rails.application.config.assets.paths+ in a console). When an asset is requested, these paths will be looked through to see if they contain an asset matching the name specified. Once an asset has been found, it's processed by Sprockets and served.
+All subdirectories that exist within these three locations will be added to the search path for Sprockets (visible by calling +Rails.application.config.assets.paths+ in a console). When an asset is requested, these paths will be looked through to see if they contain an asset matching the name specified. Once an asset has been found, it's processed by Sprockets and served.
-h4. Coding links to Assets
+h4. Coding Links to Assets
To access assets, we can use the same tags that we are generally familiar with:
+Sprockets does not add any new methods to require your assets, we still use the familiar +javascript_include_tag+ and +stylesheet_link_tag+.
+
<erb>
- <%= image_tag "rails.png" %>
+<%= stylesheet_link_tag "application" %>
+<%= javascript_include_tag "application" %>
</erb>
-Providing that assets are enabled within our application (+config.assets.enabled+ in the current environment's file is not set to +false+), this file will be served by Sprockets unless a file at +public/assets/rails.png+ exists, in which case that file will be served. Alternatively, a file with an MD5 hash after its name such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ will also be picked up by Sprockets. How these hashes are generated is covered in the "Production Assets":#production_assets section later on in this guide.
+In regular views you can access images in the +assets/images+ directory like this:
-Otherwise, Sprockets will look through the available paths until it finds a file that matches the name and then will serve it, first looking in the application's assets directories and then falling back to the various engines of the application.
+<erb>
+<%= image_tag "rails.png" %>
+</erb>
-Sprockets does not add any new methods to require your assets, we still use the familiar +javascript_include_tag+ and +stylesheet_link_tag+.
+Images can be organized into directories if required, and they can be accessed by specifying the directory's name in the tag:
<erb>
- <%= stylesheet_link_tag "application" %>
- <%= javascript_include_tag "application" %>
+<%= image_tag "icons/rails.png" %>
</erb>
-These helpers (when the pipeline is on) are providing links to the compiled manifest with the specified name (or names).
+Providing that assets are enabled within our application (+config.assets.enabled+ in the current environment's file is not set to +false+), this file will be served by Sprockets unless a file at +public/assets/rails.png+ exists, in which case that file will be served.
+
+Alternatively, a file with an MD5 hash after its name such as +public/assets/rails-af27b6a414e6da00003503148be9b409.png+ will also be picked up by Sprockets. How these hashes are generated is covered in the "Production Assets":#production_assets section later on in this guide.
+
+Otherwise, Sprockets will look through the available paths until it finds a file that matches the name and then will serve it, first looking in the application's assets directories and then falling back to the various engines of the application.
+
+If you want to use a "css data URI":http://en.wikipedia.org/wiki/Data_URI_scheme - a method of embedding the image data directly into the CSS file - you can use the +asset_data_uri+ helper.
+
+<plain>
+#logo { background: url(<%= asset_data_uri 'logo.png' %>)
+</plain>
+
+This will insert a correctly formatted data URI into the CSS source.
+
+h5. CSS and ERB
+
+If you add an +erb+ extension to a CSS asset, making it something such as +application.css.erb+ then you can use the +asset_path+ helper in your CSS rules:
+
+<plain>
+.class{background-image:<%= asset_path 'image.png' %>}
+</plain>
+
+This will write the path to the particular asset being referenced. In this example, it would make sense to have an image in one of the asset load paths, such as +app/assets/images/image.png+, which would be referenced here. If this image is already available in +public/assets+ as a fingerprinted file then that path will be referenced.
+
+Note that the closing tag cannot be of the style +-%>+.
+
+h5. CSS and SCSS
+
+When using the asset pipeline, paths to assets must be re-written and +sass-rails+ provides +_url+ and +_path+ helpers for the following asset classes: image, font, video, audio, javascript, stylesheet.
+
+* +image_url("rails.png")+ becomes +url(/assets/rails.png)+
+* +image_path("rails.png")+ becomes +"/assets/rails.png"+.
+
+The more generic form can also be used but the asset path and class must both be specified:
+
+* +asset_url("rails.png", "image")+ becomes +url(/assets/rails.png)+
+* +asset_path("rails.png", "image")+ becomes +"/assets/rails.png"+
h4. Manifest Files and Directives
-Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ - instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets will load the files specified, process them if necessary, concatenate them into one single file and then compress them (if +Rails.application.config.assets.compress+ is set to +true+). By serving one file rather than many, a page's load time is greatly reduced.
+Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ - instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets will load the files specified, process them if necessary, concatenate them into one single file and then compress them (if +Rails.application.config.assets.compress+ is set to +true+). By serving one file rather than many, a page's load time is greatly reduced as there is not as many requests to make for each file.
For example, in the default Rails application there's a +app/assets/javascripts/application.js+ file which contains the following lines:
@@ -167,19 +208,17 @@ For some assets (like CSS) the compiled order is important. You can specify indi
h4. Preprocessing
-The file extensions used on an asset will determine what preprocssing will be applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file will be generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and a +app/assets/stylesheets/projects.css.scss+ file.
+The file extensions used on an asset will determine what preprocessing will be applied. When a controller or a scaffold is generated with the default Rails gemset, a CoffeeScript file and a SCSS file will be generated in place of a regular JavaScript and CSS file. The example used before was a controller called "projects", which generated an +app/assets/javascripts/projects.js.coffee+ and a +app/assets/stylesheets/projects.css.scss+ file.
When these files are requested, they will be processed by the processors provided by the +coffee-script+ and +sass-rails+ gems and then sent back to the browser as JavaScript and CSS respectively.
-Additional layers of pre-processing can be requested by adding other extensions. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ would first be processed as ERB, then SCSS and finally served as CSS. The same applies to a JavaScript file - +app/assets/javascripts/projects.js.coffee.erb+ would be process as ERB, CoffeeScript and served as JavaScript.
+Additional layers of pre-processing can be requested by adding other extensions, where each extension will be processed in a right-to-left manner. These should be used in the order the processing should be applied. For example, a stylesheet called +app/assets/stylesheets/projects.css.scss.erb+ would first be processed as ERB, then SCSS and finally served as CSS. The same applies to a JavaScript file - +app/assets/javascripts/projects.js.coffee.erb+ would be process as ERB, CoffeeScript and served as JavaScript.
Keep in mind that the order of these pre-processors is important. For example, if we called our JavaScript file +app/assets/javascripts/projects.js.erb.coffee+ then it would be processed with the CoffeeScript interpreter first, which wouldn't understand ERB and therefore we would run into problems.
h3. In Development
-TODO: Talk about: Rack::Cache's caching (used in dev and production. The only difference is hashing and headers).
-
-In the development environment assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-validate+ cache-control http header to reduce request overhead on subsequent requests - on these the browser gets a 304 (not-modified) response.
+In the development environment assets are compiled and cached on the first request after the server is started. Sprockets sets a +must-validate+ Cache-Control HTTP header to reduce request overhead on subsequent requests - on these the browser gets a 304 (not-modified) response.
If any of the files in the manifest have changed between requests, the server will respond with a new compiled file.
@@ -195,15 +234,15 @@ You can put +?debug_assets=true+ or +?debug_assets=1+ at the end of a URL and Sp
By default, this would only render this line when used with +<%= javascript_include_tag "application" %>+ in a view or layout:
<html>
- <script src='/assets/application.js'></script>
+<script src='/assets/application.js'></script>
</html>
When the +debug_assets+ parameter is set, this line will be expanded out into three separate lines, separating out the combined file into their parts.
<html>
- <script src='/assets/application.js'></script>
- <script src='/assets/projects.js'></script>
- <script src='/assets/tickets.js'></script>
+<script src='/assets/application.js'></script>
+<script src='/assets/projects.js'></script>
+<script src='/assets/tickets.js'></script>
</html>
This allows the individual parts of an asset to be rendered and debugged separately.
@@ -219,80 +258,105 @@ On the first request the assets are compiled and cached as described above, howe
/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css
</plain>
-The MD5 is generated from the contents of the compiled files, and is included in the http +Content-MD5+ header.
+The MD5 is generated from the contents of the compiled files, and is included in the HTTP +Content-MD5+ header.
-Sprockets also sets the +Cache-Control+ http header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
+Sprockets also sets the +Cache-Control+ HTTP header to +max-age=31536000+. This signals all caches between your server and the client browser that this content (the file served) can be cached for 1 year. The effect of this is to reduce the number of requests for this asset from your server; the asset has a good chance of being in the local browser cache or some intermediate cache.
This behavior is controlled by the setting of +config.action_controller.perform_caching+ setting in Rails (which is +true+ for production, +false+ for everything else). This value is propagated to Sprockets during initialization for use when action_controller is not available.
-TODO:
-describe each and the differences between:
- * Sass-rails's handy +image_url+ helpers
- * ERB pre-processing and +asset_path+
-
-h4. Precompiling assets
+h4. Precompiling Assets
Even though assets are served by Rack::Cache with far-future headers, in high traffic sites this may not be fast enough.
Rails comes bundled with a rake task to compile the manifests to files on disc. These are located in the +public/assets+ directory where they will be served by your web server instead of the Rails application.
-TODO: Add section about image assets
-
The rake task is:
-<erb>
+<plain>
rake assets:precompile
+</plain>
+
+You can run this as part of a Capistrano deployment:
+
+<erb>
+before 'deploy:symlink' do
+ run "cd #{release_path}; RAILS_ENV=#{rails_env} rake assets:precompile"
+end
</erb>
-TODO: explain where to use this with Capistrano
+If you are not precompiling your assets, and you are using the default cache file store (which is the file system), you will need to symlink +rails_root/tmp/cache/assets+ from the shared folder that is part of the Capistrano deployment structure in order to persist the cached file between deployments.
-TODO: talk about the +config.assets.precompile+ option and the default matcher for files:
+TODO: Extend above task to allow for this and add task to set it up (See commits 8f0e0b6 and 704ee0df). Note: Capistrano folks are working on a recipe - update this when it available (see https://github.com/capistrano/capistrano/pull/35).
+
+The default matcher for compiling files will include +application.js+, +application.css+ and all files that do not end in +js+ or +css+:
+
+<ruby>
+[ /\w+\.(?!js|css).+/, /application.(css|js)$/ ]
+</ruby>
+
+If you have other manifests or individual stylesheets and JavaScript files to include, you can append them to the +precompile+ array:
<erb>
-[ /\w+\.(?!js|css).+/, "application.js", "application.css" ]
+config.assets.precompile << ['admin.js', 'admin.css', 'swfObject.js']
</erb>
+Precompiled assets exist on the filesystem and are served directly by your webserver. They do not have far-future headers by default, so to get the benefit of fingerprinting you'll have to update your server configuration to add them.
+
+For Apache:
+
+<plain>
+<LocationMatch "^/assets/.*$">
+ # Some browsers still send conditional-GET requests if there's a
+ # Last-Modified header or an ETag header even if they haven't
+ # reached the expiry date sent in the Expires header.
+ Header unset Last-Modified
+ Header unset ETag
+ FileETag None
+ # RFC says only cache for 1 year
+ ExpiresActive On
+ ExpiresDefault "access plus 1 year"
+</LocationMatch>
+</plain>
+
+TODO: NGINX instructions
-Sprockets also creates a "gzip":http://en.wikipedia.org/wiki/Gzip (.gz) of your assets. This prevents your server from contently compressing your assets for each request. You must configure your server to use gzip compression and serve the compressed assets that will be stored in the public/assets folder. The following are some configuration blocks that you can use for common servers.
-NGINX & Apache examples?
+When files are precompiled Sprockets also creates "Gzip":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. This avoids the server having to do this for any requests; it can simply read the compressed files from disc. You must configure your server to use gzip compression and serve the compressed assets that will be stored in the public/assets folder. The following configuration options can be used:
+TODO: Apache instructions
-h3. Customizing The Pipeline
+h3. Customizing the Pipeline
-h4. CSS
-There is currently one option for processing CSS - SCSS. This Gem extends the CSS syntax and offers minification.
+h4. CSS Compression
-The following line will enable SCSS in you project.
+There is currently one option for compressing CSS - YUI. This Gem extends the CSS syntax and offers minification.
+
+The following line will enable YUI compression, and requires the +yui-compressor+ gem.
<erb>
-config.assets.css_compressor = :scss
+config.assets.css_compressor = :yui
</erb>
-This option is for compression only and does not relate to the SCSS language extensions that apply when using the +.scss+ file extension on CSS assets.
-
-h4. Javascript
+The +config.assets.compress+ must be set to +true+ to enable CSS compression
-There are three options available to process javascript - uglifier, closure and yui.
+h4. JavaScript
-The default Gemfile includes "uglifier":https://github.com/lautis/uglifier. This gem wraps "UglifierJS":https://github.com/mishoo/UglifyJS (written for NodeJS) in Ruby. It compress your code by removing white spaces and other magical things like changing your if and else statements to ternary operators when possible.
+Possible options for JavaScript compression are +:closure+, +:uglifier+ and +:yui+. These require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems respectively.
-TODO: Add detail about the other two
+The default Gemfile includes "uglifier":https://github.com/lautis/uglifier. This gem wraps "UglifierJS":https://github.com/mishoo/UglifyJS (written for NodeJS) in Ruby. It compress your code by removing white spaces and other magical things like changing your +if+ and +else+ statements to ternary operators where possible.
-The following line will invoke uglifier for Javascript compression.
+The following line will invoke uglifier for JavaScript compression.
<erb>
config.assets.js_compressor = :uglifier
</erb>
+The +config.assets.compress+ must be set to +true+ to enable JavaScript compression
+h4. Using Your Own Compressor
-h4. Using your own compressor
-
-The compressor config settings for CSS and Javascript will also take an Object.
-
-This object must have a +compress+ method that takes a string as the sole argument and it must return a string.
+The compressor config settings for CSS and JavaScript will also take any Object. This object must have a +compress+ method that takes a string as the sole argument and it must return a string.
<erb>
class Transformer
@@ -309,7 +373,7 @@ config.assets.css_compressor = Transformer.new
</erb>
-h4. Changing the _assets_ path
+h4. Changing the _assets_ Path
The public path that Sprockets uses by default is +/assets+.
@@ -319,8 +383,27 @@ This can be changed to something else:
config.assets.prefix = "/some_other_path"
</erb>
-This is a handy option if you have any existing project (pre Rails 3.1) that already uses this path.
+This is a handy option if you have any existing project (pre Rails 3.1) that already uses this path or you wish to use this path for a new resource.
+h4. X-Sendfile Headers
+
+The X-Sendfile header is a directive to the server to ignore the response from the application, and instead serve the file specified in the headers. In production Rails (via Sprockets) does not send the asset - just the location and a zero-length response - relying on the web server to do the file serving, which is usually faster. Both Apache and nginx support this option.
+
+The configuration is available in <tt>config/environments/production.rb</tt>.
+
+<erb>
+config.action_dispatch.x_sendfile_header = "X-Sendfile" # Use 'X-Accel-Redirect' for nginx
+</erb>
+
+You should check that your server or hosting service actually supports this, otherwise comment it out.
+
+WARNING: If you are upgrading an existing application and intend to use this option, take care to paste this configuration option only into +production.rb+ (and not +application.rb+) and any other environment you define with production behaviour.
+
+h3. How Caching Works
+
+Sprockets uses the default rails cache store to cache assets in dev and production. The only difference is file names are fingerprinted and get far-future headers in production.
+
+TODO: Add more about changing the default store.
h3. Adding Assets to Your Gems
@@ -330,6 +413,4 @@ A good example of this is the +jquery-rails+ gem which comes with Rails as the s
h3. Making Your Library or Gem a Pre-Processor
-"You should be able to register [your gems] on Tilt and Sprockets will find them." - Josh
-Tilt: https://github.com/rtomayko/tilt
-
+TODO: Registering gems on "Tilt":https://github.com/rtomayko/tilt enabling Sprockets to find them.
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 252003edd0..ae56911441 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -15,7 +15,7 @@ h3. Basic Caching
This is an introduction to the three types of caching techniques that Rails provides by default without the use of any third party plugins.
-To start playing with testing you'll want to ensure that +config.action_controller.perform_caching+ is set to +true+ if you're running in development mode. This flag is normally set in the corresponding +config/environments/*.rb+ and caching is disabled by default for development and test, and enabled for production.
+To start playing with caching you'll want to ensure that +config.action_controller.perform_caching+ is set to +true+, if you're running in development mode. This flag is normally set in the corresponding +config/environments/*.rb+ and caching is disabled by default for development and test, and enabled for production.
<ruby>
config.action_controller.perform_caching = true
@@ -23,9 +23,9 @@ config.action_controller.perform_caching = true
h4. Page Caching
-Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
+Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. Apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with.
-So, how do you enable this super-fast cache behavior? Simple, let's say you have a controller called +ProductsController+ and an +index+ action that lists all the products
+To enable page caching, you need to use the +caches_page+ method.
<ruby>
class ProductsController < ActionController
@@ -35,11 +35,10 @@ class ProductsController < ActionController
def index
@products = Products.all
end
-
end
</ruby>
-The first time anyone requests +/products+, Rails will generate a file called +products.html+ and the webserver will then look for that file before it passes the next request for +/products+ to your Rails application.
+Let's say you have a controller called +ProductsController+ and an +index+ action that lists all the products. The first time anyone requests +/products+, Rails will generate a file called +products.html+ and the webserver will then look for that file before it passes the next request for +/products+ to your Rails application.
By default, the page cache directory is set to +Rails.public_path+ (which is usually set to the +public+ folder) and this can be configured by changing the configuration setting +config.action_controller.page_cache_directory+. Changing the default from +public+ helps avoid naming conflicts, since you may want to put other static html in +public+, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from.
@@ -104,7 +103,7 @@ INFO: Action caching runs in an after filter. Thus, invalid requests won't gener
h4. Fragment Caching
-Life would be perfect if we could get away with caching the entire contents of a page or action and serving it out to the world. Unfortunately, dynamic web applications usually build pages with a variety of components not all of which have the same caching characteristics. In order to address such a dynamically created page where different parts of the page need to be cached and expired differently Rails provides a mechanism called Fragment Caching.
+Life would be perfect if we could get away with caching the entire contents of a page or action and serving it out to the world. Unfortunately, dynamic web applications usually build pages with a variety of components not all of which have the same caching characteristics. In order to address such a dynamically created page where different parts of the page need to be cached and expired differently, Rails provides a mechanism called Fragment Caching.
Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.
@@ -416,3 +415,4 @@ h3. Changelog
* December 27, 2008: Typo fixes
* November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
* September 15, 2008: Initial version by Aditya Chadha
+
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index 9e3b25d794..b34506d4d8 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -85,6 +85,8 @@ h4. +rails generate+
The +rails generate+ command uses templates to create a whole lot of things. Running +rails generate+ by itself gives a list of available generators:
+You can also use the alias "g" to invoke the generator command: <tt>rails g</tt>.
+
<shell>
$ rails generate
Usage: rails generate GENERATOR [args] [options]
@@ -311,6 +313,8 @@ h4. +rails runner+
$ rails runner "Model.long_running_method"
</shell>
+You can also use the alias "r" to invoke the runner: <tt>rails r</tt>.
+
You can specify the environment in which the +runner+ command should operate using the +-e+ switch.
<shell>
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 8e6010ff79..7ed958be08 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -120,10 +120,12 @@ h4. Configuring Assets
Rails 3.1, by default, is set up to use the +sprockets+ gem to manage assets within an application. This gem concatenates and compresses assets in order to make serving them much less painful.
-* +config.assets.css_compressor+ defines the CSS compressor to use. Only supported value at the moment is +:yui+, which uses the +yui-compressor+ gem.
-
* +config.assets.enabled+ a flag that controls whether the asset pipeline is enabled. It is explicitly initialized in +config/application.rb+.
+* +config.assets.compress+ a flag that enables the compression of compiled assets. It is explicitly set to true in +config/production.rb+.
+
+* +config.assets.css_compressor+ defines the CSS compressor to use. Only supported value at the moment is +:yui+, which uses the +yui-compressor+ gem.
+
* +config.assets.js_compressor+ defines the JavaScript compressor to use. Possible values are +:closure+, +:uglifier+ and +:yui+ which require the use of the +closure-compiler+, +uglifier+ or +yui-compressor+ gems respectively.
* +config.assets.paths+ contains the paths which are used to look for assets. Appending paths to this configuration option will cause those paths to be used in the search for assets.
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 9051ede9dd..96a52af612 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -170,27 +170,34 @@ IMPORTANT: Always use labels for each checkbox and radio button. They associate
h4. Other Helpers of Interest
-Other form controls worth mentioning are the text area, password input and hidden input:
+Other form controls worth mentioning are the text area, password input, hidden input, search input, tel input, url input and email input:
<erb>
<%= text_area_tag(:message, "Hi, nice site", :size => "24x6") %>
<%= password_field_tag(:password) %>
<%= hidden_field_tag(:parent_id, "5") %>
+<%= search_field(:user, :name) %>
+<%= telephone_field(:user, :phone) %>
+<%= url_field(:user, :homepage) %>
+<%= email_field(:user, :address) %>
</erb>
-output:
+Output:
<html>
<textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea>
<input id="password" name="password" type="password" />
<input id="parent_id" name="parent_id" type="hidden" value="5" />
+<input id="user_name" name="user[name]" size="30" type="search" />
+<input id="user_phone" name="user[phone]" size="30" type="tel" />
+<input id="user_homepage" size="30" name="user[homepage]" type="url" />
+<input id="user_address" size="30" name="user[address]" type="email" />
</html>
-Hidden inputs are not shown to the user, but they hold data like any textual input. Values inside them can be changed with JavaScript.
+Hidden inputs are not shown to the user, but they hold data like any textual input. Values inside them can be changed with JavaScript. The search, tel, url and email inputs are specified in HTML5 and may receive special handling and/or formatting in some user-agents.
TIP: If you're using password input fields (for any purpose), you might want to configure your application to prevent those parameters from being logged.
-
h3. Dealing with Model Objects
h4. Model Object Helpers
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index dbbf8f3b51..c88e3cc338 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -17,7 +17,7 @@ endprologue.
h3. Anatomy of a Migration
-Before I dive into the details of a migration, here are a few examples of the sorts of things you can do:
+Before we dive into the details of a migration, here are a few examples of the sorts of things you can do:
<ruby>
class CreateProducts < ActiveRecord::Migration
@@ -117,6 +117,33 @@ Occasionally you will make a mistake when writing a migration. If you have alrea
In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless.
+h4. Supported Types
+
+Active Record supports the following types:
+
+* +:primary_key+
+* +:string+
+* +:text+
+* +:integer+
+* +:float+
+* +:decimal+
+* +:datetime+
+* +:timestamp+
+* +:time+
+* +:date+
+* +:binary+
+* +:boolean+
+
+These will be mapped onto an appropriate underlying database type, for example with MySQL +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non-sexy syntax, for example
+
+<ruby>
+create_table :products do |t|
+ t.column :name, 'polygon', :null => false
+end
+</ruby>
+
+This may however hinder portability to other databases.
+
h3. Creating a Migration
h4. Creating a Model
@@ -261,18 +288,6 @@ end
will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table (when using MySQL the default is +ENGINE=InnoDB+).
-The types supported by Active Record are +:primary_key+, +:string+, +:text+, +:integer+, +:float+, +:decimal+, +:datetime+, +:timestamp+, +:time+, +:date+, +:binary+, +:boolean+.
-
-These will be mapped onto an appropriate underlying database type, for example with MySQL +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non-sexy syntax, for example
-
-<ruby>
-create_table :products do |t|
- t.column :name, 'polygon', :null => false
-end
-</ruby>
-
-This may however hinder portability to other databases.
-
h4. Changing Tables
A close cousin of +create_table+ is +change_table+, used for changing existing tables. It is used in a similar fashion to +create_table+ but the object yielded to the block knows more tricks. For example
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index 79bbe495bd..188423861d 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -86,7 +86,7 @@ class CoreExtTest < Test::Unit::TestCase
end
</ruby>
-Run +rake+ to run the test. This test should fail because we haven't implemented the +to_squak+ method:
+Run +rake+ to run the test. This test should fail because we haven't implemented the +to_squawk+ method:
<shell>
1) Error:
@@ -218,8 +218,8 @@ test/dummy directory:
<shell>
$ cd test/dummy
-$ rails generate model Hickwall last_squak:string
-$ rails generate model Wickwall last_squak:string last_tweet:string
+$ rails generate model Hickwall last_squawk:string
+$ rails generate model Wickwall last_squawk:string last_tweet:string
</shell>
Now you can create the necessary database tables in your testing database by navigating to your dummy app
diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile
index 3db47a70e8..90fc763349 100644
--- a/railties/guides/source/rails_application_templates.textile
+++ b/railties/guides/source/rails_application_templates.textile
@@ -11,22 +11,18 @@ endprologue.
h3. Usage
-To apply a template, you need to provide the Rails generator with the location of the template you wish to apply, using -m option:
+To apply a template, you need to provide the Rails generator with the location of the template you wish to apply, using -m option. This can either be path to a file or a URL.
<shell>
$ rails new blog -m ~/template.rb
+$ rails new blog -m http://example.com/template.rb
</shell>
-It's also possible to apply a template using a URL:
-
-<shell>
-$ rails new blog -m https://gist.github.com/755496.txt
-</shell>
-
-Alternatively, you can use the rake task +rails:template+ to apply a template to an existing Rails application:
+You can use the rake task +rails:template+ to apply templates to an existing Rails application. The location of the template needs to be passed in to an environment variable named LOCATION. Again, this can either be path to a file or a URL.
<shell>
$ rake rails:template LOCATION=~/template.rb
+$ rake rails:template LOCATION=http://example.com/template.rb
</shell>
h3. Template API
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 1cbc5c8f6e..68fb22f5d8 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -68,7 +68,7 @@ Rails would dispatch that request to the +destroy+ method on the +photos+ contro
h4. CRUD, Verbs, and Actions
-In Rails, a resourceful route provides a mapping between HTTP verbs and URLs and controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as
+In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as
<ruby>
resources :photos
@@ -94,8 +94,8 @@ Creating a resourceful route will also expose a number of helpers to the control
* +photos_path+ returns +/photos+
* +new_photo_path+ returns +/photos/new+
-* +edit_photo_path(id)+ returns +/photos/:id/edit+ (for instance, +edit_photo_path(10)+ returns +/photos/10/edit+)
-* +photo_path(id)+ returns +/photos/:id+ (for instance, +photo_path(10)+ returns +/photos/10+)
+* +edit_photo_path(:id)+ returns +/photos/:id/edit+ (for instance, +edit_photo_path(10)+ returns +/photos/10/edit+)
+* +photo_path(:id)+ returns +/photos/:id+ (for instance, +photo_path(10)+ returns +/photos/10+)
Each of these helpers has a corresponding +_url+ helper (such as +photos_url+) which returns the same path prefixed with the current host, port and path prefix.
@@ -163,14 +163,14 @@ end
This will create a number of routes for each of the +posts+ and +comments+ controller. For +Admin::PostsController+, Rails will create:
-|_.HTTP Verb |_.Path |_.action |_.named helper |
-|GET |/admin/posts |index | admin_posts_path |
-|GET |/admin/posts/new |new | new_admin_posts_path |
-|POST |/admin/posts |create | admin_posts_path |
-|GET |/admin/posts/1 |show | admin_post_path(id) |
-|GET |/admin/posts/1/edit |edit | edit_admin_post_path(id) |
-|PUT |/admin/posts/1 |update | admin_post_path(id) |
-|DELETE |/admin/posts/1 |destroy | admin_post_path(id) |
+|_.HTTP Verb |_.Path |_.action |_.named helper |
+|GET |/admin/posts |index | admin_posts_path |
+|GET |/admin/posts/new |new | new_admin_post_path |
+|POST |/admin/posts |create | admin_posts_path |
+|GET |/admin/posts/:id |show | admin_post_path(:id) |
+|GET |/admin/posts/:id/edit |edit | edit_admin_post_path(:id) |
+|PUT |/admin/posts/:id |update | admin_post_path(:id) |
+|DELETE |/admin/posts/:id |destroy | admin_post_path(:id) |
If you want to route +/posts+ (without the prefix +/admin+) to +Admin::PostsController+, you could use
@@ -204,12 +204,12 @@ In each of these cases, the named routes remain the same as if you did not use +
|_.HTTP Verb |_.Path |_.action |_.named helper |
|GET |/admin/posts |index | posts_path |
-|GET |/admin/posts/new |new | posts_path |
+|GET |/admin/posts/new |new | new_post_path |
|POST |/admin/posts |create | posts_path |
-|GET |/admin/posts/1 |show | post_path(id) |
-|GET |/admin/posts/1/edit |edit | edit_post_path(id) |
-|PUT |/admin/posts/1 |update | post_path(id) |
-|DELETE |/admin/posts/1 |destroy | post_path(id) |
+|GET |/admin/posts/:id |show | post_path(:id) |
+|GET |/admin/posts/:id/edit|edit | edit_post_path(:id)|
+|PUT |/admin/posts/:id |update | post_path(:id) |
+|DELETE |/admin/posts/:id |destroy | post_path(:id) |
h4. Nested Resources
@@ -236,13 +236,13 @@ end
In addition to the routes for magazines, this declaration will also route ads to an +AdsController+. The ad URLs require a magazine:
|_.HTTP Verb |_.Path |_.action |_.used for |
-|GET |/magazines/1/ads |index |display a list of all ads for a specific magazine |
-|GET |/magazines/1/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine |
-|POST |/magazines/1/ads |create |create a new ad belonging to a specific magazine |
-|GET |/magazines/1/ads/1 |show |display a specific ad belonging to a specific magazine |
-|GET |/magazines/1/ads/1/edit |edit |return an HTML form for editing an ad belonging to a specific magazine |
-|PUT |/magazines/1/ads/1 |update |update a specific ad belonging to a specific magazine |
-|DELETE |/magazines/1/ads/1 |destroy |delete a specific ad belonging to a specific magazine |
+|GET |/magazines/:id/ads |index |display a list of all ads for a specific magazine |
+|GET |/magazines/:id/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine |
+|POST |/magazines/:id/ads |create |create a new ad belonging to a specific magazine |
+|GET |/magazines/:id/ads/:id |show |display a specific ad belonging to a specific magazine |
+|GET |/magazines/:id/ads/:id/edit |edit |return an HTML form for editing an ad belonging to a specific magazine |
+|PUT |/magazines/:id/ads/:id |update |update a specific ad belonging to a specific magazine |
+|DELETE |/magazines/:id/ads/:id |destroy |delete a specific ad belonging to a specific magazine |
This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+. These helpers take an instance of Magazine as the first parameter (+magazine_ads_url(@magazine)+).
@@ -560,13 +560,13 @@ would match +zoo/woo/foo/bar/baz+ with +params[:a]+ equals +"zoo/woo"+, and +par
NOTE: Starting from Rails 3.1, wildcard routes will always match the optional format segment by default. For example if you have this route:
<ruby>
-map '*pages' => 'pages#show'
+match '*pages' => 'pages#show'
</ruby>
NOTE: By requesting +"/foo/bar.json"+, your +params[:pages]+ will be equals to +"foo/bar"+ with the request format of JSON. If you want the old 3.0.x behavior back, you could supply +:format => false+ like this:
<ruby>
-map '*pages' => 'pages#show', :format => false
+match '*pages' => 'pages#show', :format => false
</ruby>
h4. Redirection
@@ -628,16 +628,16 @@ resources :photos, :controller => "images"
will recognize incoming paths beginning with +/photos+ but route to the +Images+ controller:
-|_.HTTP Verb |_.Path |_.action |_.named helper |
-|GET |/photos |index | photos_path |
-|GET |/photos/new |new | new_photo_path |
-|POST |/photos |create | photos_path |
-|GET |/photos/1 |show | 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) |
+|_.HTTP Verb |_.Path |_.action |_.named helper |
+|GET |/photos |index | photos_path |
+|GET |/photos/new |new | new_photo_path |
+|POST |/photos |create | photos_path |
+|GET |/photos/:id |show | photo_path(:id) |
+|GET |/photos/:id/edit |edit | edit_photo_path(:id) |
+|PUT |/photos/:id |update | photo_path(:id) |
+|DELETE |/photos/:id |destroy | photo_path(:id) |
-NOTE: Use +photos_path+, +new_photos_path+, etc. to generate paths for this resource.
+NOTE: Use +photos_path+, +new_photo_path+, etc. to generate paths for this resource.
h4. Specifying Constraints
@@ -672,14 +672,14 @@ resources :photos, :as => "images"
will recognize incoming paths beginning with +/photos+ and route the requests to +PhotosController+, but use the value of the :as option to name the helpers.
-|_.HTTP verb|_.Path |_.action |_.named helper |
-|GET |/photos |index | images_path |
-|GET |/photos/new |new | new_image_path |
-|POST |/photos |create | images_path |
-|GET |/photos/1 |show | image_path(id) |
-|GET |/photos/1/edit |edit | edit_image_path(id) |
-|PUT |/photos/1 |update | image_path(id) |
-|DELETE |/photos/1 |destroy | image_path(id) |
+|_.HTTP verb|_.Path |_.action |_.named helper |
+|GET |/photos |index | images_path |
+|GET |/photos/new |new | new_image_path |
+|POST |/photos |create | images_path |
+|GET |/photos/:id |show | image_path(:id) |
+|GET |/photos/:id/edit |edit | edit_image_path(:id) |
+|PUT |/photos/:id |update | image_path(:id) |
+|DELETE |/photos/:id |destroy | image_path(:id) |
h4. Overriding the +new+ and +edit+ Segments
@@ -776,14 +776,14 @@ end
Rails now creates routes to the +CategoriesController+.
-|_.HTTP verb|_.Path |_.action |_.named helper |
-|GET |/kategorien |index | categories_path |
-|GET |/kategorien/neu |new | new_category_path |
-|POST |/kategorien |create | categories_path |
-|GET |/kategorien/1 |show | category_path(id) |
-|GET |/kategorien/1/bearbeiten |edit | edit_category_path(id) |
-|PUT |/kategorien/1 |update | category_path(id) |
-|DELETE |/kategorien/1 |destroy | category_path(id) |
+|_.HTTP verb|_.Path |_.action |_.named helper |
+|GET |/kategorien |index | categories_path |
+|GET |/kategorien/neu |new | new_category_path |
+|POST |/kategorien |create | categories_path |
+|GET |/kategorien/:id |show | category_path(:id) |
+|GET |/kategorien/:id/bearbeiten |edit | edit_category_path(:id) |
+|PUT |/kategorien/:id |update | category_path(:id) |
+|DELETE |/kategorien/:id |destroy | category_path(:id) |
h4. Overriding the Singular Form
@@ -823,10 +823,10 @@ If you want a complete list of all of the available routes in your application,
For example, here's a small section of the +rake routes+ output for a RESTful route:
<pre>
- users GET /users {:controller=>"users", :action=>"index"}
-formatted_users GET /users.:format {:controller=>"users", :action=>"index"}
- POST /users {:controller=>"users", :action=>"create"}
- POST /users.:format {:controller=>"users", :action=>"create"}
+ users GET /users(.:format) users#index
+ POST /users(.:format) users#create
+ new_user GET /users/new(.:format) users#new
+edit_user GET /users/:id/edit(.:format) users#edit
</pre>
You may restrict the listing to the routes that map to a particular controller setting the +CONTROLLER+ environment variable:
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 559739b2ec..9e2f1a4b7a 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -11,7 +11,7 @@ module Rails
# == Initialization
#
# Rails::Application is responsible for executing all railties, engines and plugin
- # initializers. Besides, it also executed some bootstrap initializers (check
+ # initializers. It also executes some bootstrap initializers (check
# Rails::Application::Bootstrap) and finishing initializers, after all the others
# are executed (check Rails::Application::Finisher).
#
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 5e55aeada9..a21484e5cb 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -93,7 +93,7 @@ In addition to those, there are:
plugin Install a plugin
runner Run a piece of code in the application environment (short-cut alias: "r")
-All commands can be run with -h for more information.
+All commands can be run with -h (or --help) for more information.
EOT
exit(1)
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index eb6fcd5dd7..cb897e94d7 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -5,13 +5,14 @@ require 'rbconfig'
require 'rails/engine/railties'
module Rails
- # Rails::Engine allows you to wrap a specific Rails application and share it across
- # different applications. Since Rails 3.0, every <tt>Rails::Application</tt> is nothing
- # more than an engine, allowing you to share it very easily.
+ # <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
+ # functionality and share it with other applications. Since Rails 3.0, every
+ # <tt>Rails::Application</tt> is just an engine, which allows for simple
+ # feature and application sharing.
#
- # Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same methods
- # (like <tt>rake_tasks</tt> and +generators+) and configuration available in the
- # latter can also be used in the former.
+ # Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
+ # methods (like <tt>rake_tasks</tt> and +generators+) and configuration
+ # options that are available in railties can also be used in engines.
#
# == Creating an Engine
#
@@ -71,12 +72,13 @@ module Rails
#
# == Paths
#
- # Since Rails 3.0, both your application and engines do not have hardcoded paths.
- # This means that you are not required to place your controllers at <tt>app/controllers</tt>,
- # but in any place which you find convenient.
+ # Since Rails 3.0, applications and engines have more flexible path configuration (as
+ # opposed to the previous hardcoded path configuration). This means that you are not
+ # required to place your controllers at <tt>app/controllers</tt>, but in any place
+ # which you find convenient.
#
# For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
- # All you would need to do is:
+ # You can set that as an option:
#
# class MyEngine < Rails::Engine
# paths["app/controllers"] = "lib/controllers"
@@ -105,9 +107,9 @@ module Rails
# paths["config/routes"] # => ["config/routes.rb"]
# end
#
- # Your <tt>Application</tt> class adds a couple more paths to this set. And as in your
- # <tt>Application</tt>,all folders under +app+ are automatically added to the load path.
- # So if you have <tt>app/observers</tt>, it's added by default.
+ # The <tt>Application</tt> class adds a couple more paths to this set. And as in your
+ # <tt>Application</tt>, all folders under +app+ are automatically added to the load path.
+ # If you have an <tt>app/observers</tt> folder for example, it will be added by default.
#
# == Endpoint
#
@@ -130,8 +132,8 @@ module Rails
#
# == Middleware stack
#
- # As an engine can now be rack endpoint, it can also have a middleware stack. The usage is exactly
- # the same as in <tt>Application</tt>:
+ # As an engine can now be a rack endpoint, it can also have a middleware
+ # stack. The usage is exactly the same as in <tt>Application</tt>:
#
# module MyEngine
# class Engine < Rails::Engine
@@ -141,8 +143,8 @@ module Rails
#
# == Routes
#
- # If you don't specify an endpoint, routes will be used as the default endpoint. You can use them
- # just like you use an application's routes:
+ # If you don't specify an endpoint, routes will be used as the default
+ # endpoint. You can use them just like you use an application's routes:
#
# # ENGINE/config/routes.rb
# MyEngine::Engine.routes.draw do
@@ -174,6 +176,7 @@ module Rails
# == Engine name
#
# There are some places where an Engine's name is used:
+ #
# * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
# it's used as default :as option
# * some of the rake tasks are based on engine name, e.g. <tt>my_engine:install:migrations</tt>,
@@ -191,8 +194,8 @@ module Rails
# == Isolated Engine
#
# Normally when you create controllers, helpers and models inside an engine, they are treated
- # as they were created inside the application. This means all application helpers and named routes
- # will be available to your engine's controllers.
+ # as if they were created inside the application itself. This means that all helpers and
+ # named routes from the application will be available to your engine's controllers as well.
#
# However, sometimes you want to isolate your engine from the application, especially if your engine
# has its own router. To do that, you simply need to call +isolate_namespace+. This method requires
@@ -240,9 +243,9 @@ module Rails
# text_field :title # => <input type="text" name="article[title]" id="article_title" />
# end
#
- # Additionally an isolated engine will set its name according to namespace, so
+ # Additionally, an isolated engine will set its name according to namespace, so
# MyEngine::Engine.engine_name will be "my_engine". It will also set MyEngine.table_name_prefix
- # to "my_engine_", changing MyEngine::Article model to use my_engine_article table.
+ # to "my_engine_", changing the MyEngine::Article model to use the my_engine_article table.
#
# == Using Engine's routes outside Engine
#
@@ -274,12 +277,13 @@ module Rails
# end
# end
#
- # Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tT> as default, so most of the time
+ # Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
# you can simply omit it.
#
- # Finally, if you want to generate a url to an engine's route using <tt>polymorphic_url</tt>, you also need
- # to pass the engine helper. Let's say that you want to create a form pointing to one of the
- # engine's routes. All you need to do is pass the helper as the first element in array with
+ # Finally, if you want to generate a url to an engine's route using
+ # <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
+ # say that you want to create a form pointing to one of the engine's routes.
+ # All you need to do is pass the helper as the first element in array with
# attributes for url:
#
# form_for([my_engine, @user])
@@ -319,7 +323,7 @@ module Rails
#
# Note that some of the migrations may be skipped if a migration with the same name already exists
# in application. In such a situation you must decide whether to leave that migration or rename the
- # migration in application and rerun copying migrations.
+ # migration in the application and rerun copying migrations.
#
# If your engine has migrations, you may also want to prepare data for the database in
# the <tt>seeds.rb</tt> file. You can load that data using the <tt>load_seed</tt> method, e.g.
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 433a56dc57..b8541c236e 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -92,14 +92,15 @@ module Rails
#
def environment(data=nil, options={}, &block)
sentinel = /class [a-z_:]+ < Rails::Application/i
+ env_file_sentinel = /::Application\.configure do/
data = block.call if !data && block_given?
in_root do
if options[:env].nil?
inject_into_file 'config/application.rb', "\n #{data}", :after => sentinel, :verbose => false
else
- Array.wrap(options[:env]).each do|env|
- append_file "config/environments/#{env}.rb", "\n#{data}", :verbose => false
+ Array.wrap(options[:env]).each do |env|
+ inject_into_file "config/environments/#{env}.rb", "\n #{data}", :after => env_file_sentinel, :verbose => false
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index 500693a568..7687b1beac 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -47,9 +47,9 @@ module <%= app_const_base %>
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
- <% unless options.skip_sprockets? %>
+<% unless options.skip_sprockets? -%>
# Enable the asset pipeline
config.assets.enabled = true
- <% end %>
+<% end -%>
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec b/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
index 56b06829d8..eb1a1e5054 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/%name%.gemspec
@@ -4,7 +4,7 @@ Gem::Specification.new do |s|
s.name = "<%= name %>"
s.summary = "Insert <%= camelized %> summary."
s.description = "Insert <%= camelized %> description."
- s.files = Dir["{app,config,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
+ s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
<% unless options.skip_test_unit? -%>
s.test_files = Dir["test/**/*"]
<% end -%>
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
index b28a842731..6ed6adcf1b 100755
--- a/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/Rakefile
@@ -15,7 +15,7 @@ end
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = '<%= camelized %>'
- rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
@@ -24,3 +24,8 @@ end
APP_RAKEFILE = File.expand_path("../<%= dummy_path -%>/Rakefile", __FILE__)
load 'rails/tasks/engine.rake'
<% end %>
+
+<% unless options[:skip_gemspec] -%>
+
+Bundler::GemHelper.install_tasks
+<% end %>
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 3f1d2ca87d..de3d0b6fc5 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -2,7 +2,7 @@ require 'set'
module Rails
module Paths
- # This object is an extended hash that behaves as root of the Rails::Paths system.
+ # This object is an extended hash that behaves as root of the <tt>Rails::Paths</tt> system.
# It allows you to collect information about how you want to structure your application
# paths by a Hash like API. It requires you to give a physical path on initialization.
#
@@ -10,13 +10,13 @@ module Rails
# root.add "app/controllers", :eager_load => true
#
# The command above creates a new root object and add "app/controllers" as a path.
- # This means we can get a Path object back like below:
+ # This means we can get a +Rails::Paths::Path+ object back like below:
#
# path = root["app/controllers"]
# path.eager_load? # => true
# path.is_a?(Rails::Paths::Path) # => true
#
- # The Path object is simply an array and allows you to easily add extra paths:
+ # The +Path+ object is simply an array and allows you to easily add extra paths:
#
# path.is_a?(Array) # => true
# path.inspect # => ["app/controllers"]
@@ -24,17 +24,17 @@ module Rails
# path << "lib/controllers"
# path.inspect # => ["app/controllers", "lib/controllers"]
#
- # Notice that when you add a path using #add, the path object created already
- # contains the path with the same path value given to #add. In some situations,
+ # Notice that when you add a path using +add+, the path object created already
+ # contains the path with the same path value given to +add+. In some situations,
# you may not want this behavior, so you can give :with as option.
#
# root.add "config/routes", :with => "config/routes.rb"
# root["config/routes"].inspect # => ["config/routes.rb"]
#
- # #add also accepts the following options as argument: eager_load, autoload,
- # autoload_once and glob.
+ # The +add+ method accepts the following options as arguments:
+ # eager_load, autoload, autoload_once and glob.
#
- # Finally, the Path object also provides a few helpers:
+ # Finally, the +Path+ object also provides a few helpers:
#
# root = Root.new "/rails"
# root.add "app/controllers"
@@ -42,7 +42,7 @@ module Rails
# root["app/controllers"].expanded # => ["/rails/app/controllers"]
# root["app/controllers"].existent # => ["/rails/app/controllers"]
#
- # Check the Path documentation for more information.
+ # Check the <tt>Rails::Paths::Path</tt> documentation for more information.
class Root < ::Hash
attr_accessor :path
diff --git a/railties/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index 79255d1f56..5d613e0698 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -47,7 +47,7 @@ namespace :doc do
rdoc.rdoc_dir = 'doc/app'
rdoc.template = ENV['template'] if ENV['template']
rdoc.title = ENV['title'] || "Rails Application Documentation"
- rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.options << '--line-numbers'
rdoc.options << '--charset' << 'utf-8'
rdoc.rdoc_files.include('doc/README_FOR_APP')
rdoc.rdoc_files.include('app/**/*.rb')
@@ -60,7 +60,7 @@ namespace :doc do
rdoc.rdoc_dir = 'doc/api'
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
rdoc.title = "Rails Framework Documentation"
- rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README')
gem_path('actionmailer') do |actionmailer|
@@ -133,7 +133,7 @@ namespace :doc do
files = Rake::FileList.new
options << "-o doc/plugins/#{plugin}"
options << "--title '#{plugin.titlecase} Plugin Documentation'"
- options << '--line-numbers' << '--inline-source'
+ options << '--line-numbers'
options << '--charset' << 'utf-8'
options << '-T html'
diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake
index 77a5c4dc6c..206ce39773 100644
--- a/railties/lib/rails/tasks/framework.rake
+++ b/railties/lib/rails/tasks/framework.rake
@@ -2,14 +2,14 @@ namespace :rails do
desc "Update configs and some other initially generated files (or use just update:configs, update:scripts, or update:application_controller)"
task :update => [ "update:configs", "update:scripts", "update:application_controller" ]
- desc "Applies the template supplied by LOCATION=/path/to/template"
+ desc "Applies the template supplied by LOCATION=(/path/to/template) or URL"
task :template do
template = ENV["LOCATION"]
+ raise "No LOCATION value given. Please set LOCATION either as path to a file or a URL" if template.blank?
template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://}
-
require 'rails/generators'
require 'rails/generators/rails/app/app_generator'
- generator = Rails::Generators::AppGenerator.new [ Rails.root ], {}, :destination_root => Rails.root
+ generator = Rails::Generators::AppGenerator.new [Rails.root], {}, :destination_root => Rails.root
generator.apply template, :verbose => false
end
diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake
index a0c953967c..0c26bcf790 100644
--- a/railties/lib/rails/tasks/routes.rake
+++ b/railties/lib/rails/tasks/routes.rake
@@ -10,8 +10,16 @@ task :routes => :environment do
routes = all_routes.collect do |route|
reqs = route.requirements.dup
- reqs[:to] = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
- reqs = reqs.empty? ? "" : reqs.inspect
+ rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
+
+ endpoint = rack_app ? rack_app.inspect : "#{reqs[:controller]}##{reqs[:action]}"
+ constraints = reqs.except(:controller, :action)
+
+ reqs = endpoint == '#' ? '' : endpoint
+
+ unless constraints.empty?
+ reqs = reqs.empty? ? constraints.inspect : "#{reqs} #{constraints.inspect}"
+ end
{:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index b9877a83b5..33da2c0f5a 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -108,9 +108,9 @@ namespace :test do
Rake::TestTask.new(:uncommitted => "test:prepare") do |t|
def t.file_list
if File.directory?(".svn")
- changed_since_checkin = silence_stderr { `svn status` }.map { |path| path.chomp[7 .. -1] }
+ changed_since_checkin = silence_stderr { `svn status` }.split.map { |path| path.chomp[7 .. -1] }
elsif File.directory?(".git")
- changed_since_checkin = silence_stderr { `git ls-files --modified --others` }.map { |path| path.chomp }
+ changed_since_checkin = silence_stderr { `git ls-files --modified --others` }.split.map { |path| path.chomp }
else
abort "Not a Subversion or Git checkout."
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 7671c129e9..cc65a674c9 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -91,7 +91,7 @@ module ApplicationTests
get '/cart', :to => 'cart#show'
end
RUBY
- assert_match 'cart GET /cart(.:format)', Dir.chdir(app_path){ `rake routes` }
+ assert_equal "cart GET /cart(.:format) cart#show\n", Dir.chdir(app_path){ `rake routes` }
end
def test_rake_routes_shows_custom_assets
@@ -100,7 +100,91 @@ module ApplicationTests
get '/custom/assets', :to => 'custom_assets#show'
end
RUBY
- assert_match 'custom_assets GET /custom/assets(.:format)', Dir.chdir(app_path){ `rake routes` }
+ assert_equal "custom_assets GET /custom/assets(.:format) custom_assets#show\n",
+ Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_resources_route
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ resources :articles
+ end
+ RUBY
+ expected =
+ " articles GET /articles(.:format) articles#index\n" <<
+ " POST /articles(.:format) articles#create\n" <<
+ " new_article GET /articles/new(.:format) articles#new\n" <<
+ "edit_article GET /articles/:id/edit(.:format) articles#edit\n" <<
+ " article GET /articles/:id(.:format) articles#show\n" <<
+ " PUT /articles/:id(.:format) articles#update\n" <<
+ " DELETE /articles/:id(.:format) articles#destroy\n"
+ assert_equal expected, Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_root_route
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ root :to => 'pages#main'
+ end
+ RUBY
+ assert_equal "root / pages#main\n", Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_controller_and_action_only_route
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match ':controller/:action'
+ end
+ RUBY
+ assert_equal " /:controller/:action(.:format) \n", Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_controller_and_action_route_with_constraints
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match ':controller(/:action(/:id))', :id => /\\d+/
+ end
+ RUBY
+ assert_equal " /:controller(/:action(/:id))(.:format) {:id=>/\\d+/}\n", Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_route_with_defaults
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match 'photos/:id' => 'photos#show', :defaults => {:format => 'jpg'}
+ end
+ RUBY
+ assert_equal %Q[ /photos/:id(.:format) photos#show {:format=>"jpg"}\n], Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_route_with_constraints
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match 'photos/:id' => 'photos#show', :id => /[A-Z]\\d{5}/
+ end
+ RUBY
+ assert_equal " /photos/:id(.:format) photos#show {:id=>/[A-Z]\\d{5}/}\n", Dir.chdir(app_path){ `rake routes` }
+ end
+
+ def test_rake_routes_shows_route_with_rack_app
+ app_file "lib/rack_app.rb", <<-RUBY
+ class RackApp
+ class << self
+ def call(env)
+ end
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ require 'rack_app'
+
+ AppTemplate::Application.routes.draw do
+ match 'foo/:id' => RackApp, :id => /[A-Z]\\d{5}/
+ end
+ RUBY
+
+ assert_equal " /foo/:id(.:format) RackApp {:id=>/[A-Z]\\d{5}/}\n", Dir.chdir(app_path){ `rake routes` }
end
def test_logger_is_flushed_when_exiting_production_rake_tasks
diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb
index 597746c4aa..e4a8000425 100644
--- a/railties/test/generators/actions_test.rb
+++ b/railties/test/generators/actions_test.rb
@@ -109,6 +109,13 @@ class ActionsTest < Rails::Generators::TestCase
assert_file 'config/application.rb', /#{Regexp.escape(autoload_paths)}/
end
+ def test_environment_should_include_data_in_environment_initializer_block_with_env_option
+ run_generator
+ autoload_paths = 'config.autoload_paths += %w["#{Rails.root}/app/extras"]'
+ action :environment, autoload_paths, :env => 'development'
+ assert_file "config/environments/development.rb", /Application\.configure do\n #{Regexp.escape(autoload_paths)}/
+ end
+
def test_environment_with_block_should_include_block_contents_in_environment_initializer_block
run_generator
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 4bd77ff7e3..0ccb2ae9da 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -206,7 +206,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
def test_creating_gemspec
run_generator
assert_file "bukkits.gemspec", /s.name = "bukkits"/
- assert_file "bukkits.gemspec", /s.files = Dir\["\{app,config,lib\}\/\*\*\/\*"\]/
+ assert_file "bukkits.gemspec", /s.files = Dir\["\{app,config,db,lib\}\/\*\*\/\*"\]/
assert_file "bukkits.gemspec", /s.test_files = Dir\["test\/\*\*\/\*"\]/
assert_file "bukkits.gemspec", /s.version = "0.0.1"/
end