aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile3
-rw-r--r--actionmailer/CHANGELOG.md4
-rw-r--r--actionmailer/Rakefile4
-rw-r--r--actionmailer/lib/action_mailer/base.rb84
-rw-r--r--actionmailer/lib/action_mailer/collector.rb2
-rw-r--r--actionmailer/lib/action_mailer/delivery_methods.rb22
-rw-r--r--actionmailer/lib/action_mailer/log_subscriber.rb6
-rw-r--r--actionmailer/lib/rails/generators/mailer/USAGE2
-rw-r--r--actionmailer/lib/rails/generators/mailer/mailer_generator.rb2
-rw-r--r--actionmailer/test/asset_host_test.rb6
-rw-r--r--actionmailer/test/base_test.rb64
-rw-r--r--actionmailer/test/delivery_methods_test.rb52
-rw-r--r--actionmailer/test/i18n_with_controller_test.rb8
-rw-r--r--actionmailer/test/mail_helper_test.rb16
-rw-r--r--actionmailer/test/mail_layout_test.rb20
-rw-r--r--actionmailer/test/mailers/base_mailer.rb40
-rw-r--r--actionmailer/test/mailers/proc_mailer.rb4
-rw-r--r--actionmailer/test/test_helper_test.rb6
-rw-r--r--actionmailer/test/url_test.rb8
-rw-r--r--actionpack/CHANGELOG.md80
-rw-r--r--actionpack/actionpack.gemspec1
-rw-r--r--actionpack/lib/action_controller/caching.rb24
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb189
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb202
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb17
-rw-r--r--actionpack/lib/action_controller/metal/strong_parameters.rb6
-rw-r--r--actionpack/lib/action_controller/railtie.rb1
-rw-r--r--actionpack/lib/action_controller/test_case.rb65
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb6
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb2
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb145
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb139
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb98
-rw-r--r--actionpack/lib/action_view/helpers/cache_helper.rb50
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb101
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb105
-rw-r--r--actionpack/lib/action_view/log_subscriber.rb7
-rw-r--r--actionpack/lib/action_view/railtie.rb14
-rw-r--r--actionpack/lib/action_view/test_case.rb6
-rw-r--r--actionpack/test/active_record_unit.rb2
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb22
-rw-r--r--actionpack/test/controller/caching_test.rb750
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb18
-rw-r--r--actionpack/test/controller/parameters/multi_parameter_attributes_test.rb33
-rw-r--r--actionpack/test/controller/parameters/parameters_permit_test.rb7
-rw-r--r--actionpack/test/controller/render_test.rb11
-rw-r--r--actionpack/test/controller/show_exceptions_test.rb16
-rw-r--r--actionpack/test/dispatch/routing_test.rb20
-rw-r--r--actionpack/test/dispatch/session/cache_store_test.rb1
-rw-r--r--actionpack/test/fixtures/test/hello/hello.erb1
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb752
-rw-r--r--actionpack/test/template/html-scanner/sanitizer_test.rb2
-rw-r--r--actionpack/test/template/url_helper_test.rb338
-rw-r--r--activemodel/lib/active_model/validations.rb4
-rw-r--r--activerecord/CHANGELOG.md41
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb2
-rw-r--r--activerecord/lib/active_record/counter_cache.rb4
-rw-r--r--activerecord/lib/active_record/fixture_set/file.rb (renamed from activerecord/lib/active_record/fixtures/file.rb)2
-rw-r--r--activerecord/lib/active_record/fixtures.rb44
-rw-r--r--activerecord/lib/active_record/log_subscriber.rb10
-rw-r--r--activerecord/lib/active_record/railtie.rb3
-rw-r--r--activerecord/lib/active_record/railties/databases.rake6
-rw-r--r--activerecord/lib/active_record/sanitization.rb17
-rw-r--r--activerecord/lib/active_record/validations.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql/reserved_word_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/reserved_word_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb4
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb8
-rw-r--r--activerecord/test/cases/base_test.rb337
-rw-r--r--activerecord/test/cases/counter_cache_test.rb15
-rw-r--r--activerecord/test/cases/dirty_test.rb2
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb (renamed from activerecord/test/cases/fixtures/file_test.rb)2
-rw-r--r--activerecord/test/cases/fixtures_test.rb46
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb350
-rw-r--r--activerecord/test/cases/store_test.rb2
-rw-r--r--activerecord/test/fixtures/mateys.yml4
-rw-r--r--activerecord/test/fixtures/parrots_pirates.yml8
-rw-r--r--activerecord/test/fixtures/peoples_treasures.yml4
-rw-r--r--activerecord/test/models/admin/user.rb2
-rw-r--r--activerecord/test/models/subscription.rb2
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activesupport/CHANGELOG.md4
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/backtrace_cleaner.rb5
-rw-r--r--activesupport/lib/active_support/cache.rb148
-rw-r--r--activesupport/lib/active_support/cache/memory_store.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/date/calculations.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/conversions.rb4
-rw-r--r--activesupport/lib/active_support/deprecation.rb21
-rw-r--r--activesupport/lib/active_support/key_generator.rb23
-rw-r--r--activesupport/test/caching_test.rb93
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb1
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb1
-rw-r--r--activesupport/test/key_generator_test.rb32
-rw-r--r--activesupport/test/ordered_hash_test.rb1
-rw-r--r--activesupport/test/test_test.rb4
-rw-r--r--guides/CHANGELOG.md5
-rw-r--r--guides/assets/javascripts/guides.js50
-rw-r--r--guides/assets/javascripts/jquery.min.js4
-rwxr-xr-xguides/assets/javascripts/responsive-tables.js43
-rw-r--r--guides/assets/stylesheets/main.css667
-rwxr-xr-xguides/assets/stylesheets/responsive-tables.css50
-rw-r--r--guides/code/getting_started/app/assets/javascripts/application.js2
-rw-r--r--guides/rails_guides/helpers.rb10
-rw-r--r--guides/source/4_0_release_notes.md2
-rw-r--r--guides/source/action_controller_overview.md4
-rw-r--r--guides/source/action_mailer_basics.md2
-rw-r--r--guides/source/active_support_core_extensions.md19
-rw-r--r--guides/source/command_line.md42
-rw-r--r--guides/source/engines.md18
-rw-r--r--guides/source/generators.md12
-rw-r--r--guides/source/getting_started.md24
-rw-r--r--guides/source/initialization.md2
-rw-r--r--guides/source/layout.html.erb52
-rw-r--r--guides/source/migrations.md2
-rw-r--r--guides/source/testing.md30
-rw-r--r--guides/source/upgrading_ruby_on_rails.md7
-rw-r--r--railties/CHANGELOG.md14
-rw-r--r--railties/lib/rails.rb2
-rw-r--r--railties/lib/rails/application.rb26
-rw-r--r--railties/lib/rails/application/configuration.rb2
-rw-r--r--railties/lib/rails/generators/app_base.rb13
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb6
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb19
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt5
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb7
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/routes.rb26
-rw-r--r--railties/lib/rails/generators/rails/controller/USAGE8
-rw-r--r--railties/lib/rails/generators/rails/helper/USAGE2
-rw-r--r--railties/lib/rails/generators/rails/model/USAGE4
-rw-r--r--railties/lib/rails/generators/rails/observer/USAGE2
-rw-r--r--railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb45
-rw-r--r--railties/lib/rails/generators/rails/scaffold_controller/USAGE8
-rw-r--r--railties/lib/rails/generators/test_unit/controller/controller_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/helper/helper_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/model/model_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/observer/observer_generator.rb2
-rw-r--r--railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb2
-rw-r--r--railties/lib/rails/paths.rb1
-rw-r--r--railties/lib/rails/rack/logger.rb9
-rw-r--r--railties/lib/rails/tasks/statistics.rake8
-rw-r--r--railties/lib/rails/tasks/tmp.rake4
-rw-r--r--railties/lib/rails/test_help.rb8
-rw-r--r--railties/lib/rails/test_unit/testing.rake36
-rw-r--r--railties/test/application/asset_debugging_test.rb2
-rw-r--r--railties/test/application/assets_test.rb18
-rw-r--r--railties/test/application/configuration_test.rb53
-rw-r--r--railties/test/application/initializers/frameworks_test.rb2
-rw-r--r--railties/test/application/middleware/cache_test.rb2
-rw-r--r--railties/test/application/middleware_test.rb11
-rw-r--r--railties/test/application/rake_test.rb12
-rw-r--r--railties/test/commands/console_test.rb14
-rw-r--r--railties/test/generators/app_generator_test.rb11
-rw-r--r--railties/test/generators/controller_generator_test.rb8
-rw-r--r--railties/test/generators/helper_generator_test.rb2
-rw-r--r--railties/test/generators/mailer_generator_test.rb2
-rw-r--r--railties/test/generators/model_generator_test.rb6
-rw-r--r--railties/test/generators/namespaced_generators_test.rb112
-rw-r--r--railties/test/generators/observer_generator_test.rb2
-rw-r--r--railties/test/generators/resource_generator_test.rb10
-rw-r--r--railties/test/generators/scaffold_controller_generator_test.rb8
-rw-r--r--railties/test/generators/scaffold_generator_test.rb28
-rwxr-xr-xtools/profile2
178 files changed, 2682 insertions, 3855 deletions
diff --git a/Gemfile b/Gemfile
index d149ec6129..3b77524077 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,8 +6,11 @@ gem 'arel', github: 'rails/arel', branch: 'master'
gem 'mocha', '>= 0.11.2', :require => false
gem 'rack-test', github: 'brynary/rack-test'
+gem 'rack-cache', "~> 1.2"
gem 'bcrypt-ruby', '~> 3.0.0'
gem 'jquery-rails'
+gem 'turbolinks'
+gem 'coffee-rails', github: 'rails/coffee-rails'
gem 'journey', github: 'rails/journey', branch: 'master'
diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md
index e29890f2d8..68b34805ff 100644
--- a/actionmailer/CHANGELOG.md
+++ b/actionmailer/CHANGELOG.md
@@ -27,4 +27,8 @@
* Asynchronously send messages via the Rails Queue *Brian Cardarella*
+* Allow callbacks to be defined in mailers similar to `ActionController::Base`. You can configure default
+ settings, headers, attachments, delivery settings or change delivery using
+ `before_filter`, `after_filter` etc. *Justin S. Leitgeb*
+
Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/actionmailer/CHANGELOG.md) for previous changes.
diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile
index c1c4171cdf..45c238d302 100644
--- a/actionmailer/Rakefile
+++ b/actionmailer/Rakefile
@@ -3,7 +3,7 @@ require 'rake/packagetask'
require 'rubygems/package_task'
desc "Default Task"
-task :default => [ :test ]
+task default: [ :test ]
# Run the unit tests
Rake::TestTask.new { |t|
@@ -29,7 +29,7 @@ Gem::PackageTask.new(spec) do |p|
end
desc "Release to gemcutter"
-task :release => :package do
+task release: :package do
require 'rake/gemcutter'
Rake::Gemcutter::Tasks.new(spec).define
Rake::Task['gem:push'].invoke
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index bd33f81f1e..fee4a64248 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -23,13 +23,13 @@ module ActionMailer
# Examples:
#
# class Notifier < ActionMailer::Base
- # default :from => 'no-reply@example.com',
- # :return_path => 'system@example.com'
+ # default from: 'no-reply@example.com',
+ # return_path: 'system@example.com'
#
# def welcome(recipient)
# @account = recipient
- # mail(:to => recipient.email_address_with_name,
- # :bcc => ["bcc@example.com", "Order Watcher <watcher@example.com>"])
+ # mail(to: recipient.email_address_with_name,
+ # bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
# end
# end
#
@@ -62,21 +62,21 @@ module ActionMailer
#
# If you want to explicitly render only certain templates, pass a block:
#
- # mail(:to => user.email) do |format|
+ # mail(to: user.email) do |format|
# format.text
# format.html
# end
#
# The block syntax is also useful in providing information specific to a part:
#
- # mail(:to => user.email) do |format|
+ # mail(to: user.email) do |format|
# format.text(:content_transfer_encoding => "base64")
# format.html
# end
#
# Or even to render a special view:
#
- # mail(:to => user.email) do |format|
+ # mail(to: user.email) do |format|
# format.text
# format.html { render "some_other_template" }
# end
@@ -100,12 +100,12 @@ module ActionMailer
# You can even use Action Pack helpers in these views. For example:
#
# You got a new note!
- # <%= truncate(@note.body, :length => 25) %>
+ # <%= truncate(@note.body, length: 25) %>
#
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
#
# You got a new note from <%= message.from %>!
- # <%= truncate(@note.body, :length => 25) %>
+ # <%= truncate(@note.body, length: 25) %>
#
#
# = Generating URLs
@@ -116,11 +116,11 @@ module ActionMailer
#
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
#
- # <%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
+ # <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
#
# When using named routes you only need to supply the <tt>:host</tt>:
#
- # <%= users_url(:host => "example.com") %>
+ # <%= users_url(host: "example.com") %>
#
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
@@ -176,7 +176,7 @@ module ActionMailer
# class ApplicationMailer < ActionMailer::Base
# def welcome(recipient)
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
- # mail(:to => recipient, :subject => "New account information")
+ # mail(to: recipient, subject: "New account information")
# end
# end
#
@@ -192,7 +192,7 @@ module ActionMailer
# class ApplicationMailer < ActionMailer::Base
# def welcome(recipient)
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
- # mail(:to => recipient, :subject => "New account information", :body => "")
+ # mail(to: recipient, subject: "New account information", body: "")
# end
# end
#
@@ -204,7 +204,7 @@ module ActionMailer
# class ApplicationMailer < ActionMailer::Base
# def welcome(recipient)
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
- # mail(:to => recipient, :subject => "Here is what we look like")
+ # mail(to: recipient, subject: "Here is what we look like")
# end
# end
#
@@ -220,7 +220,7 @@ module ActionMailer
#
# <h1>Please Don't Cringe</h1>
#
- # <%= image_tag attachments['photo.png'].url, :alt => 'Our Photo', :class => 'photo' -%>
+ # <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
#
# = Observing and Intercepting Mails
#
@@ -241,7 +241,7 @@ module ActionMailer
# default method inside the class definition:
#
# class Notifier < ActionMailer::Base
- # default :sender => 'system@example.com'
+ # default sender: 'system@example.com'
# end
#
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
@@ -260,7 +260,7 @@ module ActionMailer
#
# class Notifier < ActionMailer::Base
# default 'Content-Transfer-Encoding' => '7bit',
- # :content_description => 'This is a description'
+ # content_description: 'This is a description'
# end
#
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
@@ -386,10 +386,10 @@ module ActionMailer
class_attribute :default_params
self.default_params = {
- :mime_version => "1.0",
- :charset => "UTF-8",
- :content_type => "text/plain",
- :parts_order => [ "text/plain", "text/enriched", "text/html" ]
+ mime_version: "1.0",
+ charset: "UTF-8",
+ content_type: "text/plain",
+ parts_order: [ "text/plain", "text/enriched", "text/html" ]
}.freeze
class_attribute :queue
@@ -549,17 +549,17 @@ module ActionMailer
#
# You can also specify overrides if you want by passing a hash instead of a string:
#
- # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
- # :content => File.read('/path/to/filename.jpg')}
+ # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
+ # content: File.read('/path/to/filename.jpg')}
#
# If you want to use a different encoding than Base64, you can pass an encoding in,
# but then it is up to you to pass in the content pre-encoded, and don't expect
# Mail to know how to decode this data:
#
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
- # mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
- # :encoding => 'SpecialEncoding',
- # :content => file_content }
+ # mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
+ # encoding: 'SpecialEncoding',
+ # content: file_content }
#
# You can also search for specific attachments:
#
@@ -597,9 +597,9 @@ module ActionMailer
# class method:
#
# class Notifier < ActionMailer::Base
- # self.default :from => 'no-reply@test.lindsaar.net',
- # :bcc => 'email_logger@test.lindsaar.net',
- # :reply_to => 'bounces@test.lindsaar.net'
+ # self.default from: 'no-reply@test.lindsaar.net',
+ # bcc: 'email_logger@test.lindsaar.net',
+ # reply_to: 'bounces@test.lindsaar.net'
# end
#
# If you need other headers not listed above, you can either pass them in
@@ -621,10 +621,10 @@ module ActionMailer
# For example:
#
# class Notifier < ActionMailer::Base
- # default :from => 'no-reply@test.lindsaar.net',
+ # default from: 'no-reply@test.lindsaar.net',
#
# def welcome
- # mail(:to => 'mikel@test.lindsaar.net')
+ # mail(to: 'mikel@test.lindsaar.net')
# end
# end
#
@@ -633,22 +633,22 @@ module ActionMailer
#
# However, those can be customized:
#
- # mail(:template_path => 'notifications', :template_name => 'another')
+ # mail(template_path: 'notifications', template_name: 'another')
#
# And now it will look for all templates at "app/views/notifications" with name "another".
#
# If you do pass a block, you can render specific templates of your choice:
#
- # mail(:to => 'mikel@test.lindsaar.net') do |format|
+ # mail(to: 'mikel@test.lindsaar.net') do |format|
# format.text
# format.html
# end
#
# You can even render text directly without using a template:
#
- # mail(:to => 'mikel@test.lindsaar.net') do |format|
- # format.text { render :text => "Hello Mikel!" }
- # format.html { render :text => "<h1>Hello Mikel!</h1>" }
+ # mail(to: 'mikel@test.lindsaar.net') do |format|
+ # format.text { render text: "Hello Mikel!" }
+ # format.html { render text: "<h1>Hello Mikel!</h1>" }
# end
#
# Which will render a <tt>multipart/alternative</tt> email with <tt>text/plain</tt> and
@@ -657,7 +657,7 @@ module ActionMailer
# The block syntax also allows you to customize the part headers if desired:
#
# mail(:to => 'mikel@test.lindsaar.net') do |format|
- # format.text(:content_transfer_encoding => "base64")
+ # format.text(content_transfer_encoding: "base64")
# format.html
# end
#
@@ -730,7 +730,7 @@ module ActionMailer
# humanized version of the <tt>action_name</tt>.
def default_i18n_subject #:nodoc:
mailer_scope = self.class.mailer_name.tr('/', '.')
- I18n.t(:subject, :scope => [mailer_scope, action_name], :default => action_name.humanize)
+ I18n.t(:subject, scope: [mailer_scope, action_name], default: action_name.humanize)
end
def collect_responses_and_parts_order(headers) #:nodoc:
@@ -743,8 +743,8 @@ module ActionMailer
responses = collector.responses
elsif headers[:body]
responses << {
- :body => headers.delete(:body),
- :content_type => self.class.default[:content_type] || "text/plain"
+ body: headers.delete(:body),
+ content_type: self.class.default[:content_type] || "text/plain"
}
else
templates_path = headers.delete(:template_path) || self.class.mailer_name
@@ -754,8 +754,8 @@ module ActionMailer
self.formats = template.formats
responses << {
- :body => render(:template => template),
- :content_type => template.type.to_s
+ body: render(template: template),
+ content_type: template.type.to_s
}
end
end
diff --git a/actionmailer/lib/action_mailer/collector.rb b/actionmailer/lib/action_mailer/collector.rb
index 569a09bd2a..cbb2778fae 100644
--- a/actionmailer/lib/action_mailer/collector.rb
+++ b/actionmailer/lib/action_mailer/collector.rb
@@ -21,7 +21,7 @@ module ActionMailer
alias :all :any
def custom(mime, options={})
- options.reverse_merge!(:content_type => mime.to_s)
+ options.reverse_merge!(content_type: mime.to_s)
@context.formats = [mime.to_sym]
options[:body] = block_given? ? yield : @default_render.call
@responses << options
diff --git a/actionmailer/lib/action_mailer/delivery_methods.rb b/actionmailer/lib/action_mailer/delivery_methods.rb
index 110f6769e6..caea3d7535 100644
--- a/actionmailer/lib/action_mailer/delivery_methods.rb
+++ b/actionmailer/lib/action_mailer/delivery_methods.rb
@@ -20,27 +20,27 @@ module ActionMailer
self.delivery_method = :smtp
add_delivery_method :smtp, Mail::SMTP,
- :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil,
- :enable_starttls_auto => true
+ address: "localhost",
+ port: 25,
+ domain: 'localhost.localdomain',
+ user_name: nil,
+ password: nil,
+ authentication: nil,
+ enable_starttls_auto: true
add_delivery_method :file, Mail::FileDelivery,
- :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
+ location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
add_delivery_method :sendmail, Mail::Sendmail,
- :location => '/usr/sbin/sendmail',
- :arguments => '-i -t'
+ location: '/usr/sbin/sendmail',
+ arguments: '-i -t'
add_delivery_method :test, Mail::TestMailer
end
module ClassMethods
# Provides a list of emails that have been delivered by Mail::TestMailer
- delegate :deliveries, :deliveries=, :to => Mail::TestMailer
+ delegate :deliveries, :deliveries=, to: Mail::TestMailer
# Adds a new delivery method through the given class using the given
# symbol as alias and the default options supplied.
diff --git a/actionmailer/lib/action_mailer/log_subscriber.rb b/actionmailer/lib/action_mailer/log_subscriber.rb
index a6c163832e..3fe64759ac 100644
--- a/actionmailer/lib/action_mailer/log_subscriber.rb
+++ b/actionmailer/lib/action_mailer/log_subscriber.rb
@@ -1,13 +1,15 @@
module ActionMailer
class LogSubscriber < ActiveSupport::LogSubscriber
def deliver(event)
+ return unless logger.info?
recipients = Array(event.payload[:to]).join(', ')
- info("\nSent mail to #{recipients} (%1.fms)" % event.duration)
+ info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
debug(event.payload[:mail])
end
def receive(event)
- info("\nReceived mail (%.1fms)" % event.duration)
+ return unless logger.info?
+ info("\nReceived mail (#{event.duration.round(1)}ms)")
debug(event.payload[:mail])
end
diff --git a/actionmailer/lib/rails/generators/mailer/USAGE b/actionmailer/lib/rails/generators/mailer/USAGE
index 9f1d6b182e..7470289fd3 100644
--- a/actionmailer/lib/rails/generators/mailer/USAGE
+++ b/actionmailer/lib/rails/generators/mailer/USAGE
@@ -13,6 +13,6 @@ Example:
creates a Notifications mailer class, views, test, and fixtures:
Mailer: app/mailers/notifications.rb
Views: app/views/notifications/signup.erb [...]
- Test: test/functional/notifications_test.rb
+ Test: test/mailers/notifications_test.rb
Fixtures: test/fixtures/notifications/signup [...]
diff --git a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
index dd7fa640c9..d5bf864595 100644
--- a/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
+++ b/actionmailer/lib/rails/generators/mailer/mailer_generator.rb
@@ -3,7 +3,7 @@ module Rails
class MailerGenerator < NamedBase
source_root File.expand_path("../templates", __FILE__)
- argument :actions, :type => :array, :default => [], :banner => "method method"
+ argument :actions, type: :array, default: [], banner: "method method"
check_class_collision
def create_mailer_file
diff --git a/actionmailer/test/asset_host_test.rb b/actionmailer/test/asset_host_test.rb
index 696a9f1174..32ae76a7c5 100644
--- a/actionmailer/test/asset_host_test.rb
+++ b/actionmailer/test/asset_host_test.rb
@@ -3,9 +3,9 @@ require 'action_controller'
class AssetHostMailer < ActionMailer::Base
def email_with_asset
- mail :to => 'test@localhost',
- :subject => 'testing email containing asset path while asset_host is set',
- :from => 'tester@example.com'
+ mail to: 'test@localhost',
+ subject: 'testing email containing asset path while asset_host is set',
+ from: 'tester@example.com'
end
end
diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb
index 17ce8b7072..1cb3ce63fe 100644
--- a/actionmailer/test/base_test.rb
+++ b/actionmailer/test/base_test.rb
@@ -30,21 +30,21 @@ class BaseTest < ActiveSupport::TestCase
end
test "mail() with from overwrites the class level default" do
- email = BaseMailer.welcome(:from => 'someone@example.com',
- :to => 'another@example.org')
+ email = BaseMailer.welcome(from: 'someone@example.com',
+ to: 'another@example.org')
assert_equal(['someone@example.com'], email.from)
assert_equal(['another@example.org'], email.to)
end
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
time = Time.now.beginning_of_day.to_datetime
- email = BaseMailer.welcome(:bcc => 'bcc@test.lindsaar.net',
- :cc => 'cc@test.lindsaar.net',
- :content_type => 'multipart/mixed',
- :charset => 'iso-8559-1',
- :mime_version => '2.0',
- :reply_to => 'reply-to@test.lindsaar.net',
- :date => time)
+ email = BaseMailer.welcome(bcc: 'bcc@test.lindsaar.net',
+ cc: 'cc@test.lindsaar.net',
+ content_type: 'multipart/mixed',
+ charset: 'iso-8559-1',
+ mime_version: '2.0',
+ reply_to: 'reply-to@test.lindsaar.net',
+ date: time)
assert_equal(['bcc@test.lindsaar.net'], email.bcc)
assert_equal(['cc@test.lindsaar.net'], email.cc)
assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
@@ -60,7 +60,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "can pass in :body to the mail method hash" do
- email = BaseMailer.welcome(:body => "Hello there")
+ email = BaseMailer.welcome(body: "Hello there")
assert_equal("text/plain", email.mime_type)
assert_equal("Hello there", email.body.encoded)
end
@@ -142,7 +142,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "adds the given :body as part" do
- email = BaseMailer.attachment_with_content(:body => "I'm the eggman")
+ email = BaseMailer.attachment_with_content(body: "I'm the eggman")
assert_equal(2, email.parts.length)
assert_equal("multipart/mixed", email.mime_type)
assert_equal("text/plain", email.parts[0].mime_type)
@@ -165,31 +165,31 @@ class BaseTest < ActiveSupport::TestCase
# Defaults values
test "uses default charset from class" do
- with_default BaseMailer, :charset => "US-ASCII" do
+ with_default BaseMailer, charset: "US-ASCII" do
email = BaseMailer.welcome
assert_equal("US-ASCII", email.charset)
- email = BaseMailer.welcome(:charset => "iso-8559-1")
+ email = BaseMailer.welcome(charset: "iso-8559-1")
assert_equal("iso-8559-1", email.charset)
end
end
test "uses default content type from class" do
- with_default BaseMailer, :content_type => "text/html" do
+ with_default BaseMailer, content_type: "text/html" do
email = BaseMailer.welcome
assert_equal("text/html", email.mime_type)
- email = BaseMailer.welcome(:content_type => "text/plain")
+ email = BaseMailer.welcome(content_type: "text/plain")
assert_equal("text/plain", email.mime_type)
end
end
test "uses default mime version from class" do
- with_default BaseMailer, :mime_version => "2.0" do
+ with_default BaseMailer, mime_version: "2.0" do
email = BaseMailer.welcome
assert_equal("2.0", email.mime_version)
- email = BaseMailer.welcome(:mime_version => "1.0")
+ email = BaseMailer.welcome(mime_version: "1.0")
assert_equal("1.0", email.mime_version)
end
end
@@ -202,17 +202,17 @@ class BaseTest < ActiveSupport::TestCase
end
test "subject gets default from I18n" do
- BaseMailer.default :subject => nil
- email = BaseMailer.welcome(:subject => nil)
+ BaseMailer.default subject: nil
+ email = BaseMailer.welcome(subject: nil)
assert_equal "Welcome", email.subject
- I18n.backend.store_translations('en', :base_mailer => {:welcome => {:subject => "New Subject!"}})
- email = BaseMailer.welcome(:subject => nil)
+ I18n.backend.store_translations('en', base_mailer: {welcome: {subject: "New Subject!"}})
+ email = BaseMailer.welcome(subject: nil)
assert_equal "New Subject!", email.subject
end
test "translations are scoped properly" do
- I18n.backend.store_translations('en', :base_mailer => {:email_with_translations => {:greet_user => "Hello %{name}!"}})
+ I18n.backend.store_translations('en', base_mailer: {email_with_translations: {greet_user: "Hello %{name}!"}})
email = BaseMailer.email_with_translations
assert_equal 'Hello lifo!', email.body.encoded
end
@@ -230,19 +230,19 @@ class BaseTest < ActiveSupport::TestCase
test "implicit multipart with sort order" do
order = ["text/html", "text/plain"]
- with_default BaseMailer, :parts_order => order do
+ with_default BaseMailer, parts_order: order do
email = BaseMailer.implicit_multipart
assert_equal("text/html", email.parts[0].mime_type)
assert_equal("text/plain", email.parts[1].mime_type)
- email = BaseMailer.implicit_multipart(:parts_order => order.reverse)
+ email = BaseMailer.implicit_multipart(parts_order: order.reverse)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
end
end
test "implicit multipart with attachments creates nested parts" do
- email = BaseMailer.implicit_multipart(:attachments => true)
+ email = BaseMailer.implicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
assert_equal("multipart/alternative", email.parts[1].mime_type)
assert_equal("text/plain", email.parts[1].parts[0].mime_type)
@@ -253,8 +253,8 @@ class BaseTest < ActiveSupport::TestCase
test "implicit multipart with attachments and sort order" do
order = ["text/html", "text/plain"]
- with_default BaseMailer, :parts_order => order do
- email = BaseMailer.implicit_multipart(:attachments => true)
+ with_default BaseMailer, parts_order: order do
+ email = BaseMailer.implicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
assert_equal("multipart/alternative", email.parts[1].mime_type)
assert_equal("text/plain", email.parts[1].parts[1].mime_type)
@@ -273,7 +273,7 @@ class BaseTest < ActiveSupport::TestCase
end
test "implicit multipart with other locale" do
- swap I18n, :locale => :pl do
+ swap I18n, locale: :pl do
email = BaseMailer.implicit_with_locale
assert_equal(2, email.parts.size)
assert_equal("multipart/alternative", email.mime_type)
@@ -324,19 +324,19 @@ class BaseTest < ActiveSupport::TestCase
test "explicit multipart does not sort order" do
order = ["text/html", "text/plain"]
- with_default BaseMailer, :parts_order => order do
+ with_default BaseMailer, parts_order: order do
email = BaseMailer.explicit_multipart
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
- email = BaseMailer.explicit_multipart(:parts_order => order.reverse)
+ email = BaseMailer.explicit_multipart(parts_order: order.reverse)
assert_equal("text/plain", email.parts[0].mime_type)
assert_equal("text/html", email.parts[1].mime_type)
end
end
test "explicit multipart with attachments creates nested parts" do
- email = BaseMailer.explicit_multipart(:attachments => true)
+ email = BaseMailer.explicit_multipart(attachments: true)
assert_equal("application/pdf", email.parts[0].mime_type)
assert_equal("multipart/alternative", email.parts[1].mime_type)
assert_equal("text/plain", email.parts[1].parts[0].mime_type)
@@ -651,7 +651,7 @@ class BaseTest < ActiveSupport::TestCase
test "default_from can be set" do
class DefaultFromMailer < ActionMailer::Base
- default :to => 'system@test.lindsaar.net'
+ default to: 'system@test.lindsaar.net'
self.default_options = {from: "robert.pankowecki@gmail.com"}
def welcome
diff --git a/actionmailer/test/delivery_methods_test.rb b/actionmailer/test/delivery_methods_test.rb
index 7109f23e4c..61a037ea18 100644
--- a/actionmailer/test/delivery_methods_test.rb
+++ b/actionmailer/test/delivery_methods_test.rb
@@ -22,24 +22,24 @@ end
class DefaultsDeliveryMethodsTest < ActiveSupport::TestCase
test "default smtp settings" do
- settings = { :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil,
- :enable_starttls_auto => true }
+ settings = { address: "localhost",
+ port: 25,
+ domain: 'localhost.localdomain',
+ user_name: nil,
+ password: nil,
+ authentication: nil,
+ enable_starttls_auto: true }
assert_equal settings, ActionMailer::Base.smtp_settings
end
test "default file delivery settings" do
- settings = {:location => "#{Dir.tmpdir}/mails"}
+ settings = {location: "#{Dir.tmpdir}/mails"}
assert_equal settings, ActionMailer::Base.file_settings
end
test "default sendmail settings" do
- settings = {:location => '/usr/sbin/sendmail',
- :arguments => '-i -t'}
+ settings = {location: '/usr/sbin/sendmail',
+ arguments: '-i -t'}
assert_equal settings, ActionMailer::Base.sendmail_settings
end
end
@@ -63,8 +63,8 @@ class CustomDeliveryMethodsTest < ActiveSupport::TestCase
end
test "allow to customize custom settings" do
- ActionMailer::Base.custom_settings = { :foo => :bar }
- assert_equal Hash[:foo => :bar], ActionMailer::Base.custom_settings
+ ActionMailer::Base.custom_settings = { foo: :bar }
+ assert_equal Hash[foo: :bar], ActionMailer::Base.custom_settings
end
test "respond to custom settings" do
@@ -82,8 +82,8 @@ end
class MailDeliveryTest < ActiveSupport::TestCase
class DeliveryMailer < ActionMailer::Base
DEFAULT_HEADERS = {
- :to => 'mikel@test.lindsaar.net',
- :from => 'jose@test.plataformatec.com'
+ to: 'mikel@test.lindsaar.net',
+ from: 'jose@test.plataformatec.com'
}
def welcome(hash={})
@@ -110,7 +110,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
test "delivery method can be customized per instance" do
email = DeliveryMailer.welcome.deliver
assert_instance_of Mail::SMTP, email.delivery_method
- email = DeliveryMailer.welcome(:delivery_method => :test).deliver
+ email = DeliveryMailer.welcome(delivery_method: :test).deliver
assert_instance_of Mail::TestMailer, email.delivery_method
end
@@ -125,7 +125,7 @@ class MailDeliveryTest < ActiveSupport::TestCase
test "delivery method options default to class level options" do
default_options = {a: "b"}
ActionMailer::Base.add_delivery_method :optioned, MyOptionedDelivery, default_options
- mail_instance = DeliveryMailer.welcome(:delivery_method => :optioned)
+ mail_instance = DeliveryMailer.welcome(delivery_method: :optioned)
assert_equal default_options, mail_instance.delivery_method.options
end
@@ -133,21 +133,21 @@ class MailDeliveryTest < ActiveSupport::TestCase
default_options = {a: "b"}
ActionMailer::Base.add_delivery_method :optioned, MyOptionedDelivery, default_options
overridden_options = {a: "a"}
- mail_instance = DeliveryMailer.welcome(:delivery_method => :optioned, :delivery_method_options => overridden_options)
+ mail_instance = DeliveryMailer.welcome(delivery_method: :optioned, delivery_method_options: overridden_options)
assert_equal overridden_options, mail_instance.delivery_method.options
end
test "default delivery options can be overridden per mail instance" do
- settings = { :address => "localhost",
- :port => 25,
- :domain => 'localhost.localdomain',
- :user_name => nil,
- :password => nil,
- :authentication => nil,
- :enable_starttls_auto => true }
+ settings = { address: "localhost",
+ port: 25,
+ domain: 'localhost.localdomain',
+ user_name: nil,
+ password: nil,
+ authentication: nil,
+ enable_starttls_auto: true }
assert_equal settings, ActionMailer::Base.smtp_settings
- overridden_options = {user_name: "overridden", :password => "somethingobtuse"}
- mail_instance = DeliveryMailer.welcome(:delivery_method_options => overridden_options)
+ overridden_options = {user_name: "overridden", password: "somethingobtuse"}
+ mail_instance = DeliveryMailer.welcome(delivery_method_options: overridden_options)
delivery_method_instance = mail_instance.delivery_method
assert_equal "overridden", delivery_method_instance.settings[:user_name]
assert_equal "somethingobtuse", delivery_method_instance.settings[:password]
diff --git a/actionmailer/test/i18n_with_controller_test.rb b/actionmailer/test/i18n_with_controller_test.rb
index 68ed86e0d4..a3e93c9c31 100644
--- a/actionmailer/test/i18n_with_controller_test.rb
+++ b/actionmailer/test/i18n_with_controller_test.rb
@@ -9,15 +9,15 @@ class I18nTestMailer < ActionMailer::Base
def mail_with_i18n_subject(recipient)
@recipient = recipient
I18n.locale = :de
- mail(:to => recipient, :subject => "#{I18n.t :email_subject} #{recipient}",
- :from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
+ mail(to: recipient, subject: "#{I18n.t :email_subject} #{recipient}",
+ from: "system@loudthinking.com", date: Time.local(2004, 12, 12))
end
end
class TestController < ActionController::Base
def send_mail
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
- render :text => 'Mail sent'
+ render text: 'Mail sent'
end
end
@@ -32,7 +32,7 @@ class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
end
def setup
- I18n.backend.store_translations('de', :email_subject => '[Signed up] Welcome')
+ I18n.backend.store_translations('de', email_subject: '[Signed up] Welcome')
end
def teardown
diff --git a/actionmailer/test/mail_helper_test.rb b/actionmailer/test/mail_helper_test.rb
index d8a73e6c46..24ccaab8df 100644
--- a/actionmailer/test/mail_helper_test.rb
+++ b/actionmailer/test/mail_helper_test.rb
@@ -10,7 +10,7 @@ class HelperMailer < ActionMailer::Base
"it off!"
mail_with_defaults do |format|
- format.html { render(:inline => "<%= block_format @text %>") }
+ format.html { render(inline: "<%= block_format @text %>") }
end
end
@@ -18,7 +18,7 @@ class HelperMailer < ActionMailer::Base
@text = "But soft! What light through yonder window breaks?"
mail_with_defaults do |format|
- format.html { render(:inline => "<%= format_paragraph @text, 15, 1 %>") }
+ format.html { render(inline: "<%= format_paragraph @text, 15, 1 %>") }
end
end
@@ -26,19 +26,19 @@ class HelperMailer < ActionMailer::Base
@text = "Antidisestablishmentarianism is very long."
mail_with_defaults do |format|
- format.html { render(:inline => "<%= format_paragraph @text, 10, 1 %>") }
+ format.html { render(inline: "<%= format_paragraph @text, 10, 1 %>") }
end
end
def use_mailer
mail_with_defaults do |format|
- format.html { render(:inline => "<%= mailer.message.subject %>") }
+ format.html { render(inline: "<%= mailer.message.subject %>") }
end
end
def use_message
mail_with_defaults do |format|
- format.html { render(:inline => "<%= message.subject %>") }
+ format.html { render(inline: "<%= message.subject %>") }
end
end
@@ -55,15 +55,15 @@ The second
TEXT
mail_with_defaults do |format|
- format.html { render(:inline => "<%= block_format @text %>") }
+ format.html { render(inline: "<%= block_format @text %>") }
end
end
protected
def mail_with_defaults(&block)
- mail(:to => "test@localhost", :from => "tester@example.com",
- :subject => "using helpers", &block)
+ mail(to: "test@localhost", from: "tester@example.com",
+ subject: "using helpers", &block)
end
end
diff --git a/actionmailer/test/mail_layout_test.rb b/actionmailer/test/mail_layout_test.rb
index 71e93c29f1..7f959282cb 100644
--- a/actionmailer/test/mail_layout_test.rb
+++ b/actionmailer/test/mail_layout_test.rb
@@ -1,9 +1,9 @@
require 'abstract_unit'
class AutoLayoutMailer < ActionMailer::Base
- default :to => 'test@localhost',
- :subject => "You have a mail",
- :from => "tester@example.com"
+ default to: 'test@localhost',
+ subject: "You have a mail",
+ from: "tester@example.com"
def hello
mail()
@@ -11,16 +11,16 @@ class AutoLayoutMailer < ActionMailer::Base
def spam
@world = "Earth"
- mail(:body => render(:inline => "Hello, <%= @world %>", :layout => 'spam'))
+ mail(body: render(inline: "Hello, <%= @world %>", layout: 'spam'))
end
def nolayout
@world = "Earth"
- mail(:body => render(:inline => "Hello, <%= @world %>", :layout => false))
+ mail(body: render(inline: "Hello, <%= @world %>", layout: false))
end
def multipart(type = nil)
- mail(:content_type => type) do |format|
+ mail(content_type: type) do |format|
format.text { render }
format.html { render }
end
@@ -28,11 +28,11 @@ class AutoLayoutMailer < ActionMailer::Base
end
class ExplicitLayoutMailer < ActionMailer::Base
- layout 'spam', :except => [:logout]
+ layout 'spam', except: [:logout]
- default :to => 'test@localhost',
- :subject => "You have a mail",
- :from => "tester@example.com"
+ default to: 'test@localhost',
+ subject: "You have a mail",
+ from: "tester@example.com"
def signup
mail()
diff --git a/actionmailer/test/mailers/base_mailer.rb b/actionmailer/test/mailers/base_mailer.rb
index e55d72fdb4..f25d9b9aff 100644
--- a/actionmailer/test/mailers/base_mailer.rb
+++ b/actionmailer/test/mailers/base_mailer.rb
@@ -1,13 +1,13 @@
class BaseMailer < ActionMailer::Base
self.mailer_name = "base_mailer"
- default :to => 'system@test.lindsaar.net',
- :from => 'jose@test.plataformatec.com',
- :reply_to => 'mikel@test.lindsaar.net'
+ default to: 'system@test.lindsaar.net',
+ from: 'jose@test.plataformatec.com',
+ reply_to: 'mikel@test.lindsaar.net'
def welcome(hash = {})
headers['X-SPAM'] = "Not SPAM"
- mail({:subject => "The first email on new API!"}.merge!(hash))
+ mail({subject: "The first email on new API!"}.merge!(hash))
end
def welcome_with_headers(hash = {})
@@ -16,7 +16,7 @@ class BaseMailer < ActionMailer::Base
end
def welcome_from_another_path(path)
- mail(:template_name => "welcome", :template_path => path)
+ mail(template_name: "welcome", template_path: path)
end
def html_only(hash = {})
@@ -38,15 +38,15 @@ class BaseMailer < ActionMailer::Base
end
def attachment_with_hash
- attachments['invoice.jpg'] = { :data => "\312\213\254\232)b",
- :mime_type => "image/x-jpg",
- :transfer_encoding => "base64" }
+ attachments['invoice.jpg'] = { data: "\312\213\254\232)b",
+ mime_type: "image/x-jpg",
+ transfer_encoding: "base64" }
mail
end
def attachment_with_hash_default_encoding
- attachments['invoice.jpg'] = { :data => "\312\213\254\232)b",
- :mime_type => "image/x-jpg" }
+ attachments['invoice.jpg'] = { data: "\312\213\254\232)b",
+ mime_type: "image/x-jpg" }
mail
end
@@ -62,8 +62,8 @@ class BaseMailer < ActionMailer::Base
def explicit_multipart(hash = {})
attachments['invoice.pdf'] = 'This is test File content' if hash.delete(:attachments)
mail(hash) do |format|
- format.text { render :text => "TEXT Explicit Multipart" }
- format.html { render :text => "HTML Explicit Multipart" }
+ format.text { render text: "TEXT Explicit Multipart" }
+ format.html { render text: "HTML Explicit Multipart" }
end
end
@@ -76,13 +76,13 @@ class BaseMailer < ActionMailer::Base
def explicit_multipart_with_any(hash = {})
mail(hash) do |format|
- format.any(:text, :html){ render :text => "Format with any!" }
+ format.any(:text, :html){ render text: "Format with any!" }
end
end
def explicit_multipart_with_options(include_html = false)
mail do |format|
- format.text(:content_transfer_encoding => "base64"){ render "welcome" }
+ format.text(content_transfer_encoding: "base64"){ render "welcome" }
format.html{ render "welcome" } if include_html
end
end
@@ -95,24 +95,24 @@ class BaseMailer < ActionMailer::Base
end
def implicit_different_template(template_name='')
- mail(:template_name => template_name)
+ mail(template_name: template_name)
end
def explicit_different_template(template_name='')
mail do |format|
- format.text { render :template => "#{mailer_name}/#{template_name}" }
- format.html { render :template => "#{mailer_name}/#{template_name}" }
+ format.text { render template: "#{mailer_name}/#{template_name}" }
+ format.html { render template: "#{mailer_name}/#{template_name}" }
end
end
def different_layout(layout_name='')
mail do |format|
- format.text { render :layout => layout_name }
- format.html { render :layout => layout_name }
+ format.text { render layout: layout_name }
+ format.html { render layout: layout_name }
end
end
def email_with_translations
- mail :body => render("email_with_translations", :formats => [:html])
+ mail body: render("email_with_translations", formats: [:html])
end
end
diff --git a/actionmailer/test/mailers/proc_mailer.rb b/actionmailer/test/mailers/proc_mailer.rb
index 43916e1421..733633b575 100644
--- a/actionmailer/test/mailers/proc_mailer.rb
+++ b/actionmailer/test/mailers/proc_mailer.rb
@@ -1,7 +1,7 @@
class ProcMailer < ActionMailer::Base
- default :to => 'system@test.lindsaar.net',
+ default to: 'system@test.lindsaar.net',
'X-Proc-Method' => Proc.new { Time.now.to_i.to_s },
- :subject => Proc.new { give_a_greeting }
+ subject: Proc.new { give_a_greeting }
def welcome
mail
diff --git a/actionmailer/test/test_helper_test.rb b/actionmailer/test/test_helper_test.rb
index dd62164176..7c7f0b6fdc 100644
--- a/actionmailer/test/test_helper_test.rb
+++ b/actionmailer/test/test_helper_test.rb
@@ -3,9 +3,9 @@ require 'abstract_unit'
class TestHelperMailer < ActionMailer::Base
def test
@world = "Earth"
- mail :body => render(:inline => "Hello, <%= @world %>"),
- :to => "test@example.com",
- :from => "tester@example.com"
+ mail body: render(inline: "Hello, <%= @world %>"),
+ to: "test@example.com",
+ from: "tester@example.com"
end
end
diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb
index 2ea1723434..dcd80f46b5 100644
--- a/actionmailer/test/url_test.rb
+++ b/actionmailer/test/url_test.rb
@@ -19,9 +19,9 @@ class UrlTestMailer < ActionMailer::Base
def signed_up_with_url(recipient)
@recipient = recipient
- @welcome_url = url_for :host => "example.com", :controller => "welcome", :action => "greeting"
- mail(:to => recipient, :subject => "[Signed up] Welcome #{recipient}",
- :from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
+ @welcome_url = url_for host: "example.com", controller: "welcome", action: "greeting"
+ mail(to: recipient, subject: "[Signed up] Welcome #{recipient}",
+ from: "system@loudthinking.com", date: Time.local(2004, 12, 12))
end
end
@@ -58,7 +58,7 @@ class ActionMailerUrlTest < ActionMailer::TestCase
AppRoutes.draw do
get ':controller(/:action(/:id))'
- get '/welcome' => "foo#bar", :as => "welcome"
+ get '/welcome' => "foo#bar", as: "welcome"
end
expected = new_mail
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index 248677688f..fd02492834 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,5 +1,80 @@
## Rails 4.0.0 (unreleased) ##
+* Remove old asset tag concatenation (no longer needed now that we have the asset pipeline). *Josh Peek*
+
+* Accept :remote as symbolic option for `link_to` helper. *Riley Lynch*
+
+* Warn when the `:locals` option is passed to `assert_template` outside of a view test case
+ Fix #3415
+
+ *Yves Senn*
+
+* The `Rack::Cache` middleware is now disabled by default. To enable it,
+ set `config.action_dispatch.rack_cache = true` and add `gem rack-cache` to your Gemfile.
+
+ *Guillermo Iguaran*
+
+* `ActionController::Base.page_cache_extension` option is deprecated
+ in favour of `ActionController::Base.default_static_extension`.
+
+ *Francesco Rodriguez*
+
+* Action and Page caching has been extracted from Action Dispatch
+ as `actionpack-action_caching` and `actionpack-page_caching` gems.
+ Please read the `README.md` file on both gems for the usage.
+
+ *Francesco Rodriguez*
+
+* Failsafe exception returns text/plain. *Steve Klabnik*
+
+* Remove `rack-cache` dependency from Action Pack and declare it on Gemfile
+
+ *Guillermo Iguaran*
+
+* Rename internal variables on ActionController::TemplateAssertions to prevent
+ naming collisions. @partials, @templates and @layouts are now prefixed with an underscore.
+ Fix #7459
+
+ *Yves Senn*
+
+* `resource` and `resources` don't modify the passed options hash
+ Fix #7777
+
+ *Yves Senn*
+
+* Precompiled assets include aliases from foo.js to foo/index.js and vice versa.
+
+ # Precompiles phone-<digest>.css and aliases phone/index.css to phone.css.
+ config.assets.precompile = [ 'phone.css' ]
+
+ # Precompiles phone/index-<digest>.css and aliases phone.css to phone/index.css.
+ config.assets.precompile = [ 'phone/index.css' ]
+
+ # Both of these work with either precompile thanks to their aliases.
+ <%= stylesheet_link_tag 'phone', media: 'all' %>
+ <%= stylesheet_link_tag 'phone/index', media: 'all' %>
+
+ *Jeremy Kemper*
+
+* `assert_template` is no more passing with what ever string that matches
+ with the template name.
+
+ Before when we have a template `/layout/hello.html.erb`, `assert_template`
+ was passing with any string that matches. This behavior allowed false
+ positive like:
+
+ assert_template "layout"
+ assert_template "out/hello"
+
+ Now it only passes with:
+
+ assert_template "layout/hello"
+ assert_template "hello"
+
+ Fixes #3849.
+
+ *Hugolnx*
+
* `image_tag` will set the same width and height for image if numerical value
passed to `size` option.
@@ -44,9 +119,8 @@
*Luiz Felipe Garcia Pereira*
-* Sprockets integration has been extracted from Action Pack and the `sprockets-rails`
- gem should be added to Gemfile (under the assets group) in order to use Rails asset
- pipeline in future versions of Rails.
+* Sprockets integration has been extracted from Action Pack to the `sprockets-rails`
+ gem. `rails` gem is depending on `sprockets-rails` by default.
*Guillermo Iguaran*
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index fd09d3b55b..7d292ac17c 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -18,7 +18,6 @@ Gem::Specification.new do |s|
s.requirements << 'none'
s.add_dependency('activesupport', version)
- s.add_dependency('rack-cache', '~> 1.2')
s.add_dependency('builder', '~> 3.1.0')
s.add_dependency('rack', '~> 1.4.1')
s.add_dependency('rack-test', '~> 0.6.1')
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index fc27a0774b..462f147371 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -2,11 +2,9 @@ require 'fileutils'
require 'uri'
require 'set'
-module ActionController #:nodoc:
+module ActionController
# \Caching is a cheap way of speeding up slow applications by keeping the result of
# calculations, renderings, and database calls around for subsequent requests.
- # Action Controller affords you three approaches in varying levels of granularity:
- # Page, Action, Fragment.
#
# You can read more about each approach and the sweeping assistance by clicking the
# modules below.
@@ -17,8 +15,7 @@ module ActionController #:nodoc:
# == \Caching stores
#
# All the caching stores from ActiveSupport::Cache are available to be used as backends
- # for Action Controller caching. This setting only affects action and fragment caching
- # as page caching is always written to disk.
+ # for Action Controller caching.
#
# Configuration examples (MemoryStore is the default):
#
@@ -32,9 +29,7 @@ module ActionController #:nodoc:
extend ActiveSupport::Autoload
eager_autoload do
- autoload :Actions
autoload :Fragments
- autoload :Pages
autoload :Sweeper, 'action_controller/caching/sweeping'
autoload :Sweeping, 'action_controller/caching/sweeping'
end
@@ -58,12 +53,25 @@ module ActionController #:nodoc:
include AbstractController::Callbacks
include ConfigMethods
- include Pages, Actions, Fragments
+ include Fragments
include Sweeping if defined?(ActiveRecord)
included do
extend ConfigMethods
+ config_accessor :default_static_extension
+ self.default_static_extension ||= '.html'
+
+ def self.page_cache_extension=(extension)
+ ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
+ self.default_static_extension = extension
+ end
+
+ def self.page_cache_extension
+ ActiveSupport::Deprecation.deprecation_warning(:page_cache_extension, :default_static_extension)
+ default_static_extension
+ end
+
config_accessor :perform_caching
self.perform_caching = true if perform_caching.nil?
end
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
deleted file mode 100644
index bf16fe267c..0000000000
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ /dev/null
@@ -1,189 +0,0 @@
-require 'set'
-
-module ActionController
- module Caching
- # Action caching is similar to page caching by the fact that the entire
- # output of the response is cached, but unlike page caching, every
- # request still goes through Action Pack. The key benefit of this is
- # that filters run before the cache is served, which allows for
- # authentication and other restrictions on whether someone is allowed
- # to execute such action.
- #
- # class ListsController < ApplicationController
- # before_filter :authenticate, except: :public
- #
- # caches_page :public
- # caches_action :index, :show
- # end
- #
- # In this example, the +public+ action doesn't require authentication
- # so it's possible to use the faster page caching. On the other hand
- # +index+ and +show+ require authentication. They can still be cached,
- # but we need action caching for them.
- #
- # Action caching uses fragment caching internally and an around
- # filter to do the job. The fragment cache is named according to
- # the host and path of the request. A page that is accessed at
- # <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
- # <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
- # differentiate between <tt>david.example.com/lists/</tt> and
- # <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
- # the subdomain-as-account-key pattern.
- #
- # Different representations of the same resource, e.g.
- # <tt>http://david.example.com/lists</tt> and
- # <tt>http://david.example.com/lists.xml</tt>
- # are treated like separate requests and so are cached separately.
- # Keep in mind when expiring an action cache that
- # <tt>action: 'lists'</tt> is not the same as
- # <tt>action: 'list', format: :xml</tt>.
- #
- # You can modify the default action cache path by passing a
- # <tt>:cache_path</tt> option. This will be passed directly to
- # <tt>ActionCachePath.new</tt>. This is handy for actions with
- # multiple possible routes that should be cached differently. If a
- # block is given, it is called with the current controller instance.
- #
- # And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
- # proc that specifies when the action should be cached.
- #
- # As of Rails 3.0, you can also pass <tt>:expires_in</tt> with a time
- # interval (in seconds) to schedule expiration of the cached item.
- #
- # The following example depicts some of the points made above:
- #
- # class ListsController < ApplicationController
- # before_filter :authenticate, except: :public
- #
- # caches_page :public
- #
- # caches_action :index, if: Proc.new do
- # !request.format.json? # cache if is not a JSON request
- # end
- #
- # caches_action :show, cache_path: { project: 1 },
- # expires_in: 1.hour
- #
- # caches_action :feed, cache_path: Proc.new do
- # if params[:user_id]
- # user_list_url(params[:user_id, params[:id])
- # else
- # list_url(params[:id])
- # end
- # end
- # end
- #
- # If you pass <tt>layout: false</tt>, it will only cache your action
- # content. That's useful when your layout has dynamic information.
- #
- # Warning: If the format of the request is determined by the Accept HTTP
- # header the Content-Type of the cached response could be wrong because
- # no information about the MIME type is stored in the cache key. So, if
- # you first ask for MIME type M in the Accept header, a cache entry is
- # created, and then perform a second request to the same resource asking
- # for a different MIME type, you'd get the content cached for M.
- #
- # The <tt>:format</tt> parameter is taken into account though. The safest
- # way to cache by MIME type is to pass the format in the route.
- module Actions
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Declares that +actions+ should be cached.
- # See ActionController::Caching::Actions for details.
- def caches_action(*actions)
- return unless cache_configured?
- options = actions.extract_options!
- options[:layout] = true unless options.key?(:layout)
- filter_options = options.extract!(:if, :unless).merge(:only => actions)
- cache_options = options.extract!(:layout, :cache_path).merge(:store_options => options)
-
- around_filter ActionCacheFilter.new(cache_options), filter_options
- end
- end
-
- def _save_fragment(name, options)
- content = ""
- response_body.each do |parts|
- content << parts
- end
-
- if caching_allowed?
- write_fragment(name, content, options)
- else
- content
- end
- end
-
- protected
- def expire_action(options = {})
- return unless cache_configured?
-
- if options.is_a?(Hash) && options[:action].is_a?(Array)
- options[:action].each {|action| expire_action(options.merge(:action => action)) }
- else
- expire_fragment(ActionCachePath.new(self, options, false).path)
- end
- end
-
- class ActionCacheFilter #:nodoc:
- def initialize(options, &block)
- @cache_path, @store_options, @cache_layout =
- options.values_at(:cache_path, :store_options, :layout)
- end
-
- def around(controller)
- cache_layout = @cache_layout.respond_to?(:call) ? @cache_layout.call(controller) : @cache_layout
-
- path_options = if @cache_path.respond_to?(:call)
- controller.instance_exec(controller, &@cache_path)
- else
- @cache_path
- end
-
- cache_path = ActionCachePath.new(controller, path_options || {})
-
- body = controller.read_fragment(cache_path.path, @store_options)
-
- unless body
- controller.action_has_layout = false unless cache_layout
- yield
- controller.action_has_layout = true
- body = controller._save_fragment(cache_path.path, @store_options)
- end
-
- body = controller.render_to_string(:text => body, :layout => true) unless cache_layout
-
- controller.response_body = body
- controller.content_type = Mime[cache_path.extension || :html]
- end
- end
-
- class ActionCachePath
- attr_reader :path, :extension
-
- # If +infer_extension+ is +true+, the cache path extension is looked up from the request's
- # path and format. This is desirable when reading and writing the cache, but not when
- # expiring the cache - +expire_action+ should expire the same files regardless of the
- # request format.
- def initialize(controller, options = {}, infer_extension = true)
- if infer_extension
- @extension = controller.params[:format]
- options.reverse_merge!(:format => @extension) if options.is_a?(Hash)
- end
-
- path = controller.url_for(options).split('://', 2).last
- @path = normalize!(path)
- end
-
- private
- def normalize!(path)
- ext = URI.parser.escape(extension) if extension
- path << 'index' if path[-1] == ?/
- path << ".#{ext}" if extension and !path.split('?', 2).first.ends_with?(".#{ext}")
- URI.parser.unescape(path)
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
deleted file mode 100644
index 3cf8d965ff..0000000000
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ /dev/null
@@ -1,202 +0,0 @@
-require 'fileutils'
-require 'active_support/core_ext/class/attribute_accessors'
-
-module ActionController
- module Caching
- # Page caching is an approach to caching where the entire action output of is
- # stored as a HTML file that the web server can serve without going through
- # Action Pack. This is the fastest way to cache your content as opposed to going
- # dynamically through the process of generating the content. Unfortunately, this
- # incredible speed-up is only available to stateless pages where all visitors are
- # treated the same. Content management systems -- including weblogs and wikis --
- # have many pages that are a great fit for this approach, but account-based systems
- # where people log in and manipulate their own data are often less likely candidates.
- #
- # Specifying which actions to cache is done through the +caches_page+ class method:
- #
- # class WeblogController < ActionController::Base
- # caches_page :show, :new
- # end
- #
- # This will generate cache files such as <tt>weblog/show/5.html</tt> and
- # <tt>weblog/new.html</tt>, which match the URLs used that would normally trigger
- # dynamic page generation. Page caching works by configuring a web server to first
- # check for the existence of files on disk, and to serve them directly when found,
- # without passing the request through to Action Pack. This is much faster than
- # handling the full dynamic request in the usual way.
- #
- # Expiration of the cache is handled by deleting the cached file, which results
- # in a lazy regeneration approach where the cache is not restored before another
- # hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
- #
- # class WeblogController < ActionController::Base
- # def update
- # List.update(params[:list][:id], params[:list])
- # expire_page action: 'show', id: params[:list][:id]
- # redirect_to action: 'show', id: params[:list][:id]
- # end
- # end
- #
- # Additionally, you can expire caches using Sweepers that act on changes in
- # the model to determine when a cache is supposed to be expired.
- module Pages
- extend ActiveSupport::Concern
-
- included do
- # The cache directory should be the document root for the web server and is
- # set using <tt>Base.page_cache_directory = "/document/root"</tt>. For Rails,
- # this directory has already been set to Rails.public_path (which is usually
- # set to <tt>Rails.root + "/public"</tt>). Changing this setting can be useful
- # to avoid naming conflicts with files in <tt>public/</tt>, but doing so will
- # likely require configuring your web server to look in the new location for
- # cached files.
- class_attribute :page_cache_directory
- self.page_cache_directory ||= ''
-
- # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>.
- # In these cases, the page caching mechanism will add one in order to make it
- # easy for the cached files to be picked up properly by the web server. By
- # default, this cache extension is <tt>.html</tt>. If you want something else,
- # like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension.
- # In cases where a request already has an extension, such as <tt>.xml</tt>
- # or <tt>.rss</tt>, page caching will not add an extension. This allows it
- # to work well with RESTful apps.
- class_attribute :page_cache_extension
- self.page_cache_extension ||= '.html'
-
- # The compression used for gzip. If +false+ (default), the page is not compressed.
- # If can be a symbol showing the ZLib compression method, for example, <tt>:best_compression</tt>
- # or <tt>:best_speed</tt> or an integer configuring the compression level.
- class_attribute :page_cache_compression
- self.page_cache_compression ||= false
- end
-
- module ClassMethods
- # Expires the page that was cached with the +path+ as a key.
- #
- # expire_page '/lists/show'
- def expire_page(path)
- return unless perform_caching
- path = page_cache_path(path)
-
- instrument_page_cache :expire_page, path do
- File.delete(path) if File.exist?(path)
- File.delete(path + '.gz') if File.exist?(path + '.gz')
- end
- end
-
- # Manually cache the +content+ in the key determined by +path+.
- #
- # cache_page "I'm the cached content", '/lists/show'
- def cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION)
- return unless perform_caching
- path = page_cache_path(path, extension)
-
- instrument_page_cache :write_page, path do
- FileUtils.makedirs(File.dirname(path))
- File.open(path, "wb+") { |f| f.write(content) }
- if gzip
- Zlib::GzipWriter.open(path + '.gz', gzip) { |f| f.write(content) }
- end
- end
- end
-
- # Caches the +actions+ using the page-caching approach that'll store
- # the cache in a path within the +page_cache_directory+ that
- # matches the triggering url.
- #
- # You can also pass a <tt>:gzip</tt> option to override the class configuration one.
- #
- # # cache the index action
- # caches_page :index
- #
- # # cache the index action except for JSON requests
- # caches_page :index, if: Proc.new { !request.format.json? }
- #
- # # don't gzip images
- # caches_page :image, gzip: false
- def caches_page(*actions)
- return unless perform_caching
- options = actions.extract_options!
-
- gzip_level = options.fetch(:gzip, page_cache_compression)
- gzip_level = case gzip_level
- when Symbol
- Zlib.const_get(gzip_level.upcase)
- when Fixnum
- gzip_level
- when false
- nil
- else
- Zlib::BEST_COMPRESSION
- end
-
- after_filter({:only => actions}.merge(options)) do |c|
- c.cache_page(nil, nil, gzip_level)
- end
- end
-
- private
- def page_cache_file(path, extension)
- name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
- unless (name.split('/').last || name).include? '.'
- name << (extension || self.page_cache_extension)
- end
- return name
- end
-
- def page_cache_path(path, extension = nil)
- page_cache_directory.to_s + page_cache_file(path, extension)
- end
-
- def instrument_page_cache(name, path)
- ActiveSupport::Notifications.instrument("#{name}.action_controller", :path => path){ yield }
- end
- end
-
- # Expires the page that was cached with the +options+ as a key.
- #
- # expire_page controller: 'lists', action: 'show'
- def expire_page(options = {})
- return unless self.class.perform_caching
-
- if options.is_a?(Hash)
- if options[:action].is_a?(Array)
- options[:action].each do |action|
- self.class.expire_page(url_for(options.merge(:only_path => true, :action => action)))
- end
- else
- self.class.expire_page(url_for(options.merge(:only_path => true)))
- end
- else
- self.class.expire_page(options)
- end
- end
-
- # Manually cache the +content+ in the key determined by +options+. If no content is provided,
- # the contents of response.body is used. If no options are provided, the url of the current
- # request being handled is used.
- #
- # cache_page "I'm the cached content", controller: 'lists', action: 'show'
- def cache_page(content = nil, options = nil, gzip = Zlib::BEST_COMPRESSION)
- return unless self.class.perform_caching && caching_allowed?
-
- path = case options
- when Hash
- url_for(options.merge(:only_path => true, :format => params[:format]))
- when String
- options
- else
- request.path
- end
-
- if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
- extension = ".#{type_symbol}"
- end
-
- self.class.cache_page(content || response.body, path, extension, gzip)
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index f41d1bb4b9..3d274e7dd7 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -4,6 +4,8 @@ module ActionController
INTERNAL_PARAMS = %w(controller action format _method only_path)
def start_processing(event)
+ return unless logger.info?
+
payload = event.payload
params = payload[:params].except(*INTERNAL_PARAMS)
format = payload[:format]
@@ -14,6 +16,8 @@ module ActionController
end
def process_action(event)
+ return unless logger.info?
+
payload = event.payload
additions = ActionController::Base.log_process_action(payload)
@@ -22,35 +26,36 @@ module ActionController
exception_class_name = payload[:exception].first
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
end
- message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
message << " (#{additions.join(" | ")})" unless additions.blank?
info(message)
end
def halted_callback(event)
- info "Filter chain halted as #{event.payload[:filter]} rendered or redirected"
+ info("Filter chain halted as #{event.payload[:filter]} rendered or redirected")
end
def send_file(event)
- info("Sent file %s (%.1fms)" % [event.payload[:path], event.duration])
+ info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
end
def redirect_to(event)
- info "Redirected to #{event.payload[:location]}"
+ info("Redirected to #{event.payload[:location]}")
end
def send_data(event)
- info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration])
+ info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
end
%w(write_fragment read_fragment exist_fragment?
expire_fragment expire_page write_page).each do |method|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{method}(event)
+ return unless logger.info?
key_or_path = event.payload[:key] || event.payload[:path]
human_name = #{method.to_s.humanize.inspect}
- info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}")
+ info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
end
METHOD
end
diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb
index c9a81e4866..398454d39f 100644
--- a/actionpack/lib/action_controller/metal/strong_parameters.rb
+++ b/actionpack/lib/action_controller/metal/strong_parameters.rb
@@ -112,6 +112,11 @@ module ActionController
# params.permitted? # => true
# Person.new(params) # => #<Person id: nil, name: "Francesco">
def permit!
+ each_pair do |key, value|
+ convert_hashes_to_parameters(key, value)
+ self[key].permit! if self[key].respond_to? :permit!
+ end
+
@permitted = true
self
end
@@ -173,6 +178,7 @@ module ActionController
case filter
when Symbol, String then
params[filter] = self[filter] if has_key?(filter)
+ keys.grep(/\A#{Regexp.escape(filter)}\(\di\)\z/) { |key| params[key] = self[key] }
when Hash then
self.slice(*filter.keys).each do |key, values|
return unless values
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index f2c68432c1..ee0f053bad 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -32,7 +32,6 @@ module ActionController
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
- options.page_cache_directory ||= paths["public"].first
# Ensure readers methods get compiled
options.asset_path ||= app.config.asset_path
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 0caeef3192..ace5a2c822 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -12,16 +12,16 @@ module ActionController
end
def setup_subscriptions
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
ActiveSupport::Notifications.subscribe("render_template.action_view") do |name, start, finish, id, payload|
path = payload[:layout]
if path
- @layouts[path] += 1
+ @_layouts[path] += 1
if path =~ /^layouts\/(.*)/
- @layouts[$1] += 1
+ @_layouts[$1] += 1
end
end
end
@@ -32,11 +32,11 @@ module ActionController
partial = path =~ /^.*\/_[^\/]*$/
if partial
- @partials[path] += 1
- @partials[path.split("/").last] += 1
+ @_partials[path] += 1
+ @_partials[path.split("/").last] += 1
end
- @templates[path] += 1
+ @_templates[path] += 1
end
end
@@ -46,9 +46,9 @@ module ActionController
end
def process(*args)
- @partials = Hash.new(0)
- @templates = Hash.new(0)
- @layouts = Hash.new(0)
+ @_partials = Hash.new(0)
+ @_templates = Hash.new(0)
+ @_layouts = Hash.new(0)
super
end
@@ -86,52 +86,63 @@ module ActionController
response.body
case options
- when NilClass, String, Symbol, Regexp
+ when NilClass, Regexp, String, Symbol
options = options.to_s if Symbol === options
- rendered = @templates
+ rendered = @_templates
msg = message || sprintf("expecting <%s> but rendering with <%s>",
options.inspect, rendered.keys)
matches_template =
- if options
+ case options
+ when String
+ rendered.any? do |t, num|
+ options_splited = options.split(File::SEPARATOR)
+ t_splited = t.split(File::SEPARATOR)
+ t_splited.last(options_splited.size) == options_splited
+ end
+ when Regexp
rendered.any? { |t,num| t.match(options) }
- else
- @templates.blank?
+ when NilClass
+ rendered.blank?
end
assert matches_template, msg
when Hash
if options.key?(:layout)
expected_layout = options[:layout]
msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
- expected_layout, @layouts.keys)
+ expected_layout, @_layouts.keys)
case expected_layout
when String, Symbol
- assert_includes @layouts.keys, expected_layout.to_s, msg
+ assert_includes @_layouts.keys, expected_layout.to_s, msg
when Regexp
- assert(@layouts.keys.any? {|l| l =~ expected_layout }, msg)
+ assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
when nil, false
- assert(@layouts.empty?, msg)
+ assert(@_layouts.empty?, msg)
end
end
if expected_partial = options[:partial]
if expected_locals = options[:locals]
- actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
- expected_locals.each_pair do |k,v|
- assert_equal(v, actual_locals[k])
+ if defined?(@locals)
+ actual_locals = @locals[expected_partial.to_s.sub(/^_/,'')]
+ expected_locals.each_pair do |k,v|
+ assert_equal(v, actual_locals[k])
+ end
+ else
+ warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
end
elsif expected_count = options[:count]
- actual_count = @partials[expected_partial]
+ actual_count = @_partials[expected_partial]
msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
expected_partial, expected_count, actual_count)
assert(actual_count == expected_count.to_i, msg)
else
msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
- options[:partial], @partials.keys)
- assert_includes @partials, expected_partial, msg
+ options[:partial], @_partials.keys)
+ assert_includes @_partials, expected_partial, msg
end
elsif options.key?(:partial)
- assert @partials.empty?,
+ assert @_partials.empty?,
"Expected no partials to be rendered"
end
else
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index e4b3f53f07..0de10695e0 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -15,11 +15,11 @@ module ActionDispatch
# If any exception happens inside the exceptions app, this middleware
# catches the exceptions and returns a FAILSAFE_RESPONSE.
class ShowExceptions
- FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
- ["<html><body><h1>500 Internal Server Error</h1>" <<
+ FAILSAFE_RESPONSE = [500, { 'Content-Type' => 'text/plain' },
+ ["500 Internal Server Error\n" <<
"If you are the administrator of this website, then please read this web " <<
"application's log file and/or the web server's log file to find out what " <<
- "went wrong.</body></html>"]]
+ "went wrong."]]
def initialize(app, exceptions_app)
@app = app
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index 9073e6582d..e3b15b43b9 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -29,7 +29,7 @@ module ActionDispatch
def ext
@ext ||= begin
- ext = ::ActionController::Base.page_cache_extension
+ ext = ::ActionController::Base.default_static_extension
"{,#{ext},/index#{ext}}"
end
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index ccc0435a39..284dd180db 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -12,12 +12,7 @@ module ActionDispatch
config.action_dispatch.rescue_templates = { }
config.action_dispatch.rescue_responses = { }
config.action_dispatch.default_charset = nil
-
- config.action_dispatch.rack_cache = {
- :metastore => "rails:/",
- :entitystore => "rails:/",
- :verbose => false
- }
+ config.action_dispatch.rack_cache = false
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 49afa01d25..c5cf413c8f 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1038,7 +1038,7 @@ module ActionDispatch
# === Options
# Takes same options as +resources+.
def resource(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resource, resources, options, &block)
return self
@@ -1204,7 +1204,7 @@ module ActionDispatch
# # resource actions are at /admin/posts.
# resources :posts, :path => "admin/posts"
def resources(*resources, &block)
- options = resources.extract_options!
+ options = resources.extract_options!.dup
if apply_common_behavior_for(:resources, resources, options, &block)
return self
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
deleted file mode 100644
index e42e49fb04..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/file'
-require 'action_view/helpers/tag_helper'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class AssetIncludeTag #:nodoc:
- include TagHelper
-
- attr_reader :config, :asset_paths
- class_attribute :expansions
-
- def self.inherited(base)
- base.expansions = { }
- end
-
- def initialize(config, asset_paths)
- @config = config
- @asset_paths = asset_paths
- end
-
- def asset_name
- raise NotImplementedError
- end
-
- def extension
- raise NotImplementedError
- end
-
- def custom_dir
- raise NotImplementedError
- end
-
- def asset_tag(source, options)
- raise NotImplementedError
- end
-
- def include_tag(*sources)
- options = sources.extract_options!.stringify_keys
- concat = options.delete("concat")
- cache = concat || options.delete("cache")
- recursive = options.delete("recursive")
-
- if concat || (config.perform_caching && cache)
- joined_name = (cache == true ? "all" : cache) + ".#{extension}"
- joined_path = File.join((joined_name[/^#{File::SEPARATOR}/] ? config.assets_dir : custom_dir), joined_name)
- unless config.perform_caching && File.exists?(joined_path)
- write_asset_file_contents(joined_path, compute_paths(sources, recursive))
- end
- asset_tag(joined_name, options)
- else
- sources = expand_sources(sources, recursive)
- ensure_sources!(sources) if cache
- sources.collect { |source| asset_tag(source, options) }.join("\n").html_safe
- end
- end
-
- private
-
- def path_to_asset(source, options = {})
- asset_paths.compute_public_path(source, asset_name.to_s.pluralize, options.merge(:ext => extension))
- end
-
- def path_to_asset_source(source)
- asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension)
- end
-
- def compute_paths(*args)
- expand_sources(*args).collect { |source| path_to_asset_source(source) }
- end
-
- def expand_sources(sources, recursive)
- if sources.first == :all
- collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
- else
- sources.inject([]) do |list, source|
- determined_source = determine_source(source, expansions)
- update_source_list(list, determined_source)
- end
- end
- end
-
- def update_source_list(list, source)
- case source
- when String
- list.delete(source)
- list << source
- when Array
- updated_sources = source - list
- list.concat(updated_sources)
- end
- end
-
- def ensure_sources!(sources)
- sources.each do |source|
- asset_file_path!(path_to_asset_source(source))
- end
- end
-
- def collect_asset_files(*path)
- dir = path.first
-
- Dir[File.join(*path.compact)].collect do |file|
- file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
- end.sort
- end
-
- def determine_source(source, collection)
- case source
- when Symbol
- collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
- else
- source
- end
- end
-
- def join_asset_file_contents(paths)
- paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
- end
-
- def write_asset_file_contents(joined_asset_path, asset_paths)
- FileUtils.mkdir_p(File.dirname(joined_asset_path))
- File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
-
- # Set mtime to the latest of the combined files to allow for
- # consistent ETag without a shared filesystem.
- mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max
- File.utime(mt, mt, joined_asset_path)
- end
-
- def asset_file_path!(absolute_path, error_if_file_is_uri = false)
- if asset_paths.is_uri?(absolute_path)
- raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
- else
- raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
- return absolute_path
- end
- end
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index 139f4d19ab..b6b9eadc88 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -1,76 +1,11 @@
require 'active_support/core_ext/file'
-require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
module ActionView
module Helpers
module AssetTagHelper
-
- class JavascriptIncludeTag < AssetIncludeTag #:nodoc:
- def asset_name
- 'javascript'
- end
-
- def extension
- 'js'
- end
-
- def asset_tag(source, options)
- content_tag("script", "", { "src" => path_to_asset(source) }.merge(options))
- end
-
- def custom_dir
- config.javascripts_dir
- end
-
- private
-
- def expand_sources(sources, recursive = false)
- if sources.include?(:all)
- all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application'])
- add_application_js(all_asset_files, sources)
- ((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
- else
- expanded_sources = sources.inject([]) do |list, source|
- determined_source = determine_source(source, expansions)
- update_source_list(list, determined_source)
- end
- add_application_js(expanded_sources, sources)
- expanded_sources
- end
- end
-
- def add_application_js(expanded_sources, sources)
- if (sources.include?(:defaults) || sources.include?(:all)) && File.exist?(File.join(custom_dir, "application.#{extension}"))
- expanded_sources.delete('application')
- expanded_sources << "application"
- end
- end
- end
-
-
module JavascriptTagHelpers
extend ActiveSupport::Concern
- module ClassMethods
- # Register one or more javascript files to be included when <tt>symbol</tt>
- # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register javascript files
- # that the plugin installed in <tt>public/javascripts</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
- #
- # javascript_include_tag :monkey # =>
- # <script src="/javascripts/head.js"></script>
- # <script src="/javascripts/body.js"></script>
- # <script src="/javascripts/tail.js"></script>
- def register_javascript_expansion(expansions)
- js_expansions = JavascriptIncludeTag.expansions
- expansions.each do |key, values|
- js_expansions[key] = (js_expansions[key] || []) | Array(values)
- end
- end
- end
-
# Computes the path to a javascript asset in the public javascripts directory.
# If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
# Full paths from the document root will be passed through.
@@ -101,15 +36,6 @@ module ActionView
#
# When passing paths, the ".js" extension is optional.
#
- # If the application is not using the asset pipeline, to include the default JavaScript
- # expansion pass <tt>:defaults</tt> as source. By default, <tt>:defaults</tt> loads jQuery,
- # and that can be overridden in <tt>config/application.rb</tt>:
- #
- # config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
- #
- # When using <tt>:defaults</tt> or <tt>:all</tt>, if an <tt>application.js</tt> file exists
- # in <tt>public/javascripts</tt> it will be included as well at the end.
- #
# You can modify the HTML attributes of the script tag by passing a hash as the
# last argument.
#
@@ -129,65 +55,14 @@ module ActionView
# javascript_include_tag "http://www.example.com/xmlhr.js"
# # => <script src="http://www.example.com/xmlhr.js"></script>
#
- # javascript_include_tag :defaults
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # Note: The application.js file is only referenced if it exists
- #
- # You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
- #
- # javascript_include_tag :all
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/shop.js?1284139606"></script>
- # # <script src="/javascripts/checkout.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # Note that your defaults of choice will be included first, so they will be available to all subsequently
- # included files.
- #
- # If you want Rails to search in all the subdirectories under <tt>public/javascripts</tt>, you should
- # explicitly set <tt>:recursive</tt>:
- #
- # javascript_include_tag :all, :recursive => true
- #
- # == Caching multiple JavaScripts into one
- #
- # You can also cache multiple JavaScripts into one file, which requires less HTTP connections to download
- # and can better be compressed by gzip (leading to faster transfers). Caching will only happen if
- # <tt>config.perform_caching</tt> is set to true (which is the case by default for the Rails
- # production environment, but not for the development environment).
- #
- # # assuming config.perform_caching is false
- # javascript_include_tag :all, :cache => true
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/shop.js?1284139606"></script>
- # # <script src="/javascripts/checkout.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # # assuming config.perform_caching is true
- # javascript_include_tag :all, :cache => true
- # # => <script src="/javascripts/all.js?1344139789"></script>
- #
- # # assuming config.perform_caching is false
- # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/cart.js?1289139157"></script>
- # # <script src="/javascripts/checkout.js?1299139816"></script>
- #
- # # assuming config.perform_caching is true
- # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
- # # => <script src="/javascripts/shop.js?1299139816"></script>
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # javascript_include_tag :all, :cache => true, :recursive => true
def javascript_include_tag(*sources)
- @javascript_include ||= JavascriptIncludeTag.new(config, asset_paths)
- @javascript_include.include_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ sources.dup.map { |source|
+ tag_options = {
+ "src" => path_to_javascript(source)
+ }.merge(options)
+ content_tag(:script, "", tag_options)
+ }.join("\n").html_safe
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
index e3a86a8889..bf59f8e81a 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
@@ -1,53 +1,11 @@
require 'active_support/core_ext/file'
-require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
module ActionView
module Helpers
module AssetTagHelper
-
- class StylesheetIncludeTag < AssetIncludeTag #:nodoc:
- def asset_name
- 'stylesheet'
- end
-
- def extension
- 'css'
- end
-
- def asset_tag(source, options)
- # We force the :request protocol here to avoid a double-download bug in IE7 and IE8
- tag("link", { "rel" => "stylesheet", "media" => "screen", "href" => path_to_asset(source, :protocol => :request) }.merge(options))
- end
-
- def custom_dir
- config.stylesheets_dir
- end
- end
-
-
module StylesheetTagHelpers
extend ActiveSupport::Concern
- module ClassMethods
- # Register one or more stylesheet files to be included when <tt>symbol</tt>
- # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register stylesheet files
- # that the plugin installed in <tt>public/stylesheets</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
- #
- # stylesheet_link_tag :monkey # =>
- # <link href="/stylesheets/head.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/body.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" />
- def register_stylesheet_expansion(expansions)
- style_expansions = StylesheetIncludeTag.expansions
- expansions.each do |key, values|
- style_expansions[key] = (style_expansions[key] || []) | Array(values)
- end
- end
- end
-
# Computes the path to a stylesheet asset in the public stylesheets directory.
# If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
# Full paths from the document root will be passed through.
@@ -96,56 +54,18 @@ module ActionView
# <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
# <link href="/css/stylish.css" media="screen" rel="stylesheet" />
#
- # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
- #
- # stylesheet_link_tag :all # =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" />
- #
- # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
- #
- # stylesheet_link_tag :all, :recursive => true
- #
- # == Caching multiple stylesheets into one
- #
- # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
- # compressed by gzip (leading to faster transfers). Caching will only happen if +config.perform_caching+
- # is set to true (which is the case by default for the Rails production environment, but not for the development
- # environment). Examples:
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is false =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is true =>
- # <link href="/stylesheets/all.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is false =>
- # <link href="/stylesheets/shop.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/cart.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is true =>
- # <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" />
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # stylesheet_link_tag :all, :cache => true, :recursive => true
- #
- # To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
- # you have too many stylesheets for IE to load.
- #
- # stylesheet_link_tag :all, :concat => true
- #
def stylesheet_link_tag(*sources)
- @stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
- @stylesheet_include.include_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ sources.uniq.map { |source|
+ tag_options = {
+ "rel" => "stylesheet",
+ "media" => "screen",
+ "href" => path_to_stylesheet(source)
+ }.merge(options)
+ tag(:link, tag_options)
+ }.join("\n").html_safe
end
-
end
-
end
end
end
diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb
index 59e1015976..ddac87a37d 100644
--- a/actionpack/lib/action_view/helpers/cache_helper.rb
+++ b/actionpack/lib/action_view/helpers/cache_helper.rb
@@ -2,11 +2,11 @@ module ActionView
# = Action View Cache Helper
module Helpers
module CacheHelper
- # This helper exposes a method for caching fragments of a view
+ # This helper exposes a method for caching fragments of a view
# rather than an entire action or page. This technique is useful
# caching pieces like menus, lists of newstopics, static HTML
# fragments, and so on. This method takes a block that contains
- # the content you wish to cache.
+ # the content you wish to cache.
#
# The best way to use this is by doing key-based cache expiration
# on top of a cache store like Memcached that'll automatically
@@ -56,40 +56,40 @@ module ActionView
#
# Most template dependencies can be derived from calls to render in the template itself.
# Here are some examples of render calls that Cache Digests knows how to decode:
- #
+ #
# render partial: "comments/comment", collection: commentable.comments
# render "comments/comments"
# render 'comments/comments'
# render('comments/comments')
- #
+ #
# render "header" => render("comments/header")
- #
+ #
# render(@topic) => render("topics/topic")
# render(topics) => render("topics/topic")
# render(message.topics) => render("topics/topic")
- #
+ #
# It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
- #
+ #
# render group_of_attachments
# render @project.documents.where(published: true).order('created_at')
- #
+ #
# You will have to rewrite those to the explicit form:
- #
+ #
# render partial: 'attachments/attachment', collection: group_of_attachments
# render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
#
# === Explicit dependencies
- #
+ #
# Some times you'll have template dependencies that can't be derived at all. This is typically
# the case when you have template rendering that happens in helpers. Here's an example:
- #
+ #
# <%= render_sortable_todolists @project.todolists %>
- #
+ #
# You'll need to use a special comment format to call those out:
- #
+ #
# <%# Template Dependency: todolists/todolist %>
# <%= render_sortable_todolists @project.todolists %>
- #
+ #
# The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
# You can only declare one template dependency per line.
#
@@ -113,6 +113,17 @@ module ActionView
nil
end
+ def fragment_name_with_digest(name) #:nodoc:
+ if @virtual_path
+ [
+ *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
+ Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
+ ]
+ else
+ name
+ end
+ end
+
private
# TODO: Create an object that has caching read/write on it
def fragment_for(name = {}, options = nil, &block) #:nodoc:
@@ -131,17 +142,6 @@ module ActionView
controller.write_fragment(name, fragment, options)
end
end
-
- def fragment_name_with_digest(name)
- if @virtual_path
- [
- *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
- Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context)
- ]
- else
- name
- end
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 0bb08cd7ff..b87c2e936f 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -207,7 +207,7 @@ module ActionView
# if the object's class is +Post+. However, this can be overwritten using
# the <tt>:as</tt> option, e.g. -
#
- # <%= form_for(@person, :as => :client) do |f| %>
+ # <%= form_for(@person, as: :client) do |f| %>
# ...
# <% end %>
#
@@ -242,7 +242,7 @@ module ActionView
#
# is then equivalent to something like:
#
- # <%= form_for @post, :as => :post, :url => post_path(@post), :method => :put, :html => { :class => "edit_post", :id => "edit_post_45" } do |f| %>
+ # <%= form_for @post, as: :post, url: post_path(@post), method: :put, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
# ...
# <% end %>
#
@@ -254,19 +254,19 @@ module ActionView
#
# is equivalent to something like:
#
- # <%= form_for @post, :as => :post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %>
+ # <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
# ...
# <% end %>
#
# However you can still overwrite individual conventions, such as:
#
- # <%= form_for(@post, :url => super_posts_path) do |f| %>
+ # <%= form_for(@post, url: super_posts_path) do |f| %>
# ...
# <% end %>
#
# You can also set the answer format, like this:
#
- # <%= form_for(@post, :format => :json) do |f| %>
+ # <%= form_for(@post, format: :json) do |f| %>
# ...
# <% end %>
#
@@ -290,7 +290,7 @@ module ActionView
#
# You can force the form to use the full array of HTTP verbs by setting
#
- # :method => (:get|:post|:patch|:put|:delete)
+ # method: (:get|:post|:patch|:put|:delete)
#
# in the options hash. If the verb is not GET or POST, which are natively
# supported by HTML forms, the form will be set to POST and a hidden input
@@ -300,7 +300,7 @@ module ActionView
#
# Specifying:
#
- # :remote => true
+ # remote: true
#
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
# behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
@@ -310,7 +310,7 @@ module ActionView
#
# Example:
#
- # <%= form_for(@post, :remote => true) do |f| %>
+ # <%= form_for(@post, remote: true) do |f| %>
# ...
# <% end %>
#
@@ -354,7 +354,7 @@ module ActionView
# Example:
#
# <%= form_for(@post) do |f| %>
- # <%= f.fields_for(:comments, :include_id => false) do |cf| %>
+ # <%= f.fields_for(:comments, include_id: false) do |cf| %>
# ...
# <% end %>
# <% end %>
@@ -366,7 +366,7 @@ module ActionView
# custom builder. For example, let's say you made a helper to
# automatically add labels to form inputs.
#
- # <%= form_for @person, :url => { :action => "create" }, :builder => LabellingFormBuilder do |f| %>
+ # <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
# <%= f.text_field :first_name %>
# <%= f.text_field :last_name %>
# <%= f.text_area :biography %>
@@ -390,7 +390,7 @@ module ActionView
#
# def labelled_form_for(record_or_name_or_array, *args, &proc)
# options = args.extract_options!
- # form_for(record_or_name_or_array, *(args << options.merge(:builder => LabellingFormBuilder)), &proc)
+ # form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &proc)
# end
#
# If you don't need to attach a form to a model instance, then check out
@@ -403,13 +403,13 @@ module ActionView
#
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
#
- # <%= form_for @invoice, :url => external_url, :authenticity_token => 'external_token' do |f|
+ # <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
# ...
# <% end %>
#
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
#
- # <%= form_for @invoice, :url => external_url, :authenticity_token => false do |f|
+ # <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
# ...
# <% end %>
def form_for(record, options = {}, &proc)
@@ -423,7 +423,7 @@ module ActionView
object = nil
else
object = record.is_a?(Array) ? record.last : record
- raise ArgumentError, "First argument in form cannot contain nil or be empty" if object.blank?
+ raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
object_name = options[:as] || model_name_from_record_or_class(object).param_key
apply_form_for_options!(record, object, options)
end
@@ -435,7 +435,7 @@ module ActionView
builder = options[:parent_builder] = instantiate_builder(object_name, object, options)
fields_for = fields_for(object_name, object, options, &proc)
- default_options = builder.multipart? ? { :multipart => true } : {}
+ default_options = builder.multipart? ? { multipart: true } : {}
default_options.merge!(options.delete(:html))
form_tag(options.delete(:url) || {}, default_options) { fields_for }
@@ -447,12 +447,12 @@ module ActionView
as = options[:as]
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
options[:html].reverse_merge!(
- :class => as ? "#{action}_#{as}" : dom_class(object, action),
- :id => as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
- :method => method
+ class: as ? "#{action}_#{as}" : dom_class(object, action),
+ id: as ? "#{action}_#{as}" : [options[:namespace], dom_id(object, action)].compact.join("_").presence,
+ method: method
)
- options[:url] ||= polymorphic_path(record, :format => options.delete(:format))
+ options[:url] ||= polymorphic_path(record, format: options.delete(:format))
end
private :apply_form_for_options!
@@ -578,7 +578,7 @@ module ActionView
#
# class Person < ActiveRecord::Base
# has_one :address
- # accepts_nested_attributes_for :address, :allow_destroy => true
+ # accepts_nested_attributes_for :address, allow_destroy: true
# end
#
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
@@ -674,7 +674,7 @@ module ActionView
#
# class Person < ActiveRecord::Base
# has_many :projects
- # accepts_nested_attributes_for :projects, :allow_destroy => true
+ # accepts_nested_attributes_for :projects, allow_destroy: true
# end
#
# This will allow you to specify which models to destroy in the
@@ -747,10 +747,10 @@ module ActionView
# label(:post, :title, "A short title")
# # => <label for="post_title">A short title</label>
#
- # label(:post, :title, "A short title", :class => "title_label")
+ # label(:post, :title, "A short title", class: "title_label")
# # => <label for="post_title" class="title_label">A short title</label>
#
- # label(:post, :privacy, "Public Post", :value => "public")
+ # label(:post, :privacy, "Public Post", value: "public")
# # => <label for="post_privacy_public">Public Post</label>
#
# label(:post, :terms) do
@@ -766,18 +766,17 @@ module ActionView
# shown.
#
# ==== Examples
- # text_field(:post, :title, :size => 20)
+ # text_field(:post, :title, size: 20)
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
#
- # text_field(:post, :title, :class => "create_input")
+ # text_field(:post, :title, class: "create_input")
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
#
- # text_field(:session, :user, :onchange => "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
+ # text_field(:session, :user, onchange: "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }")
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/>
#
- # text_field(:snippet, :code, :size => 20, :class => 'code_input')
+ # text_field(:snippet, :code, size: 20, class: 'code_input')
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
- #
def text_field(object_name, method, options = {})
Tags::TextField.new(object_name, method, self, options).render
end
@@ -788,18 +787,17 @@ module ActionView
# shown. For security reasons this field is blank by default; pass in a value via +options+ if this is not desired.
#
# ==== Examples
- # password_field(:login, :pass, :size => 20)
+ # password_field(:login, :pass, size: 20)
# # => <input type="password" id="login_pass" name="login[pass]" size="20" />
#
- # password_field(:account, :secret, :class => "form_input", :value => @account.secret)
+ # password_field(:account, :secret, class: "form_input", value: @account.secret)
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
#
- # password_field(:user, :password, :onchange => "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
+ # password_field(:user, :password, onchange: "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }")
# # => <input type="password" id="user_password" name="user[password]" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/>
#
- # password_field(:account, :pin, :size => 20, :class => 'form_input')
+ # password_field(:account, :pin, size: 20, class: 'form_input')
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
- #
def password_field(object_name, method, options = {})
Tags::PasswordField.new(object_name, method, self, options).render
end
@@ -833,12 +831,11 @@ module ActionView
# file_field(:user, :avatar)
# # => <input type="file" id="user_avatar" name="user[avatar]" />
#
- # file_field(:post, :attached, :accept => 'text/html')
+ # file_field(:post, :attached, accept: 'text/html')
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
#
- # file_field(:attachment, :file, :class => 'file_input')
+ # file_field(:attachment, :file, class: 'file_input')
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
- #
def file_field(object_name, method, options = {})
Tags::FileField.new(object_name, method, self, options).render
end
@@ -848,22 +845,22 @@ module ActionView
# hash with +options+.
#
# ==== Examples
- # text_area(:post, :body, :cols => 20, :rows => 40)
+ # text_area(:post, :body, cols: 20, rows: 40)
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
# # #{@post.body}
# # </textarea>
#
- # text_area(:comment, :text, :size => "20x30")
+ # text_area(:comment, :text, size: "20x30")
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
# # #{@comment.text}
# # </textarea>
#
- # text_area(:application, :notes, :cols => 40, :rows => 15, :class => 'app_input')
+ # text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
# # #{@application.notes}
# # </textarea>
#
- # text_area(:entry, :body, :size => "20x20", :disabled => 'disabled')
+ # text_area(:entry, :body, size: "20x20", disabled: 'disabled')
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
# # #{@entry.body}
# # </textarea>
@@ -902,7 +899,7 @@ module ActionView
# Unfortunately that workaround does not work when the check box goes
# within an array-like parameter, as in
#
- # <%= fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
+ # <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
# <%= form.check_box :paid %>
# ...
# <% end %>
@@ -924,10 +921,9 @@ module ActionView
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
#
- # check_box("eula", "accepted", { :class => 'eula_check' }, "yes", "no")
+ # check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
# # => <input name="eula[accepted]" type="hidden" value="no" />
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
- #
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
end
@@ -936,7 +932,7 @@ module ActionView
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
# radio button will be checked.
#
- # To force the radio button to be checked pass <tt>:checked => true</tt> in the
+ # To force the radio button to be checked pass <tt>checked: true</tt> in the
# +options+ hash. You may pass HTML options there as well.
#
# # Let's say that @post.category returns "rails":
@@ -957,7 +953,6 @@ module ActionView
#
# color_field("car", "color")
# # => <input id="car_color" name="car[color]" type="color" value="#000000" />
- #
def color_field(object_name, method, options = {})
Tags::ColorField.new(object_name, method, self, options).render
end
@@ -968,18 +963,18 @@ module ActionView
#
# search_field(:user, :name)
# # => <input id="user_name" name="user[name]" type="search" />
- # search_field(:user, :name, :autosave => false)
+ # search_field(:user, :name, autosave: false)
# # => <input autosave="false" id="user_name" name="user[name]" type="search" />
- # search_field(:user, :name, :results => 3)
+ # search_field(:user, :name, results: 3)
# # => <input id="user_name" name="user[name]" results="3" type="search" />
# # Assume request.host returns "www.example.com"
- # search_field(:user, :name, :autosave => true)
+ # search_field(:user, :name, autosave: true)
# # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
- # search_field(:user, :name, :onsearch => true)
+ # search_field(:user, :name, onsearch: true)
# # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
- # search_field(:user, :name, :autosave => false, :onsearch => true)
+ # search_field(:user, :name, autosave: false, onsearch: true)
# # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
- # search_field(:user, :name, :autosave => true, :onsearch => true)
+ # 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" type="search" />
def search_field(object_name, method, options = {})
Tags::SearchField.new(object_name, method, self, options).render
@@ -1351,7 +1346,7 @@ module ActionView
private
def objectify_options(options)
- @default_options.merge(options.merge(:object => @object))
+ @default_options.merge(options.merge(object: @object))
end
def submit_default_value
@@ -1369,7 +1364,7 @@ module ActionView
defaults << :"helpers.submit.#{key}"
defaults << "#{key.to_s.humanize} #{model}"
- I18n.t(defaults.shift, :model => model, :default => defaults)
+ I18n.t(defaults.shift, model: model, default: defaults)
end
def nested_attributes_association?(association_name)
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 3f65791aa0..5105d0e585 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -46,7 +46,6 @@ module ActionView
end
protected :_back_url
-
# Creates a link tag of the given +name+ using a URL created by the set of +options+.
# See the valid options in the documentation for +url_for+. It's also possible to
# pass a String instead of an options hash, which generates a link tag that uses the
@@ -115,7 +114,7 @@ module ActionView
#
# in place of the older more verbose, non-resource-oriented
#
- # link_to "Profile", :controller => "profiles", :action => "show", :id => @profile
+ # link_to "Profile", controller: "profiles", action: "show", id: @profile
# # => <a href="/profiles/show/1">Profile</a>
#
# Similarly,
@@ -125,7 +124,7 @@ module ActionView
#
# is better than
#
- # link_to "Profiles", :controller => "profiles"
+ # link_to "Profiles", controller: "profiles"
# # => <a href="/profiles">Profiles</a>
#
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
@@ -139,45 +138,46 @@ module ActionView
#
# Classes and ids for CSS are easy to produce:
#
- # link_to "Articles", articles_path, :id => "news", :class => "article"
+ # link_to "Articles", articles_path, id: "news", class: "article"
# # => <a href="/articles" class="article" id="news">Articles</a>
#
# Be careful when using the older argument style, as an extra literal hash is needed:
#
- # link_to "Articles", { :controller => "articles" }, :id => "news", :class => "article"
+ # link_to "Articles", { controller: "articles" }, id: "news", class: "article"
# # => <a href="/articles" class="article" id="news">Articles</a>
#
# Leaving the hash off gives the wrong link:
#
- # link_to "WRONG!", :controller => "articles", :id => "news", :class => "article"
+ # link_to "WRONG!", controller: "articles", id: "news", class: "article"
# # => <a href="/articles/index/news?class=article">WRONG!</a>
#
# +link_to+ can also produce links with anchors or query strings:
#
- # link_to "Comment wall", profile_path(@profile, :anchor => "wall")
+ # link_to "Comment wall", profile_path(@profile, anchor: "wall")
# # => <a href="/profiles/1#wall">Comment wall</a>
#
- # link_to "Ruby on Rails search", :controller => "searches", :query => "ruby on rails"
+ # link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
# # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
#
- # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux")
+ # link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
# # => <a href="/searches?foo=bar&amp;baz=quux">Nonsense search</a>
#
# The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
#
- # link_to("Destroy", "http://www.example.com", :method => :delete)
+ # link_to("Destroy", "http://www.example.com", method: :delete)
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
#
# You can also use custom data attributes using the <tt>:data</tt> option:
#
- # link_to "Visit Other Site", "http://www.rubyonrails.org/", :data => { :confirm => "Are you sure?" }
+ # link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?"">Visit Other Site</a>
def link_to(name = nil, options = nil, html_options = nil, &block)
html_options, options = options, name if block_given?
options ||= {}
- url = url_for(options)
html_options = convert_options_to_data_attributes(options, html_options)
+
+ url = url_for(options)
html_options['href'] ||= url
content_tag(:a, name || url, html_options, &block)
@@ -196,7 +196,7 @@ module ActionView
# the form submission and input element behavior using +html_options+.
# This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
# If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
- # You can also disable the button by passing <tt>:disabled => true</tt> in +html_options+.
+ # You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
# If you are using RESTful routes, you can pass the <tt>:method</tt>
# to change the HTTP verb used to submit the form.
#
@@ -225,7 +225,7 @@ module ActionView
# by the unobtrusive JavaScript driver.
#
# ==== Examples
- # <%= button_to "New", :action => "new" %>
+ # <%= button_to "New", action: "new" %>
# # => "<form method="post" action="/controller/new" class="button_to">
# # <div><input value="New" type="submit" /></div>
# # </form>"
@@ -241,13 +241,13 @@ module ActionView
# # </div>
# # </form>"
#
- # <%= button_to "New", :action => "new", :form_class => "new-thing" %>
+ # <%= button_to "New", action: "new", form_class: "new-thing" %>
# # => "<form method="post" action="/controller/new" class="new-thing">
# # <div><input value="New" type="submit" /></div>
# # </form>"
#
#
- # <%= button_to "Create", :action => "create", :remote => true, :form => { "data-type" => "json" } %>
+ # <%= button_to "Create", action: "create", remote: true, form: { "data-type" => "json" } %>
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
# # <div>
# # <input value="Create" type="submit" />
@@ -256,8 +256,8 @@ module ActionView
# # </form>"
#
#
- # <%= button_to "Delete Image", { :action => "delete", :id => @image.id },
- # :method => :delete, :data => { :confirm => "Are you sure?" } %>
+ # <%= button_to "Delete Image", { action: "delete", id: @image.id },
+ # method: :delete, data: { confirm: "Are you sure?" } %>
# # => "<form method="post" action="/images/delete/1" class="button_to">
# # <div>
# # <input type="hidden" name="_method" value="delete" />
@@ -268,7 +268,7 @@ module ActionView
#
#
# <%= button_to('Destroy', 'http://www.example.com',
- # :method => "delete", :remote => true, :data => { :confirm' => 'Are you sure?', :disable_with => 'loading...' }) %>
+ # method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
# # <div>
# # <input name='_method' value='delete' type='hidden' />
@@ -294,7 +294,7 @@ module ActionView
form_method = method == 'get' ? 'get' : 'post'
form_options = html_options.delete('form') || {}
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
- form_options.merge!(:method => form_method, :action => url)
+ form_options.merge!(method: form_method, action: url)
form_options.merge!("data-remote" => "true") if remote
request_token_tag = form_method == 'post' ? token_tag : ''
@@ -313,7 +313,6 @@ module ActionView
content_tag('form', content_tag('div', inner_tags), form_options)
end
-
# Creates a link tag of the given +name+ using a URL created by the set of
# +options+ unless the current request URI is the same as the links, in
# which case only the name is returned (or the given block is yielded, if
@@ -325,8 +324,8 @@ module ActionView
# Let's say you have a navigation menu...
#
# <ul id="navbar">
- # <li><%= link_to_unless_current("Home", { :action => "index" }) %></li>
- # <li><%= link_to_unless_current("About Us", { :action => "about" }) %></li>
+ # <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
+ # <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
# </ul>
#
# If in the "about" action, it will render...
@@ -348,8 +347,8 @@ module ActionView
# "Go Back" link instead of a link to the comments page, we could do something like this...
#
# <%=
- # link_to_unless_current("Comment", { :controller => "comments", :action => "new" }) do
- # link_to("Go back", { :controller => "posts", :action => "index" })
+ # link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
+ # link_to("Go back", { controller: "posts", action: "index" })
# end
# %>
def link_to_unless_current(name, options = {}, html_options = {}, &block)
@@ -363,13 +362,13 @@ module ActionView
# accepts the name or the full argument list for +link_to_unless+.
#
# ==== Examples
- # <%= link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) %>
+ # <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
# # If the user is logged in...
# # => <a href="/controller/reply/">Reply</a>
#
# <%=
- # link_to_unless(@current_user.nil?, "Reply", { :action => "reply" }) do |name|
- # link_to(name, { :controller => "accounts", :action => "signup" })
+ # link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
+ # link_to(name, { controller: "accounts", action: "signup" })
# end
# %>
# # If the user is logged in...
@@ -395,13 +394,13 @@ module ActionView
# in +link_to_unless+).
#
# ==== Examples
- # <%= link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) %>
+ # <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
# # If the user isn't logged in...
# # => <a href="/sessions/new/">Login</a>
#
# <%=
- # link_to_if(@current_user.nil?, "Login", { :controller => "sessions", :action => "new" }) do
- # link_to(@current_user.login, { :controller => "accounts", :action => "show", :id => @current_user })
+ # link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
+ # link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
# end
# %>
# # If the user isn't logged in...
@@ -442,17 +441,17 @@ module ActionView
# mail_to "me@domain.com"
# # => <a href="mailto:me@domain.com">me@domain.com</a>
#
- # mail_to "me@domain.com", "My email", :encode => "javascript"
+ # mail_to "me@domain.com", "My email", encode: "javascript"
# # => <script>eval(decodeURIComponent('%64%6f%63...%27%29%3b'))</script>
#
- # mail_to "me@domain.com", "My email", :encode => "hex"
+ # mail_to "me@domain.com", "My email", encode: "hex"
# # => <a href="mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>
#
- # mail_to "me@domain.com", nil, :replace_at => "_at_", :replace_dot => "_dot_", :class => "email"
+ # mail_to "me@domain.com", nil, replace_at: "_at_", replace_dot: "_dot_", class: "email"
# # => <a href="mailto:me@domain.com" class="email">me_at_domain_dot_com</a>
#
- # mail_to "me@domain.com", "My email", :cc => "ccaddress@domain.com",
- # :subject => "This is an example email"
+ # mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
+ # subject: "This is an example email"
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
def mail_to(email_address, name = nil, html_options = {})
email_address = ERB::Util.html_escape(email_address)
@@ -501,47 +500,47 @@ module ActionView
# ==== Examples
# Let's say we're in the <tt>/shop/checkout?order=desc</tt> action.
#
- # current_page?(:action => 'process')
+ # current_page?(action: 'process')
# # => false
#
- # current_page?(:controller => 'shop', :action => 'checkout')
+ # current_page?(controller: 'shop', action: 'checkout')
# # => true
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'asc')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'asc')
# # => false
#
- # current_page?(:action => 'checkout')
+ # current_page?(action: 'checkout')
# # => true
#
- # current_page?(:controller => 'library', :action => 'checkout')
+ # current_page?(controller: 'library', action: 'checkout')
# # => false
#
# Let's say we're in the <tt>/shop/checkout?order=desc&page=1</tt> action.
#
- # current_page?(:action => 'process')
+ # current_page?(action: 'process')
# # => false
#
- # current_page?(:controller => 'shop', :action => 'checkout')
+ # current_page?(controller: 'shop', action: 'checkout')
# # => true
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '1')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
# # => true
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc', :page => '2')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
# # => false
#
- # current_page?(:controller => 'shop', :action => 'checkout', :order => 'desc')
+ # current_page?(controller: 'shop', action: 'checkout', order: 'desc')
# # => false
#
- # current_page?(:action => 'checkout')
+ # current_page?(action: 'checkout')
# # => true
#
- # current_page?(:controller => 'library', :action => 'checkout')
+ # current_page?(controller: 'library', action: 'checkout')
# # => false
#
# Let's say we're in the <tt>/products</tt> action with method POST in case of invalid product.
#
- # current_page?(:controller => 'product', :action => 'index')
+ # current_page?(controller: 'product', action: 'index')
# # => false
#
def current_page?(options)
@@ -598,7 +597,9 @@ module ActionView
end
def link_to_remote_options?(options)
- options.is_a?(Hash) && options.delete('remote')
+ if options.is_a?(Hash)
+ options.delete('remote') || options.delete(:remote)
+ end
end
def add_method_to_attributes!(html_options, method)
@@ -639,14 +640,14 @@ module ActionView
def token_tag(token=nil)
if token != false && protect_against_forgery?
token ||= form_authenticity_token
- tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => token)
+ tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
else
''
end
end
def method_tag(method)
- tag('input', :type => 'hidden', :name => '_method', :value => method.to_s)
+ tag('input', type: 'hidden', name: '_method', value: method.to_s)
end
end
end
diff --git a/actionpack/lib/action_view/log_subscriber.rb b/actionpack/lib/action_view/log_subscriber.rb
index cc3a871576..fd9a543e0a 100644
--- a/actionpack/lib/action_view/log_subscriber.rb
+++ b/actionpack/lib/action_view/log_subscriber.rb
@@ -3,10 +3,13 @@ module ActionView
#
# Provides functionality so that Rails can output logs from Action View.
class LogSubscriber < ActiveSupport::LogSubscriber
+ VIEWS_PATTERN = /^app\/views\//.freeze
+
def render_template(event)
+ return unless logger.info?
message = " Rendered #{from_rails_root(event.payload[:identifier])}"
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
- message << (" (%.1fms)" % event.duration)
+ message << " (#{event.duration.round(1)}ms)"
info(message)
end
alias :render_partial :render_template
@@ -19,7 +22,7 @@ module ActionView
protected
def from_rails_root(string)
- string.sub("#{Rails.root}/", "").sub(/^app\/views\//, "")
+ string.sub("#{Rails.root}/", "").sub(VIEWS_PATTERN, "")
end
end
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 2d36deaa78..55f6ea5522 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -5,8 +5,6 @@ module ActionView
# = Action View Railtie
class Railtie < Rails::Railtie
config.action_view = ActiveSupport::OrderedOptions.new
- config.action_view.stylesheet_expansions = {}
- config.action_view.javascript_expansions = { :defaults => %w(jquery jquery_ujs) }
config.action_view.embed_authenticity_token_in_remote_forms = false
config.eager_load_namespaces << ActionView
@@ -30,18 +28,6 @@ module ActionView
end
end
- initializer "action_view.javascript_expansions" do |app|
- ActiveSupport.on_load(:action_view) do
- ActionView::Helpers::AssetTagHelper.register_javascript_expansion(
- app.config.action_view.delete(:javascript_expansions)
- )
-
- ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion(
- app.config.action_view.delete(:stylesheet_expansions)
- )
- end
- end
-
initializer "action_view.set_configs" do |app|
ActiveSupport.on_load(:action_view) do
app.config.action_view.each do |k,v|
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index b7fd6ec7e1..5434b3421e 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -196,17 +196,17 @@ module ActionView
:@_result,
:@_routes,
:@controller,
- :@layouts,
+ :@_layouts,
:@locals,
:@method_name,
:@output_buffer,
- :@partials,
+ :@_partials,
:@passed,
:@rendered,
:@request,
:@routes,
:@tagged_logger,
- :@templates,
+ :@_templates,
:@options,
:@test_passed,
:@view,
diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb
index 4f2b052720..4dd7406798 100644
--- a/actionpack/test/active_record_unit.rb
+++ b/actionpack/test/active_record_unit.rb
@@ -11,7 +11,7 @@ class ActiveRecordTestConnector
end
# Try to grab AR
-unless defined?(ActiveRecord) && defined?(Fixtures)
+unless defined?(ActiveRecord) && defined?(FixtureSet)
begin
PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib"
raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR)
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 4ab5d92a2b..ca542eb7e2 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -7,6 +7,7 @@ class ActionPackAssertionsController < ActionController::Base
def nothing() head :ok end
def hello_world() render :template => "test/hello_world"; end
+ def hello_repeating_in_path() render :template => "test/hello/hello"; end
def hello_xml_world() render :template => "test/hello_xml_world"; end
@@ -464,6 +465,20 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_incorrect_string_that_matches
+ get :hello_world
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template 'est/he'
+ end
+ end
+
+ def test_fails_with_repeated_name_in_path
+ get :hello_repeating_in_path
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template 'test/hello'
+ end
+ end
+
def test_fails_with_incorrect_symbol
get :hello_world
assert_raise(ActiveSupport::TestCase::Assertion) do
@@ -471,6 +486,13 @@ class AssertTemplateTest < ActionController::TestCase
end
end
+ def test_fails_with_incorrect_symbol_that_matches
+ get :hello_world
+ assert_raise(ActiveSupport::TestCase::Assertion) do
+ assert_template :"est/he"
+ end
+ end
+
def test_fails_with_wrong_layout
get :render_with_layout
assert_raise(ActiveSupport::TestCase::Assertion) do
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 620479cb0c..65c18dfb64 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -6,744 +6,40 @@ CACHE_DIR = 'test_cache'
# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed
FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)
-class CachingMetalController < ActionController::Metal
+class FragmentCachingMetalTestController < ActionController::Metal
abstract!
include ActionController::Caching
- self.page_cache_directory = FILE_STORE_PATH
- self.cache_store = :file_store, FILE_STORE_PATH
-end
-
-class PageCachingMetalTestController < CachingMetalController
- caches_page :ok
-
- def ok
- self.response_body = 'ok'
- end
+ def some_action; end
end
-class PageCachingMetalTest < ActionController::TestCase
- tests PageCachingMetalTestController
-
+class FragmentCachingMetalTest < ActionController::TestCase
def setup
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- FileUtils.mkdir_p(FILE_STORE_PATH)
- end
-
- def teardown
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
+ super
+ @store = ActiveSupport::Cache::MemoryStore.new
+ @controller = FragmentCachingMetalTestController.new
+ @controller.perform_caching = true
+ @controller.cache_store = @store
+ @params = { controller: 'posts', action: 'index'}
+ @request = ActionController::TestRequest.new
+ @response = ActionController::TestResponse.new
+ @controller.params = @params
+ @controller.request = @request
+ @controller.response = @response
end
- def test_should_cache_get_with_ok_status
- get :ok
- assert_response :ok
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_metal_test/ok.html"), 'get with ok status should have been cached'
+ def test_fragment_cache_key
+ assert_equal 'views/what a key', @controller.fragment_cache_key('what a key')
end
end
-ActionController::Base.page_cache_directory = FILE_STORE_PATH
-
class CachingController < ActionController::Base
abstract!
self.cache_store = :file_store, FILE_STORE_PATH
end
-class PageCachingTestController < CachingController
- self.page_cache_compression = :best_compression
-
- caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? }
- caches_page :found, :not_found
- caches_page :about_me
- caches_page :default_gzip
- caches_page :no_gzip, :gzip => false
- caches_page :gzip_level, :gzip => :best_speed
-
- def ok
- head :ok
- end
-
- def no_content
- head :no_content
- end
-
- def found
- redirect_to :action => 'ok'
- end
-
- def not_found
- head :not_found
- end
-
- def custom_path
- render :text => "Super soaker"
- cache_page("Super soaker", "/index.html")
- end
-
- def default_gzip
- render :text => "Text"
- end
-
- def no_gzip
- render :text => "PNG"
- end
-
- def gzip_level
- render :text => "Big text"
- end
-
- def expire_custom_path
- expire_page("/index.html")
- head :ok
- end
-
- def trailing_slash
- render :text => "Sneak attack"
- end
-
- def about_me
- respond_to do |format|
- format.html {render :text => 'I am html'}
- format.xml {render :text => 'I am xml'}
- end
- end
-
-end
-
-class PageCachingTest < ActionController::TestCase
- def setup
- super
-
- @request = ActionController::TestRequest.new
- @request.host = 'hostname.com'
- @request.env.delete('PATH_INFO')
-
- @controller = PageCachingTestController.new
- @controller.perform_caching = true
- @controller.cache_store = :file_store, FILE_STORE_PATH
-
- @response = ActionController::TestResponse.new
-
- @params = {:controller => 'posts', :action => 'index', :only_path => true}
-
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- FileUtils.mkdir_p(FILE_STORE_PATH)
- end
-
- def teardown
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- @controller.perform_caching = false
- end
-
- def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route
- with_routing do |set|
- set.draw do
- get 'posts.:format', :to => 'posts#index', :as => :formatted_posts
- get '/', :to => 'posts#index', :as => :main
- end
- @params[:format] = 'rss'
- assert_equal '/posts.rss', @routes.url_for(@params)
- @params[:format] = nil
- assert_equal '/', @routes.url_for(@params)
- end
- end
-
- def test_should_cache_get_with_ok_status
- get :ok
- assert_response :ok
- assert_page_cached :ok, "get with ok status should have been cached"
- end
-
- def test_should_cache_with_custom_path
- get :custom_path
- assert File.exist?("#{FILE_STORE_PATH}/index.html")
- end
-
- def test_should_expire_cache_with_custom_path
- get :custom_path
- assert File.exist?("#{FILE_STORE_PATH}/index.html")
-
- get :expire_custom_path
- assert !File.exist?("#{FILE_STORE_PATH}/index.html")
- end
-
- def test_should_gzip_cache
- get :custom_path
- assert File.exist?("#{FILE_STORE_PATH}/index.html.gz")
-
- get :expire_custom_path
- assert !File.exist?("#{FILE_STORE_PATH}/index.html.gz")
- end
-
- def test_should_allow_to_disable_gzip
- get :no_gzip
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/no_gzip.html")
- assert !File.exist?("#{FILE_STORE_PATH}/page_caching_test/no_gzip.html.gz")
- end
-
- def test_should_use_config_gzip_by_default
- @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_COMPRESSION)
- get :default_gzip
- end
-
- def test_should_set_gzip_level
- @controller.expects(:cache_page).with(nil, nil, Zlib::BEST_SPEED)
- get :gzip_level
- end
-
- def test_should_cache_without_trailing_slash_on_url
- @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash'
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
- end
-
- def test_should_obey_http_accept_attribute
- @request.env['HTTP_ACCEPT'] = 'text/xml'
- get :about_me
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/about_me.xml")
- assert_equal 'I am xml', @response.body
- end
-
- def test_cached_page_should_not_have_trailing_slash_even_if_url_has_trailing_slash
- @controller.class.cache_page 'cached content', '/page_caching_test/trailing_slash/'
- assert File.exist?("#{FILE_STORE_PATH}/page_caching_test/trailing_slash.html")
- end
-
- def test_should_cache_ok_at_custom_path
- @request.env['PATH_INFO'] = '/index.html'
- get :ok
- assert_response :ok
- assert File.exist?("#{FILE_STORE_PATH}/index.html")
- end
-
- [:ok, :no_content, :found, :not_found].each do |status|
- [:get, :post, :patch, :put, :delete].each do |method|
- unless method == :get && status == :ok
- define_method "test_shouldnt_cache_#{method}_with_#{status}_status" do
- send(method, status)
- assert_response status
- assert_page_not_cached status, "#{method} with #{status} status shouldn't have been cached"
- end
- end
- end
- end
-
- def test_page_caching_conditional_options
- get :ok, :format=>'json'
- assert_page_not_cached :ok
- end
-
- def test_page_caching_directory_set_as_pathname
- begin
- ActionController::Base.page_cache_directory = Pathname.new(FILE_STORE_PATH)
- get :ok
- assert_response :ok
- assert_page_cached :ok
- ensure
- ActionController::Base.page_cache_directory = FILE_STORE_PATH
- end
- end
-
- private
- def assert_page_cached(action, message = "#{action} should have been cached")
- assert page_cached?(action), message
- end
-
- def assert_page_not_cached(action, message = "#{action} shouldn't have been cached")
- assert !page_cached?(action), message
- end
-
- def page_cached?(action)
- File.exist? "#{FILE_STORE_PATH}/page_caching_test/#{action}.html"
- end
-end
-
-class ActionCachingTestController < CachingController
- rescue_from(Exception) { head 500 }
- rescue_from(ActionController::UnknownFormat) { head :not_acceptable }
- if defined? ActiveRecord
- rescue_from(ActiveRecord::RecordNotFound) { head :not_found }
- end
-
- # Eliminate uninitialized ivar warning
- before_filter { @title = nil }
-
- caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| c.request.format && !c.request.format.json? }, :expires_in => 1.hour
- caches_action :show, :cache_path => 'http://test.host/custom/show'
- caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }
- caches_action :with_layout
- caches_action :with_format_and_http_param, :cache_path => Proc.new { |c| { :key => 'value' } }
- caches_action :layout_false, :layout => false
- caches_action :with_layout_proc_param, :layout => Proc.new { |c| c.params[:layout] }
- caches_action :record_not_found, :four_oh_four, :simple_runtime_error
- caches_action :streaming
- caches_action :invalid
-
- layout 'talk_from_action'
-
- def index
- @cache_this = MockTime.now.to_f.to_s
- render :text => @cache_this
- end
-
- def redirected
- redirect_to :action => 'index'
- end
-
- def forbidden
- render :text => "Forbidden"
- response.status = "403 Forbidden"
- end
-
- def with_layout
- @cache_this = MockTime.now.to_f.to_s
- @title = nil
- render :text => @cache_this, :layout => true
- end
-
- def with_format_and_http_param
- @cache_this = MockTime.now.to_f.to_s
- render :text => @cache_this
- end
-
- def record_not_found
- raise ActiveRecord::RecordNotFound, "oops!"
- end
-
- def four_oh_four
- render :text => "404'd!", :status => 404
- end
-
- def simple_runtime_error
- raise "oops!"
- end
-
- alias_method :show, :index
- alias_method :edit, :index
- alias_method :destroy, :index
- alias_method :layout_false, :with_layout
- alias_method :with_layout_proc_param, :with_layout
-
- def expire
- expire_action :controller => 'action_caching_test', :action => 'index'
- render :nothing => true
- end
-
- def expire_xml
- expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'
- render :nothing => true
- end
-
- def expire_with_url_string
- expire_action url_for(:controller => 'action_caching_test', :action => 'index')
- render :nothing => true
- end
-
- def streaming
- render :text => "streaming", :stream => true
- end
-
- def invalid
- @cache_this = MockTime.now.to_f.to_s
-
- respond_to do |format|
- format.json{ render :json => @cache_this }
- end
- end
-end
-
-class MockTime < Time
- # Let Time spicy to assure that Time.now != Time.now
- def to_f
- super+rand
- end
-end
-
-class ActionCachingMockController
- attr_accessor :mock_url_for
- attr_accessor :mock_path
-
- def initialize
- yield self if block_given?
- end
-
- def url_for(*args)
- @mock_url_for
- end
-
- def params
- request.parameters
- end
-
- def request
- Object.new.instance_eval(<<-EVAL)
- def path; '#{@mock_path}' end
- def format; 'all' end
- def parameters; {:format => nil}; end
- self
- EVAL
- end
-end
-
-class ActionCacheTest < ActionController::TestCase
- tests ActionCachingTestController
-
- def setup
- super
- @request.host = 'hostname.com'
- FileUtils.mkdir_p(FILE_STORE_PATH)
- @path_class = ActionController::Caching::Actions::ActionCachePath
- @mock_controller = ActionCachingMockController.new
- end
-
- def teardown
- super
- FileUtils.rm_rf(File.dirname(FILE_STORE_PATH))
- end
-
- def test_simple_action_cache
- get :index
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test')
-
- get :index
- assert_response :success
- assert_equal cached_time, @response.body
- end
-
- def test_simple_action_not_cached
- get :destroy
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert !fragment_exist?('hostname.com/action_caching_test/destroy')
-
- get :destroy
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
-
- include RackTestUtils
-
- def test_action_cache_with_layout
- get :with_layout
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/with_layout')
-
- get :with_layout
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout'))
- assert_equal @response.body, body
- end
-
- def test_action_cache_with_layout_and_layout_cache_false
- get :layout_false
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/layout_false')
-
- get :layout_false
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/layout_false'))
- assert_equal cached_time, body
- end
-
- def test_action_cache_with_layout_and_layout_cache_false_via_proc
- get :with_layout_proc_param, :layout => false
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param')
-
- get :with_layout_proc_param, :layout => false
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout_proc_param'))
- assert_equal cached_time, body
- end
-
- def test_action_cache_with_layout_and_layout_cache_true_via_proc
- get :with_layout_proc_param, :layout => true
- assert_response :success
- cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/with_layout_proc_param')
-
- get :with_layout_proc_param, :layout => true
- assert_response :success
- assert_not_equal cached_time, @response.body
- body = body_to_string(read_fragment('hostname.com/action_caching_test/with_layout_proc_param'))
- assert_equal @response.body, body
- end
-
- def test_action_cache_conditional_options
- @request.env['HTTP_ACCEPT'] = 'application/json'
- get :index
- assert_response :success
- assert !fragment_exist?('hostname.com/action_caching_test')
- end
-
- def test_action_cache_with_format_and_http_param
- get :with_format_and_http_param, :format => 'json'
- assert_response :success
- assert !fragment_exist?('hostname.com/action_caching_test/with_format_and_http_param.json?key=value.json')
- assert fragment_exist?('hostname.com/action_caching_test/with_format_and_http_param.json?key=value')
- end
-
- def test_action_cache_with_store_options
- MockTime.expects(:now).returns(12345).once
- @controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once
- @controller.expects(:write_fragment).with('hostname.com/action_caching_test', '12345.0', :expires_in => 1.hour).once
- get :index
- assert_response :success
- end
-
- def test_action_cache_with_custom_cache_path
- get :show
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert fragment_exist?('test.host/custom/show')
-
- get :show
- assert_response :success
- assert_equal cached_time, @response.body
- end
-
- def test_action_cache_with_custom_cache_path_in_block
- get :edit
- assert_response :success
- assert fragment_exist?('test.host/edit')
-
- get :edit, :id => 1
- assert_response :success
- assert fragment_exist?('test.host/1;edit')
- end
-
- def test_cache_expiration
- get :index
- assert_response :success
- cached_time = content_to_cache
-
- get :index
- assert_response :success
- assert_equal cached_time, @response.body
-
- get :expire
- assert_response :success
-
- get :index
- assert_response :success
- new_cached_time = content_to_cache
- assert_not_equal cached_time, @response.body
-
- get :index
- assert_response :success
- assert_equal new_cached_time, @response.body
- end
-
- def test_cache_expiration_isnt_affected_by_request_format
- get :index
- cached_time = content_to_cache
-
- @request.request_uri = "/action_caching_test/expire.xml"
- get :expire, :format => :xml
- assert_response :success
-
- get :index
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
-
- def test_cache_expiration_with_url_string
- get :index
- cached_time = content_to_cache
-
- @request.request_uri = "/action_caching_test/expire_with_url_string"
- get :expire_with_url_string
- assert_response :success
-
- get :index
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
-
- def test_cache_is_scoped_by_subdomain
- @request.host = 'jamis.hostname.com'
- get :index
- assert_response :success
- jamis_cache = content_to_cache
-
- @request.host = 'david.hostname.com'
- get :index
- assert_response :success
- david_cache = content_to_cache
- assert_not_equal jamis_cache, @response.body
-
- @request.host = 'jamis.hostname.com'
- get :index
- assert_response :success
- assert_equal jamis_cache, @response.body
-
- @request.host = 'david.hostname.com'
- get :index
- assert_response :success
- assert_equal david_cache, @response.body
- end
-
- def test_redirect_is_not_cached
- get :redirected
- assert_response :redirect
- get :redirected
- assert_response :redirect
- end
-
- def test_forbidden_is_not_cached
- get :forbidden
- assert_response :forbidden
- get :forbidden
- assert_response :forbidden
- end
-
- def test_xml_version_of_resource_is_treated_as_different_cache
- with_routing do |set|
- set.draw do
- get ':controller(/:action(.:format))'
- end
-
- get :index, :format => 'xml'
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
- assert fragment_exist?('hostname.com/action_caching_test/index.xml')
-
- get :index, :format => 'xml'
- assert_response :success
- assert_equal cached_time, @response.body
- assert_equal 'application/xml', @response.content_type
-
- get :expire_xml
- assert_response :success
-
- get :index, :format => 'xml'
- assert_response :success
- assert_not_equal cached_time, @response.body
- end
- end
-
- def test_correct_content_type_is_returned_for_cache_hit
- # run it twice to cache it the first time
- get :index, :id => 'content-type', :format => 'xml'
- get :index, :id => 'content-type', :format => 'xml'
- assert_response :success
- assert_equal 'application/xml', @response.content_type
- end
-
- def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key
- # run it twice to cache it the first time
- get :show, :format => 'xml'
- get :show, :format => 'xml'
- assert_response :success
- assert_equal 'application/xml', @response.content_type
- end
-
- def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key_from_proc
- # run it twice to cache it the first time
- get :edit, :id => 1, :format => 'xml'
- get :edit, :id => 1, :format => 'xml'
- assert_response :success
- assert_equal 'application/xml', @response.content_type
- end
-
- def test_empty_path_is_normalized
- @mock_controller.mock_url_for = 'http://example.org/'
- @mock_controller.mock_path = '/'
-
- assert_equal 'example.org/index', @path_class.new(@mock_controller, {}).path
- end
-
- def test_file_extensions
- get :index, :id => 'kitten.jpg'
- get :index, :id => 'kitten.jpg'
-
- assert_response :success
- end
-
- if defined? ActiveRecord
- def test_record_not_found_returns_404_for_multiple_requests
- get :record_not_found
- assert_response 404
- get :record_not_found
- assert_response 404
- end
- end
-
- def test_four_oh_four_returns_404_for_multiple_requests
- get :four_oh_four
- assert_response 404
- get :four_oh_four
- assert_response 404
- end
-
- def test_four_oh_four_renders_content
- get :four_oh_four
- assert_equal "404'd!", @response.body
- end
-
- def test_simple_runtime_error_returns_500_for_multiple_requests
- get :simple_runtime_error
- assert_response 500
- get :simple_runtime_error
- assert_response 500
- end
-
- def test_action_caching_plus_streaming
- get :streaming
- assert_response :success
- assert_match(/streaming/, @response.body)
- assert fragment_exist?('hostname.com/action_caching_test/streaming')
- end
-
- def test_invalid_format_returns_not_acceptable
- get :invalid, :format => "json"
- assert_response :success
- cached_time = content_to_cache
- assert_equal cached_time, @response.body
-
- assert fragment_exist?("hostname.com/action_caching_test/invalid.json")
-
- get :invalid, :format => "json"
- assert_response :success
- assert_equal cached_time, @response.body
-
- get :invalid, :format => "xml"
- assert_response :not_acceptable
-
- get :invalid, :format => "\xC3\x83"
- assert_response :not_acceptable
- end
-
- private
- def content_to_cache
- assigns(:cache_this)
- end
-
- def fragment_exist?(path)
- @controller.fragment_exist?(path)
- end
-
- def read_fragment(path)
- @controller.read_fragment(path)
- end
-end
-
class FragmentCachingTestController < CachingController
def some_action; end;
end
@@ -988,3 +284,17 @@ class CacheHelperOutputBufferTest < ActionController::TestCase
end
end
+class DeprecatedPageCacheExtensionTest < ActiveSupport::TestCase
+ def test_page_cache_extension_binds_default_static_extension
+ deprecation_behavior = ActiveSupport::Deprecation.behavior
+ ActiveSupport::Deprecation.behavior = :silence
+ old_extension = ActionController::Base.default_static_extension
+
+ ActionController::Base.page_cache_extension = '.rss'
+
+ assert_equal '.rss', ActionController::Base.default_static_extension
+ ensure
+ ActiveSupport::Deprecation.behavior = deprecation_behavior
+ ActionController::Base.default_static_extension = old_extension
+ end
+end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index a72b6dde1a..9efb6ab95f 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -42,11 +42,6 @@ module Another
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
- def with_page_cache
- cache_page("Super soaker", "/index.html")
- render :nothing => true
- end
-
def with_exception
raise Exception
end
@@ -71,7 +66,6 @@ class ACLogSubscriberTest < ActionController::TestCase
@old_logger = ActionController::Base.logger
@cache_path = File.expand_path('../temp/test_cache', File.dirname(__FILE__))
- ActionController::Base.page_cache_directory = @cache_path
@controller.cache_store = :file_store, @cache_path
ActionController::LogSubscriber.attach_to :action_controller
end
@@ -199,18 +193,6 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
- def test_with_page_cache
- @controller.config.perform_caching = true
- get :with_page_cache
- wait
-
- assert_equal 3, logs.size
- assert_match(/Write page/, logs[1])
- assert_match(/\/index\.html/, logs[1])
- ensure
- @controller.config.perform_caching = true
- end
-
def test_process_action_with_exception_includes_http_status_code
begin
get :with_exception
diff --git a/actionpack/test/controller/parameters/multi_parameter_attributes_test.rb b/actionpack/test/controller/parameters/multi_parameter_attributes_test.rb
new file mode 100644
index 0000000000..2214ec769c
--- /dev/null
+++ b/actionpack/test/controller/parameters/multi_parameter_attributes_test.rb
@@ -0,0 +1,33 @@
+require 'abstract_unit'
+require 'action_controller/metal/strong_parameters'
+
+class MultiParameterAttributesTest < ActiveSupport::TestCase
+ test "permitted multi-parameter attribute keys" do
+ params = ActionController::Parameters.new({
+ book: {
+ "shipped_at(1i)" => "2012",
+ "shipped_at(2i)" => "3",
+ "shipped_at(3i)" => "25",
+ "shipped_at(4i)" => "10",
+ "shipped_at(5i)" => "15",
+ "published_at(1i)" => "1999",
+ "published_at(2i)" => "2",
+ "published_at(3i)" => "5"
+ }
+ })
+
+ permitted = params.permit book: [ :shipped_at ]
+
+ assert permitted.permitted?
+
+ assert_equal "2012", permitted[:book]["shipped_at(1i)"]
+ assert_equal "3", permitted[:book]["shipped_at(2i)"]
+ assert_equal "25", permitted[:book]["shipped_at(3i)"]
+ assert_equal "10", permitted[:book]["shipped_at(4i)"]
+ assert_equal "15", permitted[:book]["shipped_at(5i)"]
+
+ assert_nil permitted[:book]["published_at(1i)"]
+ assert_nil permitted[:book]["published_at(2i)"]
+ assert_nil permitted[:book]["published_at(3i)"]
+ end
+end
diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb
index 7fe8e6051b..18bb51c5a3 100644
--- a/actionpack/test/controller/parameters/parameters_permit_test.rb
+++ b/actionpack/test/controller/parameters/parameters_permit_test.rb
@@ -57,6 +57,13 @@ class ParametersPermitTest < ActiveSupport::TestCase
assert_equal @params.permitted?, @params.dup.permitted?
end
+ test "permit is recursive" do
+ @params.permit!
+ assert @params.permitted?
+ assert @params[:person].permitted?
+ assert @params[:person][:name].permitted?
+ end
+
test "permitted takes a default value when Parameters.permit_all_parameters is set" do
begin
ActionController::Parameters.permit_all_parameters = true
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index fd8f87e377..aa33f01d02 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -1428,6 +1428,17 @@ class RenderTest < ActionController::TestCase
assert_equal "Bonjour: davidBonjour: mary", @response.body
end
+ def test_locals_option_to_assert_template_is_not_supported
+ warning_buffer = StringIO.new
+ $stderr = warning_buffer
+
+ get :partial_collection_with_locals
+ assert_template partial: 'customer_greeting', locals: { greeting: 'Bonjour' }
+ assert_equal "the :locals option to #assert_template is only supported in a ActionView::TestCase\n", warning_buffer.string
+ ensure
+ $stderr = STDERR
+ end
+
def test_partial_collection_with_spacer
get :partial_collection_with_spacer
assert_equal "Hello: davidonly partialHello: mary", @response.body
diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb
index 351b9c4cfa..ab1bd0e3b6 100644
--- a/actionpack/test/controller/show_exceptions_test.rb
+++ b/actionpack/test/controller/show_exceptions_test.rb
@@ -93,4 +93,20 @@ module ShowExceptions
assert_equal 'text/html', response.content_type.to_s
end
end
+
+ class ShowFailsafeExceptionsTest < ActionDispatch::IntegrationTest
+ def test_render_failsafe_exception
+ @app = ShowExceptionsOverridenController.action(:boom)
+ @exceptions_app = @app.instance_variable_get(:@exceptions_app)
+ @app.instance_variable_set(:@exceptions_app, nil)
+ $stderr = StringIO.new
+
+ get '/', {}, 'HTTP_ACCEPT' => 'text/json'
+ assert_response :internal_server_error
+ assert_equal 'text/plain', response.content_type.to_s
+
+ @app.instance_variable_set(:@exceptions_app, @exceptions_app)
+ $stderr = STDERR
+ end
+ end
end
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 4e83ad16d7..93d89f7568 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -1124,6 +1124,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal '/sheep/1/_it', _it_sheep_path(1)
end
+ def test_resource_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resource :user, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
+ def test_resources_does_not_modify_passed_options
+ options = {:id => /.+?/, :format => /json|xml/}
+ self.class.stub_controllers do |routes|
+ routes.draw do
+ resources :users, options
+ end
+ end
+ assert_equal({:id => /.+?/, :format => /json|xml/}, options)
+ end
+
def test_path_names
get '/pt/projetos'
assert_equal 'projects#index', @response.body
diff --git a/actionpack/test/dispatch/session/cache_store_test.rb b/actionpack/test/dispatch/session/cache_store_test.rb
index a74e165826..b8479e8836 100644
--- a/actionpack/test/dispatch/session/cache_store_test.rb
+++ b/actionpack/test/dispatch/session/cache_store_test.rb
@@ -1,4 +1,5 @@
require 'abstract_unit'
+require 'fixtures/session_autoload_test/session_autoload_test/foo'
class CacheStoreTest < ActionDispatch::IntegrationTest
class TestController < ActionController::Base
diff --git a/actionpack/test/fixtures/test/hello/hello.erb b/actionpack/test/fixtures/test/hello/hello.erb
new file mode 100644
index 0000000000..6769dd60bd
--- /dev/null
+++ b/actionpack/test/fixtures/test/hello/hello.erb
@@ -0,0 +1 @@
+Hello world! \ No newline at end of file
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index a04694714d..754622c883 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -45,8 +45,6 @@ class AssetTagHelperTest < ActionView::TestCase
end.new
@controller.request = @request
-
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails']
end
def url_for(*args)
@@ -54,11 +52,7 @@ class AssetTagHelperTest < ActionView::TestCase
end
def teardown
- config.perform_caching = false
ENV.delete('RAILS_ASSET_ID')
-
- JavascriptIncludeTag.expansions.clear
- StylesheetIncludeTag.expansions.clear
end
AutoDiscoveryToTag = {
@@ -105,12 +99,6 @@ class AssetTagHelperTest < ActionView::TestCase
%(javascript_include_tag("bank.js")) => %(<script src="/javascripts/bank.js" ></script>),
%(javascript_include_tag("bank", :lang => "vbscript")) => %(<script lang="vbscript" src="/javascripts/bank.js" ></script>),
%(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" ></script>\n<script src="/elsewhere/cools.js" ></script>),
- %(javascript_include_tag(:defaults)) => %(<script src="/javascripts/prototype.js" ></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:all)) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:all, :recursive => true)) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/subdir/subdir.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:defaults, "bank")) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag(:defaults, "application")) => %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>),
- %(javascript_include_tag("bank", :defaults)) => %(<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>),
%(javascript_include_tag("http://example.com/all")) => %(<script src="http://example.com/all"></script>),
%(javascript_include_tag("http://example.com/all.js")) => %(<script src="http://example.com/all.js"></script>),
@@ -151,9 +139,6 @@ class AssetTagHelperTest < ActionView::TestCase
%(stylesheet_link_tag("/elsewhere/file")) => %(<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("subdir/subdir")) => %(<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("bank", :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" />),
- %(stylesheet_link_tag(:all)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- %(stylesheet_link_tag(:all, :recursive => true)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- %(stylesheet_link_tag(:all, :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="all" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="all" rel="stylesheet" />),
%(stylesheet_link_tag("random.styles", "/elsewhere/file")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />\n<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
%(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style" media="screen" rel="stylesheet" />),
@@ -339,93 +324,14 @@ class AssetTagHelperTest < ActionView::TestCase
}
assert_nothing_raised {
- javascript_include_tag(:defaults, 'missing_security_guard')
- }
-
- assert_nothing_raised {
javascript_include_tag('http://example.com/css/missing_security_guard')
}
end
- def test_javascript_include_tag_with_given_asset_id
- ENV["RAILS_ASSET_ID"] = "1"
- assert_dom_equal(%(<script src="/javascripts/prototype.js?1"></script>\n<script src="/javascripts/effects.js?1"></script>\n<script src="/javascripts/dragdrop.js?1"></script>\n<script src="/javascripts/controls.js?1"></script>\n<script src="/javascripts/rails.js?1"></script>\n<script src="/javascripts/application.js?1"></script>), javascript_include_tag(:defaults))
- end
-
def test_javascript_include_tag_is_html_safe
- assert javascript_include_tag(:defaults).html_safe?
assert javascript_include_tag("prototype").html_safe?
end
- def test_custom_javascript_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/effects.js"></script>), javascript_include_tag('controls', :robbery, 'effects')
- end
-
- def test_custom_javascript_expansions_return_unique_set
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :defaults => %w(prototype effects dragdrop controls rails application)
- assert_dom_equal %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag(:defaults)
- end
-
- def test_custom_javascript_expansions_and_defaults_puts_application_js_at_the_end
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('controls',:defaults, :robbery, 'effects')
- end
-
- def test_javascript_include_tag_should_not_output_the_same_asset_twice
- ENV["RAILS_ASSET_ID"] = ""
- assert_dom_equal %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('prototype', 'effects', :defaults)
- end
-
- def test_javascript_include_tag_should_not_output_the_same_expansion_twice
- ENV["RAILS_ASSET_ID"] = ""
- assert_dom_equal %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag(:defaults, :defaults)
- end
-
- def test_single_javascript_asset_keys_should_take_precedence_over_expansions
- ENV["RAILS_ASSET_ID"] = ""
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('controls', :defaults, 'effects')
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/rails.js"></script>\n<script src="/javascripts/application.js"></script>), javascript_include_tag('controls', 'effects', :defaults)
- end
-
- def test_registering_javascript_expansions_merges_with_existing_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :can_merge => ['bank']
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :can_merge => ['robber']
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :can_merge => ['bank']
- assert_dom_equal %(<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>), javascript_include_tag(:can_merge)
- end
-
- def test_custom_javascript_expansions_with_undefined_symbol
- assert_raise(ArgumentError) { javascript_include_tag('first', :unknown, 'last') }
- end
-
- def test_custom_javascript_expansions_with_nil_value
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => nil
- assert_dom_equal %(<script src="/javascripts/first.js"></script>\n<script src="/javascripts/last.js"></script>), javascript_include_tag('first', :monkey, 'last')
- end
-
- def test_custom_javascript_expansions_with_empty_array_value
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :monkey => []
- assert_dom_equal %(<script src="/javascripts/first.js"></script>\n<script src="/javascripts/last.js"></script>), javascript_include_tag('first', :monkey, 'last')
- end
-
- def test_custom_javascript_and_stylesheet_expansion_with_same_name
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_javascript_expansion :robbery => ["bank", "robber"]
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :robbery => ["money", "security"]
- assert_dom_equal %(<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/effects.js"></script>), javascript_include_tag('controls', :robbery, 'effects')
- assert_dom_equal %(<link href="/stylesheets/style.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/money.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/security.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/print.css" rel="stylesheet" media="screen" />), stylesheet_link_tag('style', :robbery, 'print')
- end
-
- def test_reset_javascript_expansions
- JavascriptIncludeTag.expansions.clear
- assert_raise(ArgumentError) { javascript_include_tag(:defaults) }
- end
-
def test_all_javascript_expansion_not_include_application_js_if_not_exists
FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'),
File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'))
@@ -478,57 +384,11 @@ class AssetTagHelperTest < ActionView::TestCase
assert_dom_equal %(<link href="/file.css" media="&lt;script&gt;" rel="stylesheet" />), stylesheet_link_tag('/file', :media => '<script>')
end
- def test_custom_stylesheet_expansions
- ENV["RAILS_ASSET_ID"] = ''
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :robbery => ["bank", "robber"]
- assert_dom_equal %(<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('version.1.0', :robbery, 'subdir/subdir')
- end
-
- def test_custom_stylesheet_expansions_return_unique_set
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :cities => %w(wellington amsterdam london)
- assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/london.css" media="screen" rel="stylesheet" />), stylesheet_link_tag(:cities)
- end
-
def test_stylesheet_link_tag_should_not_output_the_same_asset_twice
ENV["RAILS_ASSET_ID"] = ""
assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', 'wellington', 'amsterdam')
end
- def test_stylesheet_link_tag_should_not_output_the_same_expansion_twice
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :cities => %w(wellington amsterdam london)
- assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/london.css" media="screen" rel="stylesheet" />), stylesheet_link_tag(:cities, :cities)
- end
-
- def test_single_stylesheet_asset_keys_should_take_precedence_over_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :cities => %w(wellington amsterdam london)
- assert_dom_equal %(<link href="/stylesheets/london.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('london', :cities)
- end
-
- def test_custom_stylesheet_expansions_with_unknown_symbol
- assert_raise(ArgumentError) { stylesheet_link_tag('first', :unknown, 'last') }
- end
-
- def test_custom_stylesheet_expansions_with_nil_value
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => nil
- assert_dom_equal %(<link href="/stylesheets/first.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/last.css" rel="stylesheet" media="screen" />), stylesheet_link_tag('first', :monkey, 'last')
- end
-
- def test_custom_stylesheet_expansions_with_empty_array_value
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :monkey => []
- assert_dom_equal %(<link href="/stylesheets/first.css" rel="stylesheet" media="screen" />\n<link href="/stylesheets/last.css" rel="stylesheet" media="screen" />), stylesheet_link_tag('first', :monkey, 'last')
- end
-
- def test_registering_stylesheet_expansions_merges_with_existing_expansions
- ENV["RAILS_ASSET_ID"] = ""
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :can_merge => ['bank']
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :can_merge => ['robber']
- ActionView::Helpers::AssetTagHelper::register_stylesheet_expansion :can_merge => ['bank']
- assert_dom_equal %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />), stylesheet_link_tag(:can_merge)
- end
-
def test_image_path
ImagePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -714,616 +574,6 @@ class AssetTagHelperTest < ActionView::TestCase
@controller.request.stubs(:ssl?).returns(true)
assert_equal "http://localhost/images/xml.png", image_path("xml.png")
end
-
- def test_caching_javascript_include_tag_when_caching_on
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/all.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/money.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/absolute/test.js"></script>),
- javascript_include_tag(:all, :cache => "/absolute/test")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_with_proc_asset_host
- ENV['RAILS_ASSET_ID'] = ''
- @controller.config.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
- config.perform_caching = true
-
- assert_equal '/javascripts/scripts.js'.length, 23
- assert_dom_equal(
- %(<script src="http://a23.example.com/javascripts/scripts.js"></script>),
- javascript_include_tag(:all, :cache => 'scripts')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'scripts.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'scripts.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_with_2_argument_proc_asset_host
- ENV['RAILS_ASSET_ID'] = ''
- @controller.config.asset_host = Proc.new { |source, request|
- if request.ssl?
- "#{request.protocol}#{request.host_with_port}"
- else
- "#{request.protocol}assets#{source.length}.example.com"
- end
- }
- config.perform_caching = true
-
- assert_equal '/javascripts/vanilla.js'.length, 23
- assert_dom_equal(
- %(<script src="http://assets23.example.com/javascripts/vanilla.js"></script>),
- javascript_include_tag(:all, :cache => 'vanilla')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
-
- class << @controller.request
- def protocol() 'https://' end
- def ssl?() true end
- end
-
- assert_equal '/javascripts/secure.js'.length, 22
- assert_dom_equal(
- %(<script src="https://localhost/javascripts/secure.js"></script>),
- javascript_include_tag(:all, :cache => 'secure')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_with_2_argument_object_asset_host
- ENV['RAILS_ASSET_ID'] = ''
- @controller.config.asset_host = Class.new do
- def call(source, request)
- if request.ssl?
- "#{request.protocol}#{request.host_with_port}"
- else
- "#{request.protocol}assets#{source.length}.example.com"
- end
- end
- end.new
-
- config.perform_caching = true
-
- assert_equal '/javascripts/vanilla.js'.length, 23
- assert_dom_equal(
- %(<script src="http://assets23.example.com/javascripts/vanilla.js"></script>),
- javascript_include_tag(:all, :cache => 'vanilla')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
-
- class << @controller.request
- def protocol() 'https://' end
- def ssl?() true end
- end
-
- assert_equal '/javascripts/secure.js'.length, 22
- assert_dom_equal(
- %(<script src="https://localhost/javascripts/secure.js"></script>),
- javascript_include_tag(:all, :cache => 'secure')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'vanilla.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'secure.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_and_using_subdirectory
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a%d.example.com'
- config.perform_caching = true
-
- number = Zlib.crc32('/javascripts/cache/money.js') % 4
- assert_dom_equal(
- %(<script src="http://a#{number}.example.com/javascripts/cache/money.js"></script>),
- javascript_include_tag(:all, :cache => "cache/money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))
- end
-
- def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/combined.js"></script>),
- javascript_include_tag(:all, :cache => "combined", :recursive => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
-
- assert_equal(
- %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// bank js\n\n// robber js\n\n// subdir js\n\n\n// version.1.0 js\n\n// application js),
- IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- )
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- end
-
- def test_caching_javascript_include_tag_with_all_puts_defaults_at_the_start_of_the_file
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'http://a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="http://a0.example.com/javascripts/combined.js"></script>),
- javascript_include_tag(:all, :cache => "combined")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
-
- assert_equal(
- %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// bank js\n\n// robber js\n\n// version.1.0 js\n\n// application js),
- IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- )
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js'))
- end
-
- def test_caching_javascript_include_tag_with_relative_url_root
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/all.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/money.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- # Same as above, but with script_name
- def test_caching_javascript_include_tag_with_script_name
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/all.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/money.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_with_named_paths_and_relative_url_root_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- # Same as above, but with script_name
- def test_caching_javascript_include_tag_with_named_paths_and_script_name_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/collaboration/hieraki/javascripts/robber.js"></script>),
- javascript_include_tag('robber', :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => true)
- )
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/subdir/subdir.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => true, :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => "money")
- )
-
- assert_dom_equal(
- %(<script src="/javascripts/prototype.js"></script>\n<script src="/javascripts/effects.js"></script>\n<script src="/javascripts/dragdrop.js"></script>\n<script src="/javascripts/controls.js"></script>\n<script src="/javascripts/bank.js"></script>\n<script src="/javascripts/robber.js"></script>\n<script src="/javascripts/subdir/subdir.js"></script>\n<script src="/javascripts/version.1.0.js"></script>\n<script src="/javascripts/application.js"></script>),
- javascript_include_tag(:all, :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_and_missing_javascript_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = true
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_on_and_javascript_file_is_uri
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = true
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.js', :cache => true)
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
- end
-
- def test_caching_javascript_include_tag_when_caching_off_and_missing_javascript_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
-
- assert_raise(Errno::ENOENT) {
- javascript_include_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_on
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = 'a0.example.com'
- config.perform_caching = true
-
- assert_dom_equal(
- %(<link href="http://a0.example.com/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
-
- expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
- assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- bytes_added_by_join = "\n\n".size * files_to_be_joined.size - "\n\n".size
- expected_size = files_to_be_joined.sum { |p| File.size(p) } + bytes_added_by_join
- assert_equal expected_size, File.size(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="http://a0.example.com/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- assert_dom_equal(
- %(<link href="http://a0.example.com/absolute/test.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "/absolute/test")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute'))
- end
-
- def test_concat_stylesheet_link_tag_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
-
- assert_dom_equal(
- %(<link href="/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :concat => true)
- )
-
- expected = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/*.css"].map { |p| File.mtime(p) }.max
- assert_equal expected, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :concat => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- assert_dom_equal(
- %(<link href="/absolute/test.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :concat => "/absolute/test")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute', 'test.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::ASSETS_DIR, 'absolute'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_on_and_missing_css_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = true
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_off_and_missing_css_file
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => true)
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_raise(Errno::ENOENT) {
- stylesheet_link_tag('bank', 'robber', 'missing_security_guard', :cache => "money")
- }
-
- assert ! File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.asset_host = Proc.new { |source| "a#{source.length}.example.com" }
- config.perform_caching = true
-
- assert_equal '/stylesheets/styles.css'.length, 23
- assert_dom_equal(
- %(<link href="http://a23.example.com/stylesheets/styles.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => 'styles')
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'styles.css'))
-
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'styles.css'))
- end
-
- def test_caching_stylesheet_link_tag_with_relative_url_root
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
-
- expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
- assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- # Same as above, but with script_name
- def test_caching_stylesheet_link_tag_with_script_name
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = true
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"]
-
- expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max
- assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- ensure
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
-
- def test_caching_stylesheet_link_tag_with_named_paths_and_relative_url_root_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @controller.config.relative_url_root = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => "money")
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- # Same as above, but with script_name
- def test_caching_stylesheet_link_tag_with_named_paths_and_script_name_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- @request.script_name = "/collaboration/hieraki"
- config.perform_caching = false
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag('robber', :cache => "money")
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_include_tag_when_caching_off
- ENV["RAILS_ASSET_ID"] = ""
- config.perform_caching = false
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true)
- )
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => true, :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money")
- )
-
- assert_dom_equal(
- %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag(:all, :cache => "money", :recursive => true)
- )
-
- assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
- end
-
- def test_caching_stylesheet_include_tag_with_absolute_uri
- ENV["RAILS_ASSET_ID"] = ""
-
- assert_dom_equal(
- %(<link href="/stylesheets/all.css" media="screen" rel="stylesheet" />),
- stylesheet_link_tag("/foo/baz", :cache => true)
- )
-
- FileUtils.rm(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
- end
end
class AssetTagHelperNonVhostTest < ActionView::TestCase
@@ -1336,8 +586,6 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
@request = Struct.new(:protocol).new("gopher://")
@controller.request = @request
-
- JavascriptIncludeTag.expansions.clear
end
def url_for(options)
diff --git a/actionpack/test/template/html-scanner/sanitizer_test.rb b/actionpack/test/template/html-scanner/sanitizer_test.rb
index 324caef224..d9b57776c9 100644
--- a/actionpack/test/template/html-scanner/sanitizer_test.rb
+++ b/actionpack/test/template/html-scanner/sanitizer_test.rb
@@ -233,7 +233,7 @@ class SanitizerTest < ActionController::TestCase
end
def test_should_sanitize_attributes
- assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="'&gt;&lt;script&gt;alert()&lt;/script&gt;">blah</span>)
+ assert_sanitized %(<SPAN title="'><script>alert()</script>">blah</SPAN>), %(<span title="#{CGI.escapeHTML "'><script>alert()</script>"}">blah</span>)
end
def test_should_sanitize_illegal_style_properties
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 134177d74d..bbfdf7f944 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -31,17 +31,17 @@ class UrlHelperTest < ActiveSupport::TestCase
setup :_prepare_context
def hash_for(options = {})
- { :controller => "foo", :action => "bar" }.merge!(options)
+ { controller: "foo", action: "bar" }.merge!(options)
end
alias url_hash hash_for
def test_url_for_does_not_escape_urls
- assert_equal "/?a=b&c=d", url_for(hash_for(:a => :b, :c => :d))
+ assert_equal "/?a=b&c=d", url_for(hash_for(a: :b, c: :d))
end
def test_url_for_with_back
referer = 'http://www.example.com/referer'
- @controller = Struct.new(:request).new(Struct.new(:env).new({"HTTP_REFERER" => referer}))
+ @controller = Struct.new(:request).new(Struct.new(:env).new("HTTP_REFERER" => referer))
assert_equal 'http://www.example.com/referer', url_for(:back)
end
@@ -53,7 +53,7 @@ class UrlHelperTest < ActiveSupport::TestCase
# TODO: missing test cases
def test_button_to_with_straight_url
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com")
+ assert_dom_equal %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com")
end
def test_button_to_with_straight_url_and_request_forgery
@@ -68,143 +68,146 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_button_to_with_form_class
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :form_class => 'custom-class')
+ assert_dom_equal %{<form method="post" action="http://www.example.com" class="custom-class"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", form_class: 'custom-class')
end
def test_button_to_with_form_class_escapes
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"&lt;script&gt;evil_js&lt;/script&gt;\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :form_class => '<script>evil_js</script>')
+ assert_dom_equal %{<form method="post" action="http://www.example.com" class="&lt;script&gt;evil_js&lt;/script&gt;"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com", form_class: '<script>evil_js</script>')
end
def test_button_to_with_query
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
+ assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&amp;q2=v2" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com/q1=v1&q2=v2")
end
def test_button_to_with_html_safe_URL
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com/q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2".html_safe)
+ assert_dom_equal %{<form method="post" action="http://www.example.com/q1=v1&amp;q2=v2" class="button_to"><div><input type="submit" value="Hello" /></div></form>}, button_to("Hello", "http://www.example.com/q1=v1&amp;q2=v2".html_safe)
end
def test_button_to_with_query_and_no_name
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com?q1=v1&amp;q2=v2\" class=\"button_to\"><div><input type=\"submit\" value=\"http://www.example.com?q1=v1&amp;q2=v2\" /></div></form>", button_to(nil, "http://www.example.com?q1=v1&q2=v2")
+ assert_dom_equal %{<form method="post" action="http://www.example.com?q1=v1&amp;q2=v2" class="button_to"><div><input type="submit" value="http://www.example.com?q1=v1&amp;q2=v2" /></div></form>}, button_to(nil, "http://www.example.com?q1=v1&q2=v2")
end
def test_button_to_with_javascript_confirm
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", data: { confirm: "Are you sure?" })
)
end
def test_button_to_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", confirm: "Are you sure?")
)
end
end
def test_button_to_with_javascript_disable_with
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :data => { :disable_with => "Greeting..." })
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", data: { disable_with: "Greeting..." })
)
end
def test_button_to_with_javascript_deprecated_disable_with
assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disable_with => "Greeting...")
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", disable_with: "Greeting...")
)
end
end
def test_button_to_with_remote_and_form_options
- assert_dom_equal "<form method=\"post\" action=\"http://www.example.com\" class=\"custom-class\" data-remote=\"true\" data-type=\"json\"><div><input type=\"submit\" value=\"Hello\" /></div></form>", button_to("Hello", "http://www.example.com", :remote => true, :form => { :class => "custom-class", "data-type" => "json" } )
+ assert_dom_equal(
+ %{<form method="post" action="http://www.example.com" class="custom-class" data-remote="true" data-type="json"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, form: { class: "custom-class", "data-type" => "json" })
+ )
end
def test_button_to_with_remote_and_javascript_confirm
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :data => { :confirm => "Are you sure?" })
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, data: { confirm: "Are you sure?" })
)
end
def test_button_to_with_remote_and_javascript_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-confirm=\"Are you sure?\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :confirm => "Are you sure?")
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-confirm="Are you sure?" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, confirm: "Are you sure?")
)
end
end
def test_button_to_with_remote_and_javascript_disable_with
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :data => { :disable_with => "Greeting..." })
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, data: { disable_with: "Greeting..." })
)
end
def test_button_to_with_remote_and_javascript_deprecated_disable_with
assert_deprecated ":disable_with option is deprecated and will be removed from Rails 4.1. Use ':data => { :disable_with => \'Text\' }' instead" do
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\" data-remote=\"true\"><div><input data-disable-with=\"Greeting...\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => true, :disable_with => "Greeting...")
+ %{<form method="post" action="http://www.example.com" class="button_to" data-remote="true"><div><input data-disable-with="Greeting..." type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: true, disable_with: "Greeting...")
)
end
end
def test_button_to_with_remote_false
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :remote => false)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", remote: false)
)
end
def test_button_to_enabled_disabled
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disabled => false)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", disabled: false)
)
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input disabled=\"disabled\" type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :disabled => true)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input disabled="disabled" type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", disabled: true)
)
end
def test_button_to_with_method_delete
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"hidden\" name=\"_method\" value=\"delete\" /><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :method => :delete)
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><input type="hidden" name="_method" value="delete" /><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", method: :delete)
)
end
def test_button_to_with_method_get
assert_dom_equal(
- "<form method=\"get\" action=\"http://www.example.com\" class=\"button_to\"><div><input type=\"submit\" value=\"Hello\" /></div></form>",
- button_to("Hello", "http://www.example.com", :method => :get)
+ %{<form method="get" action="http://www.example.com" class="button_to"><div><input type="submit" value="Hello" /></div></form>},
+ button_to("Hello", "http://www.example.com", method: :get)
)
end
def test_button_to_with_block
assert_dom_equal(
- "<form method=\"post\" action=\"http://www.example.com\" class=\"button_to\"><div><button type=\"submit\"><span>Hello</span></button></div></form>",
+ %{<form method="post" action="http://www.example.com" class="button_to"><div><button type="submit"><span>Hello</span></button></div></form>},
button_to("http://www.example.com") { content_tag(:span, 'Hello') }
)
end
def test_link_tag_with_straight_url
- assert_dom_equal "<a href=\"http://www.example.com\">Hello</a>", link_to("Hello", "http://www.example.com")
+ assert_dom_equal %{<a href="http://www.example.com">Hello</a>}, link_to("Hello", "http://www.example.com")
end
def test_link_tag_without_host_option
- assert_dom_equal(%q{<a href="/">Test Link</a>}, link_to('Test Link', url_hash))
+ assert_dom_equal(%{<a href="/">Test Link</a>}, link_to('Test Link', url_hash))
end
def test_link_tag_with_host_option
- hash = hash_for(:host => "www.example.com")
- expected = %q{<a href="http://www.example.com/">Test Link</a>}
+ hash = hash_for(host: "www.example.com")
+ expected = %{<a href="http://www.example.com/">Test Link</a>}
assert_dom_equal(expected, link_to('Test Link', hash))
end
@@ -243,131 +246,143 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_link_tag_with_custom_onclick
- link = link_to("Hello", "http://www.example.com", :onclick => "alert('yay!')")
+ link = link_to("Hello", "http://www.example.com", onclick: "alert('yay!')")
expected = %{<a href="http://www.example.com" onclick="alert(&#39;yay!&#39;)">Hello</a>}
assert_dom_equal expected, link
end
def test_link_tag_with_javascript_confirm
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :data => { :confirm => "Are you sure?" })
+ %{<a href="http://www.example.com" data-confirm="Are you sure?">Hello</a>},
+ link_to("Hello", "http://www.example.com", data: { confirm: "Are you sure?" })
)
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure, can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :data => { :confirm => "You cant possibly be sure, can you?" })
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure, can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", data: { confirm: "You cant possibly be sure, can you?" })
)
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure,\n can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :data => { :confirm => "You cant possibly be sure,\n can you?" })
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure,\n can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", data: { confirm: "You cant possibly be sure,\n can you?" })
)
end
def test_link_tag_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"Are you sure?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "Are you sure?")
+ %{<a href="http://www.example.com" data-confirm="Are you sure?">Hello</a>},
+ link_to("Hello", "http://www.example.com", confirm: "Are you sure?")
)
end
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure, can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You cant possibly be sure, can you?")
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure, can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", confirm: "You cant possibly be sure, can you?")
)
end
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-confirm=\"You cant possibly be sure,\n can you?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :confirm => "You cant possibly be sure,\n can you?")
+ %{<a href="http://www.example.com" data-confirm="You cant possibly be sure,\n can you?">Hello</a>},
+ link_to("Hello", "http://www.example.com", confirm: "You cant possibly be sure,\n can you?")
)
end
end
def test_link_to_with_remote
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-remote=\"true\">Hello</a>",
- link_to("Hello", "http://www.example.com", :remote => true)
+ %{<a href="http://www.example.com" data-remote="true">Hello</a>},
+ link_to("Hello", "http://www.example.com", remote: true)
)
end
def test_link_to_with_remote_false
assert_dom_equal(
- "<a href=\"http://www.example.com\">Hello</a>",
- link_to("Hello", "http://www.example.com", :remote => false)
+ %{<a href="http://www.example.com">Hello</a>},
+ link_to("Hello", "http://www.example.com", remote: false)
+ )
+ end
+
+ def test_link_to_with_symbolic_remote_in_non_html_options
+ assert_dom_equal(
+ %{<a href="/" data-remote="true">Hello</a>},
+ link_to("Hello", hash_for(remote: true), {})
+ )
+ end
+
+ def test_link_to_with_string_remote_in_non_html_options
+ assert_dom_equal(
+ %{<a href="/" data-remote="true">Hello</a>},
+ link_to("Hello", hash_for('remote' => true), {})
)
end
def test_link_tag_using_post_javascript
assert_dom_equal(
- "<a href='http://www.example.com' data-method=\"post\" rel=\"nofollow\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post)
+ %{<a href="http://www.example.com" data-method="post" rel="nofollow">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post)
)
end
def test_link_tag_using_delete_javascript
assert_dom_equal(
- "<a href='http://www.example.com' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete)
+ %{<a href="http://www.example.com" rel="nofollow" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete)
)
end
def test_link_tag_using_delete_javascript_and_href
assert_dom_equal(
- "<a href='\#' rel=\"nofollow\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#')
+ %{<a href="\#" rel="nofollow" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete, href: '#')
)
end
def test_link_tag_using_post_javascript_and_rel
assert_dom_equal(
- "<a href='http://www.example.com' data-method=\"post\" rel=\"example nofollow\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :rel => 'example')
+ %{<a href="http://www.example.com" data-method="post" rel="example nofollow">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post, rel: 'example')
)
end
def test_link_tag_using_post_javascript_and_confirm
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :data => { :confirm => "Are you serious?" })
+ %{<a href="http://www.example.com" data-method="post" rel="nofollow" data-confirm="Are you serious?">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post, data: { confirm: "Are you serious?" })
)
end
def test_link_tag_using_post_javascript_and_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href=\"http://www.example.com\" data-method=\"post\" rel=\"nofollow\" data-confirm=\"Are you serious?\">Hello</a>",
- link_to("Hello", "http://www.example.com", :method => :post, :confirm => "Are you serious?")
+ %{<a href="http://www.example.com" data-method="post" rel="nofollow" data-confirm="Are you serious?">Hello</a>},
+ link_to("Hello", "http://www.example.com", method: :post, confirm: "Are you serious?")
)
end
end
def test_link_tag_using_delete_javascript_and_href_and_confirm
assert_dom_equal(
- "<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :data => { :confirm => "Are you serious?" }),
- "When specifying url, form should be generated with it, but not this.href"
+ %{<a href="\#" rel="nofollow" data-confirm="Are you serious?" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete, href: '#', data: { confirm: "Are you serious?" })
)
end
def test_link_tag_using_delete_javascript_and_href_and_with_deprecated_confirm
assert_deprecated ":confirm option is deprecated and will be removed from Rails 4.1. Use ':data => { :confirm => \'Text\' }' instead" do
assert_dom_equal(
- "<a href='\#' rel=\"nofollow\" data-confirm=\"Are you serious?\" data-method=\"delete\">Destroy</a>",
- link_to("Destroy", "http://www.example.com", :method => :delete, :href => '#', :confirm => "Are you serious?"),
- "When specifying url, form should be generated with it, but not this.href"
+ %{<a href="\#" rel="nofollow" data-confirm="Are you serious?" data-method="delete">Destroy</a>},
+ link_to("Destroy", "http://www.example.com", method: :delete, href: '#', confirm: "Are you serious?")
)
end
end
def test_link_tag_with_block
- assert_dom_equal '<a href="/"><span>Example site</span></a>',
+ assert_dom_equal %{<a href="/"><span>Example site</span></a>},
link_to('/') { content_tag(:span, 'Example site') }
end
def test_link_tag_with_block_and_html_options
- assert_dom_equal '<a class="special" href="/"><span>Example site</span></a>',
- link_to('/', :class => "special") { content_tag(:span, 'Example site') }
+ assert_dom_equal %{<a class="special" href="/"><span>Example site</span></a>},
+ link_to('/', class: "special") { content_tag(:span, 'Example site') }
end
def test_link_tag_using_block_in_erb
@@ -377,18 +392,18 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_tag_with_html_safe_string
assert_dom_equal(
- "<a href=\"/article/Gerd_M%C3%BCller\">Gerd Müller</a>",
+ %{<a href="/article/Gerd_M%C3%BCller">Gerd Müller</a>},
link_to("Gerd Müller", article_path("Gerd_Müller".html_safe))
)
end
def test_link_tag_escapes_content
- assert_dom_equal '<a href="/">Malicious &lt;script&gt;content&lt;/script&gt;</a>',
+ assert_dom_equal %{<a href="/">Malicious &lt;script&gt;content&lt;/script&gt;</a>},
link_to("Malicious <script>content</script>", "/")
end
def test_link_tag_does_not_escape_html_safe_content
- assert_dom_equal '<a href="/">Malicious <script>content</script></a>',
+ assert_dom_equal %{<a href="/">Malicious <script>content</script></a>},
link_to("Malicious <script>content</script>".html_safe, "/")
end
@@ -438,12 +453,12 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_current_page_with_params_that_match
@request = request_for_url("/?order=desc&page=1")
- assert current_page?(hash_for(:order => "desc", :page => "1"))
+ assert current_page?(hash_for(order: "desc", page: "1"))
assert current_page?("http://www.example.com/?order=desc&page=1")
end
def test_current_page_with_not_get_verb
- @request = request_for_url("/events", :method => :post)
+ @request = request_for_url("/events", method: :post)
assert !current_page?('/events')
end
@@ -466,20 +481,20 @@ class UrlHelperTest < ActiveSupport::TestCase
@request = request_for_url("/?order=desc&page=1")
assert_equal "Showing",
- link_to_unless_current("Showing", hash_for(:order => 'desc', :page => '1'))
+ link_to_unless_current("Showing", hash_for(order: 'desc', page: '1'))
assert_equal "Showing",
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=1")
@request = request_for_url("/?order=desc")
assert_equal %{<a href="/?order=asc">Showing</a>},
- link_to_unless_current("Showing", hash_for(:order => :asc))
+ link_to_unless_current("Showing", hash_for(order: :asc))
assert_equal %{<a href="http://www.example.com/?order=asc">Showing</a>},
link_to_unless_current("Showing", "http://www.example.com/?order=asc")
@request = request_for_url("/?order=desc")
assert_equal %{<a href="/?order=desc&amp;page=2\">Showing</a>},
- link_to_unless_current("Showing", hash_for(:order => "desc", :page => 2))
+ link_to_unless_current("Showing", hash_for(order: "desc", page: 2))
assert_equal %{<a href="http://www.example.com/?order=desc&amp;page=2">Showing</a>},
link_to_unless_current("Showing", "http://www.example.com/?order=desc&page=2")
@@ -492,56 +507,86 @@ class UrlHelperTest < ActiveSupport::TestCase
end
def test_mail_to
- assert_dom_equal "<a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>", mail_to("david@loudthinking.com")
- assert_dom_equal "<a href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>", mail_to("david@loudthinking.com", "David Heinemeier Hansson")
+ assert_dom_equal %{<a href="mailto:david@loudthinking.com">david@loudthinking.com</a>}, mail_to("david@loudthinking.com")
+ assert_dom_equal %{<a href="mailto:david@loudthinking.com">David Heinemeier Hansson</a>}, mail_to("david@loudthinking.com", "David Heinemeier Hansson")
assert_dom_equal(
- "<a class=\"admin\" href=\"mailto:david@loudthinking.com\">David Heinemeier Hansson</a>",
+ %{<a class="admin" href="mailto:david@loudthinking.com">David Heinemeier Hansson</a>},
mail_to("david@loudthinking.com", "David Heinemeier Hansson", "class" => "admin")
)
assert_equal mail_to("david@loudthinking.com", "David Heinemeier Hansson", "class" => "admin"),
- mail_to("david@loudthinking.com", "David Heinemeier Hansson", :class => "admin")
+ mail_to("david@loudthinking.com", "David Heinemeier Hansson", class: "admin")
end
def test_mail_to_with_javascript
- snippet = mail_to("me@domain.com", "My email", :encode => "javascript")
+ snippet = mail_to("me@domain.com", "My email", encode: "javascript")
assert_dom_equal "<script>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
end
def test_mail_to_with_javascript_unicode
- snippet = mail_to("unicode@example.com", "únicode", :encode => "javascript")
+ snippet = mail_to("unicode@example.com", "únicode", encode: "javascript")
assert_dom_equal "<script>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
end
def test_mail_with_options
assert_dom_equal(
- %(<a href="mailto:me@example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email">My email</a>),
- mail_to("me@example.com", "My email", :cc => "ccaddress@example.com", :bcc => "bccaddress@example.com", :subject => "This is an example email", :body => "This is the body of the message.")
+ %{<a href="mailto:me@example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email">My email</a>},
+ mail_to("me@example.com", "My email", cc: "ccaddress@example.com", bcc: "bccaddress@example.com", subject: "This is an example email", body: "This is the body of the message.")
)
end
def test_mail_to_with_img
- assert_dom_equal %(<a href="mailto:feedback@example.com"><img src="/feedback.png" /></a>),
+ assert_dom_equal %{<a href="mailto:feedback@example.com"><img src="/feedback.png" /></a>},
mail_to('feedback@example.com', '<img src="/feedback.png" />'.html_safe)
end
def test_mail_to_with_hex
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#64;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex")
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>},
+ mail_to("me@domain.com", "My email", encode: "hex")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#64;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>},
+ mail_to("me@domain.com", nil, encode: "hex")
+ )
end
def test_mail_to_with_replace_options
- assert_dom_equal "<a href=\"mailto:wolfgang@stufenlos.net\">wolfgang(at)stufenlos(dot)net</a>", mail_to("wolfgang@stufenlos.net", nil, :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")
- assert_dom_equal "<a href=\"&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<script>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>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")
- assert_dom_equal "<script>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)")
+ assert_dom_equal(
+ %{<a href="mailto:wolfgang@stufenlos.net">wolfgang(at)stufenlos(dot)net</a>},
+ mail_to("wolfgang@stufenlos.net", nil, replace_at: "(at)", replace_dot: "(dot)")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>},
+ mail_to("me@domain.com", nil, encode: "hex", replace_at: "(at)")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>},
+ mail_to("me@domain.com", "My email", encode: "hex", replace_at: "(at)")
+ )
+
+ assert_dom_equal(
+ %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>},
+ mail_to("me@domain.com", nil, encode: "hex", replace_at: "(at)", replace_dot: "(dot)")
+ )
+
+ assert_dom_equal(
+ %{<script>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>},
+ mail_to("me@domain.com", "My email", encode: "javascript", replace_at: "(at)", replace_dot: "(dot)")
+ )
+
+ assert_dom_equal(
+ %{<script>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?
+ 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
def protect_against_forgery?
@@ -568,62 +613,62 @@ class UrlHelperControllerTest < ActionController::TestCase
class UrlHelperController < ActionController::Base
test_routes do
get 'url_helper_controller_test/url_helper/show/:id',
- :to => 'url_helper_controller_test/url_helper#show',
- :as => :show
+ to: 'url_helper_controller_test/url_helper#show',
+ as: :show
get 'url_helper_controller_test/url_helper/profile/:name',
- :to => 'url_helper_controller_test/url_helper#show',
- :as => :profile
+ to: 'url_helper_controller_test/url_helper#show',
+ as: :profile
get 'url_helper_controller_test/url_helper/show_named_route',
- :to => 'url_helper_controller_test/url_helper#show_named_route',
- :as => :show_named_route
+ to: 'url_helper_controller_test/url_helper#show_named_route',
+ as: :show_named_route
get "/:controller(/:action(/:id))"
get 'url_helper_controller_test/url_helper/normalize_recall_params',
- :to => UrlHelperController.action(:normalize_recall),
- :as => :normalize_recall_params
+ to: UrlHelperController.action(:normalize_recall),
+ as: :normalize_recall_params
get '/url_helper_controller_test/url_helper/override_url_helper/default',
- :to => 'url_helper_controller_test/url_helper#override_url_helper',
- :as => :override_url_helper
+ to: 'url_helper_controller_test/url_helper#override_url_helper',
+ as: :override_url_helper
end
def show
if params[:name]
- render :inline => 'ok'
+ render inline: 'ok'
else
redirect_to profile_path(params[:id])
end
end
def show_url_for
- render :inline => "<%= url_for :controller => 'url_helper_controller_test/url_helper', :action => 'show_url_for' %>"
+ render inline: "<%= url_for controller: 'url_helper_controller_test/url_helper', action: 'show_url_for' %>"
end
def show_overriden_url_for
- render :inline => "<%= url_for params.merge(:controller => 'url_helper_controller_test/url_helper', :action => 'show_url_for') %>"
+ render inline: "<%= url_for params.merge(controller: 'url_helper_controller_test/url_helper', action: 'show_url_for') %>"
end
def show_named_route
- render :inline => "<%= show_named_route_#{params[:kind]} %>"
+ render inline: "<%= show_named_route_#{params[:kind]} %>"
end
def nil_url_for
- render :inline => '<%= url_for(nil) %>'
+ render inline: '<%= url_for(nil) %>'
end
def normalize_recall_params
- render :inline => '<%= normalize_recall_params_path %>'
+ render inline: '<%= normalize_recall_params_path %>'
end
def recall_params_not_changed
- render :inline => '<%= url_for(:action => :show_url_for) %>'
+ render inline: '<%= url_for(action: :show_url_for) %>'
end
def override_url_helper
- render :inline => '<%= override_url_helper_path %>'
+ render inline: '<%= override_url_helper_path %>'
end
def override_url_helper_path
@@ -645,13 +690,13 @@ class UrlHelperControllerTest < ActionController::TestCase
end
def test_named_route_url_shows_host_and_path
- get :show_named_route, :kind => 'url'
+ get :show_named_route, kind: 'url'
assert_equal 'http://test.host/url_helper_controller_test/url_helper/show_named_route',
@response.body
end
def test_named_route_path_shows_only_path
- get :show_named_route, :kind => 'path'
+ get :show_named_route, kind: 'path'
assert_equal '/url_helper_controller_test/url_helper/show_named_route', @response.body
end
@@ -663,11 +708,11 @@ class UrlHelperControllerTest < ActionController::TestCase
def test_named_route_should_show_host_and_path_using_controller_default_url_options
class << @controller
def default_url_options
- {:host => 'testtwo.host'}
+ { host: 'testtwo.host' }
end
end
- get :show_named_route, :kind => 'url'
+ get :show_named_route, kind: 'url'
assert_equal 'http://testtwo.host/url_helper_controller_test/url_helper/show_named_route', @response.body
end
@@ -682,11 +727,11 @@ class UrlHelperControllerTest < ActionController::TestCase
end
def test_recall_params_should_normalize_id
- get :show, :id => '123'
+ get :show, id: '123'
assert_equal 302, @response.status
assert_equal 'http://test.host/url_helper_controller_test/url_helper/profile/123', @response.location
- get :show, :name => '123'
+ get :show, name: '123'
assert_equal 'ok', @response.body
end
@@ -711,9 +756,8 @@ class TasksController < ActionController::Base
protected
def render_default
- render :inline =>
- "<%= link_to_unless_current(\"tasks\", tasks_path) %>\n" +
- "<%= link_to_unless_current(\"tasks\", tasks_url) %>"
+ render inline: "<%= link_to_unless_current('tasks', tasks_path) %>\n" +
+ "<%= link_to_unless_current('tasks', tasks_url) %>"
end
end
@@ -726,9 +770,9 @@ class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase
end
def test_link_to_unless_current_shows_link
- get :show, :id => 1
- assert_equal "<a href=\"/tasks\">tasks</a>\n" +
- "<a href=\"#{@request.protocol}#{@request.host_with_port}/tasks\">tasks</a>",
+ get :show, id: 1
+ assert_equal %{<a href="/tasks">tasks</a>\n} +
+ %{<a href="#{@request.protocol}#{@request.host_with_port}/tasks">tasks</a>},
@response.body
end
end
@@ -760,12 +804,12 @@ class WorkshopsController < ActionController::Base
def index
@workshop = Workshop.new(nil)
- render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+ render inline: "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
def show
@workshop = Workshop.new(params[:id])
- render :inline => "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
+ render inline: "<%= url_for(@workshop) %>\n<%= link_to('Workshop', @workshop) %>"
end
end
@@ -779,13 +823,13 @@ class SessionsController < ActionController::Base
def index
@workshop = Workshop.new(params[:workshop_id])
@session = Session.new(nil)
- render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+ render inline: "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
def show
@workshop = Workshop.new(params[:workshop_id])
@session = Session.new(params[:id])
- render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
+ render inline: "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>"
end
end
@@ -794,27 +838,27 @@ class PolymorphicControllerTest < ActionController::TestCase
@controller = WorkshopsController.new
get :index
- assert_equal "/workshops\n<a href=\"/workshops\">Workshop</a>", @response.body
+ assert_equal %{/workshops\n<a href="/workshops">Workshop</a>}, @response.body
end
def test_existing_resource
@controller = WorkshopsController.new
- get :show, :id => 1
- assert_equal "/workshops/1\n<a href=\"/workshops/1\">Workshop</a>", @response.body
+ get :show, id: 1
+ assert_equal %{/workshops/1\n<a href="/workshops/1">Workshop</a>}, @response.body
end
def test_new_nested_resource
@controller = SessionsController.new
- get :index, :workshop_id => 1
- assert_equal "/workshops/1/sessions\n<a href=\"/workshops/1/sessions\">Session</a>", @response.body
+ get :index, workshop_id: 1
+ assert_equal %{/workshops/1/sessions\n<a href="/workshops/1/sessions">Session</a>}, @response.body
end
def test_existing_nested_resource
@controller = SessionsController.new
- get :show, :workshop_id => 1, :id => 1
- assert_equal "/workshops/1/sessions/1\n<a href=\"/workshops/1/sessions/1\">Session</a>", @response.body
+ get :show, workshop_id: 1, id: 1
+ assert_equal %{/workshops/1/sessions/1\n<a href="/workshops/1/sessions/1">Session</a>}, @response.body
end
end
diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb
index 4762f39044..243d911f71 100644
--- a/activemodel/lib/active_model/validations.rb
+++ b/activemodel/lib/active_model/validations.rb
@@ -188,9 +188,9 @@ module ActiveModel
# # #<ActiveModel::Validations::InclusionValidator:0x007fe603bb8780 @attributes=[:age], @options={:in=>0..99}>
# # ]
def validators_on(*attributes)
- attributes.map do |attribute|
+ attributes.flat_map do |attribute|
_validators[attribute.to_sym]
- end.flatten
+ end
end
# Returns +true+ if +attribute+ is an attribute method, +false+ otherwise.
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index 67bec5f38e..9abfe2e6fd 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,44 @@
## Rails 4.0.0 (unreleased) ##
+* Rename `ActiveRecord::Fixtures` class to `ActiveRecord::FixtureSet`.
+ Instances of this class normally hold a collection of fixtures (records)
+ loaded either from a single YAML file, or from a file and a folder
+ with the same name. This change make the class name singular and makes
+ the class easier to distinguish from the modules like
+ `ActiveRecord::TestFixtures`, which operates on multiple fixture sets,
+ or `DelegatingFixtures`, `::Fixtures`, etc.,
+ and from the class `ActiveRecord::Fixture`, which corresponds to a single
+ fixture.
+
+ *Alexey Muranov*
+
+* The postgres adapter now supports tables with capital letters.
+ Fix #5920
+
+ *Yves Senn*
+
+* `CollectionAssociation#count` returns `0` without querying if the
+ parent record is not persisted.
+
+ Before:
+
+ person.pets.count
+ # SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" IS NULL
+ # => 0
+
+ After:
+
+ person.pets.count
+ # fires without sql query
+ # => 0
+
+ *Francesco Rodriguez*
+
+* Fix `reset_counters` crashing on `has_many :through` associations.
+ Fix #7822.
+
+ *lulalala*
+
* Support for partial inserts.
When inserting new records, only the fields which have been changed
@@ -608,7 +647,7 @@
*Michael Pearson*
* Added default order to `first` to assure consistent results among
- diferent database engines. Introduced `take` as a replacement to
+ different database engines. Introduced `take` as a replacement to
the old behavior of `first`.
*Marcelo Silveira*
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index fe3e5b00f7..7f39d3083e 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -174,6 +174,8 @@ module ActiveRecord
# association, it will be used for the query. Otherwise, construct options and pass them with
# scope to the target class's +count+.
def count(column_name = nil, count_options = {})
+ return 0 if owner.new_record?
+
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
if options[:counter_sql] || options[:finder_sql]
@@ -412,7 +414,7 @@ module ActiveRecord
persisted.map! do |record|
if mem_record = memory.delete(record)
- (record.attribute_names - mem_record.changes.keys).each do |name|
+ ((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
mem_record[name] = record[name]
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 8a073bf878..2264595751 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -314,7 +314,7 @@ module ActiveRecord
INNER JOIN pg_depend dep ON attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
WHERE cons.contype = 'p'
- AND dep.refobjid = '#{table}'::regclass
+ AND dep.refobjid = '#{quote_table_name(table)}'::regclass
end_sql
row && row.first
diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb
index c877079b25..57838ff984 100644
--- a/activerecord/lib/active_record/counter_cache.rb
+++ b/activerecord/lib/active_record/counter_cache.rb
@@ -22,6 +22,10 @@ module ActiveRecord
counters.each do |association|
has_many_association = reflect_on_association(association.to_sym)
+ if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
+ has_many_association = has_many_association.through_reflection
+ end
+
foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
diff --git a/activerecord/lib/active_record/fixtures/file.rb b/activerecord/lib/active_record/fixture_set/file.rb
index 0f6ab3e396..11b53275e1 100644
--- a/activerecord/lib/active_record/fixtures/file.rb
+++ b/activerecord/lib/active_record/fixture_set/file.rb
@@ -2,7 +2,7 @@ require 'erb'
require 'yaml'
module ActiveRecord
- class Fixtures
+ class FixtureSet
class File # :nodoc:
include Enumerable
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 60fc653735..899e89a6f5 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -2,9 +2,11 @@ require 'erb'
require 'yaml'
require 'zlib'
require 'active_support/dependencies'
-require 'active_record/fixtures/file'
+require 'active_record/fixture_set/file'
require 'active_record/errors'
+require 'active_support/deprecation' # temporary
+
module ActiveRecord
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
end
@@ -350,8 +352,8 @@ module ActiveRecord
# to the rescue:
#
# george_reginald:
- # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
- # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
#
# == Support for YAML defaults
#
@@ -370,10 +372,11 @@ module ActiveRecord
# *DEFAULTS
#
# Any fixture labeled "DEFAULTS" is safely ignored.
- class Fixtures
+ class FixtureSet
#--
- # NOTE: an instance of Fixtures can be called fixture_set, it is normally stored in a single YAML file and possibly in a folder with the same name.
+ # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
#++
+
MAX_ID = 2 ** 30 - 1
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
@@ -451,7 +454,7 @@ module ActiveRecord
fixtures_map = {}
fixture_sets = files_to_read.map do |fs_name|
- fixtures_map[fs_name] = new( # ActiveRecord::Fixtures.new
+ fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
connection,
fs_name,
class_names[fs_name] || default_fixture_model_name(fs_name),
@@ -565,7 +568,7 @@ module ActiveRecord
# generate a primary key if necessary
if has_primary_key_column? && !row.include?(primary_key_name)
- row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
end
# If STI is used, find the correct subclass for association reflection
@@ -588,7 +591,7 @@ module ActiveRecord
row[association.foreign_type] = $1
end
- row[fk_name] = ActiveRecord::Fixtures.identify(value)
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value)
end
when :has_and_belongs_to_many
if (targets = row.delete(association.name.to_s))
@@ -596,7 +599,7 @@ module ActiveRecord
table_name = association.join_table
rows[table_name].concat targets.map { |target|
{ association.foreign_key => row[primary_key_name],
- association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
+ association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
}
end
end
@@ -637,7 +640,7 @@ module ActiveRecord
} + [yaml_file_path]
yaml_files.each do |file|
- Fixtures::File.open(file) do |fh|
+ FixtureSet::File.open(file) do |fh|
fh.each do |fixture_name, row|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
end
@@ -651,6 +654,11 @@ module ActiveRecord
end
+ #--
+ # Deprecate 'Fixtures' in favor of 'FixtureSet'.
+ #++
+ Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', '::ActiveRecord::FixtureSet')
+
class Fixture #:nodoc:
include Enumerable
@@ -712,7 +720,7 @@ module ActiveRecord
self.pre_loaded_fixtures = false
self.fixture_class_names = Hash.new do |h, fixture_set_name|
- h[fixture_set_name] = ActiveRecord::Fixtures.default_fixture_model_name(fixture_set_name)
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
end
end
@@ -847,7 +855,7 @@ module ActiveRecord
end
# Load fixtures for every test.
else
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
@@already_loaded_fixtures[self.class] = nil
@loaded_fixtures = load_fixtures
end
@@ -860,7 +868,7 @@ module ActiveRecord
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
unless run_in_transaction?
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
end
# Rollback changes if a transaction is active.
@@ -879,7 +887,7 @@ module ActiveRecord
private
def load_fixtures
- fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
Hash[fixtures.map { |f| [f.name, f] }]
end
@@ -888,16 +896,16 @@ module ActiveRecord
def instantiate_fixtures
if pre_loaded_fixtures
- raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
+ raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
unless @@required_fixture_classes
- self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
+ self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
@@required_fixture_classes = true
end
- ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
else
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
@loaded_fixtures.each_value do |fixture_set|
- ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
end
end
end
diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb
index a25f2c7bca..ca79950049 100644
--- a/activerecord/lib/active_record/log_subscriber.rb
+++ b/activerecord/lib/active_record/log_subscriber.rb
@@ -1,11 +1,13 @@
module ActiveRecord
class LogSubscriber < ActiveSupport::LogSubscriber
+ IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
+
def self.runtime=(value)
- Thread.current["active_record_sql_runtime"] = value
+ Thread.current[:active_record_sql_runtime] = value
end
def self.runtime
- Thread.current["active_record_sql_runtime"] ||= 0
+ Thread.current[:active_record_sql_runtime] ||= 0
end
def self.reset_runtime
@@ -24,9 +26,9 @@ module ActiveRecord
payload = event.payload
- return if 'SCHEMA' == payload[:name]
+ return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
- name = '%s (%.1fms)' % [payload[:name], event.duration]
+ name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
sql = payload[:sql].squeeze(' ')
binds = nil
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index b11483de8c..d7e35fb771 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -129,7 +129,8 @@ module ActiveRecord
end
initializer "active_record.add_watchable_files" do |app|
- config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"]
+ path = app.paths["db"].first
+ config.watchable_files.concat ["#{path}/schema.rb", "#{path}/structure.sql"]
end
config.after_initialize do |app|
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index d134978128..69a9526fcc 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -196,7 +196,7 @@ db_namespace = namespace :db do
fixtures_dir = File.join [base_dir, ENV['FIXTURES_DIR']].compact
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir["#{fixtures_dir}/**/*.yml"].map {|f| f[(fixtures_dir.size + 1)..-5] }).each do |fixture_file|
- ActiveRecord::Fixtures.create_fixtures(fixtures_dir, fixture_file)
+ ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_file)
end
end
@@ -207,13 +207,13 @@ db_namespace = namespace :db do
label, id = ENV['LABEL'], ENV['ID']
raise 'LABEL or ID required' if label.blank? && id.blank?
- puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::Fixtures.identify(label)}.) if label
+ puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label
base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures')
Dir["#{base_dir}/**/*.yml"].each do |file|
if data = YAML::load(ERB.new(IO.read(file)).result)
data.keys.each do |key|
- key_id = ActiveRecord::Fixtures.identify(key)
+ key_id = ActiveRecord::FixtureSet.identify(key)
if key == label || key_id == id.to_i
puts "#{file}: #{key} (#{key_id})"
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 42b4cff4b8..f3e47a958e 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -141,23 +141,6 @@ module ActiveRecord
end
end
- def expand_range_bind_variables(bind_vars) #:nodoc:
- expanded = []
-
- bind_vars.each do |var|
- next if var.is_a?(Hash)
-
- if var.is_a?(Range)
- expanded << var.first
- expanded << var.last
- else
- expanded << var
- end
- end
-
- expanded
- end
-
def quote_bound_value(value, c = connection) #:nodoc:
if value.respond_to?(:map) && !value.acts_like?(:string)
if value.respond_to?(:empty?) && value.empty?
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 019290725d..3706885881 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -10,7 +10,7 @@ module ActiveRecord
# puts invalid.record.errors
# end
class RecordInvalid < ActiveRecordError
- attr_reader :record # :nodoc:
+ attr_reader :record # :nodoc:
def initialize(record) # :nodoc:
@record = record
errors = @record.errors.full_messages.join(", ")
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index f3620c1324..5dece1cb36 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -64,14 +64,12 @@ module ActiveRecord
end
def build_relation(klass, table, attribute, value) #:nodoc:
- reflection = klass.reflect_on_association(attribute)
- if reflection
- column = klass.columns_hash[reflection.foreign_key]
+ if reflection = klass.reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.primary_key_column.name]
- else
- column = klass.columns_hash[attribute.to_s]
end
+
+ column = klass.columns_hash[attribute.to_s]
value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text?
if !options[:case_sensitive] && value && column.text?
diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
index aff971a955..5164acf77f 100644
--- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
@@ -136,9 +136,9 @@ class MysqlReservedWordTest < ActiveRecord::TestCase
#the following functions were added to DRY test cases
private
- # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
+ # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
def create_test_fixtures(*fixture_names)
- ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
end
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index 9fd07f014e..1017b0758d 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -136,9 +136,9 @@ class MysqlReservedWordTest < ActiveRecord::TestCase
#the following functions were added to DRY test cases
private
- # custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
+ # custom fixture loader, uses FixtureSet#create_fixtures and appends base_path to the current file's path
def create_test_fixtures(*fixture_names)
- ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
end
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 92e31a3e44..f1362dd15f 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -14,6 +14,10 @@ module ActiveRecord
assert_equal 'id', @connection.primary_key('ex')
end
+ def test_primary_key_works_tables_containing_capital_letters
+ assert_equal 'id', @connection.primary_key('CamelCase')
+ end
+
def test_non_standard_primary_key
@connection.exec_query('drop table if exists ex')
@connection.exec_query('create table ex(data character varying(255) primary key)')
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index f3520d43e0..42f5b69d4e 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -799,6 +799,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, developer.projects.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Developer.new.projects.count
+ end
+ end
+
unless current_adapter?(:PostgreSQLAdapter)
def test_count_with_finder_sql
assert_equal 3, projects(:active_record).developers_with_finder_sql.count
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 4b56037a08..50c23c863f 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -262,6 +262,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal firm.limited_clients.length, firm.limited_clients.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.readers.count
+ end
+ end
+
def test_finding
assert_equal 2, Firm.all.merge!(:order => "id").first.clients.length
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index d4ceae6f80..f0582a3090 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -767,6 +767,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 1, authors(:mary).categories.general.count
end
+ def test_counting_should_not_fire_sql_if_parent_is_unsaved
+ assert_no_queries do
+ assert_equal 0, Person.new.posts.count
+ end
+ end
+
def test_has_many_through_belongs_to_should_update_when_the_through_foreign_key_changes
post = posts(:eager_other)
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 86893ec4b3..9b00c21b52 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -231,6 +231,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal "2", categories(:sti_test).authors_with_select.first.post_id.to_s
end
+ def test_create_through_has_many_with_piggyback
+ category = categories(:sti_test)
+ ernie = category.authors_with_select.create(:name => 'Ernie')
+ assert_nothing_raised do
+ assert_equal ernie, category.authors_with_select.detect {|a| a.name == 'Ernie'}
+ end
+ end
+
def test_include_has_many_through
posts = Post.all.merge!(:order => 'posts.id').to_a
posts_with_authors = Post.all.merge!(:includes => :authors, :order => 'posts.id').to_a
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fbfdd0f07a..0f859bf452 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -617,343 +617,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal 'value2', weird.read_attribute('a$b')
end
- def test_multiparameter_attributes_on_date
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
- end
-
- def test_multiparameter_attributes_on_date_with_empty_year
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_month
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_day
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_day_and_year
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_day_and_month
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_empty_year_and_month
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
- topic = Topic.find(1)
- topic.attributes = attributes
- # note that extra #to_date call allows test to pass for Oracle, which
- # treats dates/times the same
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_date_with_all_empty
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.last_read
- end
-
- def test_multiparameter_attributes_on_time
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_with_no_date
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_with_invalid_time_params
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_with_old_date
- attributes = {
- "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
- assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
- end
-
- def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(4i)" => "16", "written_on(5i)" => "24"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- attributes = {
- "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- end
- assert_equal("written_on", ex.errors[0].attribute)
- end
-
- def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
- "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
- attributes = {
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
- "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
- attributes = {
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
- "written_on(4i)" => "16", "written_on(5i)" => "24"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.written_on
- end
- def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
- attributes = {
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
- "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_nil topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_with_utc
- ActiveRecord::Base.default_timezone = :utc
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
- end
-
- def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
- Time.zone = ActiveSupport::TimeZone[-28800]
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
- assert_equal Time.zone, topic.written_on.time_zone
- end
-
- def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
- Time.zone = ActiveSupport::TimeZone[-28800]
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- assert_equal false, topic.written_on.respond_to?(:time_zone)
- end
-
- def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
- Time.zone = ActiveSupport::TimeZone[-28800]
- Topic.skip_time_zone_conversion_for_attributes = [:written_on]
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
- assert_equal false, topic.written_on.respond_to?(:time_zone)
- ensure
- Topic.skip_time_zone_conversion_for_attributes = []
- end
-
- # Oracle, and Sybase do not have a TIME datatype.
- unless current_adapter?(:OracleAdapter, :SybaseAdapter)
- def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
- ActiveRecord::Base.time_zone_aware_attributes = true
- ActiveRecord::Base.default_timezone = :utc
- Time.zone = ActiveSupport::TimeZone[-28800]
- attributes = {
- "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
- "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
- assert topic.bonus_time.utc?
- end
- end
-
- def test_multiparameter_attributes_on_time_with_empty_seconds
- attributes = {
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
- }
- topic = Topic.find(1)
- topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- end
-
- def test_multiparameter_attributes_setting_time_attribute
- return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter
-
- topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
- assert_equal 1, topic.bonus_time.hour
- assert_equal 5, topic.bonus_time.min
- end
-
- def test_multiparameter_attributes_setting_date_attribute
- topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
- assert_equal 1952, topic.written_on.year
- assert_equal 3, topic.written_on.month
- assert_equal 11, topic.written_on.day
- end
-
- def test_multiparameter_attributes_setting_date_and_time_attribute
- topic = Topic.new(
- "written_on(1i)" => "1952",
- "written_on(2i)" => "3",
- "written_on(3i)" => "11",
- "written_on(4i)" => "13",
- "written_on(5i)" => "55")
- assert_equal 1952, topic.written_on.year
- assert_equal 3, topic.written_on.month
- assert_equal 11, topic.written_on.day
- assert_equal 13, topic.written_on.hour
- assert_equal 55, topic.written_on.min
- end
-
- def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
- assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
- Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
- end
- end
-
- def test_multiparameter_assignment_of_aggregation
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- assert_equal address, customer.address
- end
-
- def test_multiparameter_assignment_of_aggregation_out_of_order
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
- customer.attributes = attributes
- assert_equal address, customer.address
- end
-
- def test_multiparameter_assignment_of_aggregation_with_missing_values
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- end
- assert_equal("address", ex.errors[0].attribute)
- end
-
- def test_multiparameter_assignment_of_aggregation_with_blank_values
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
- customer.attributes = attributes
- assert_equal Address.new(nil, "The City", "The Country"), customer.address
- end
-
- def test_multiparameter_assignment_of_aggregation_with_large_index
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
- customer = Customer.new
- address = Address.new("The Street", "The City", "The Country")
- attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
- customer.attributes = attributes
- end
-
- assert_equal("address", ex.errors[0].attribute)
- end
-
def test_attributes_on_dummy_time
# Oracle, and Sybase do not have a TIME datatype.
return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index ee443741ca..fc46a249c8 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -10,9 +10,12 @@ require 'models/dog'
require 'models/dog_lover'
require 'models/person'
require 'models/friendship'
+require 'models/subscriber'
+require 'models/subscription'
+require 'models/book'
class CounterCacheTest < ActiveRecord::TestCase
- fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships
+ fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships, :subscribers, :subscriptions, :books
class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -118,4 +121,14 @@ class CounterCacheTest < ActiveRecord::TestCase
Person.reset_counters(michael.id, :followers)
end
end
+
+ test "reset counter of has_many :through association" do
+ subscriber = subscribers('second')
+ Subscriber.reset_counters(subscriber.id, 'books')
+ Subscriber.increment_counter('books_count', subscriber.id)
+
+ assert_difference 'subscriber.reload.books_count', -1 do
+ Subscriber.reset_counters(subscriber.id, 'books')
+ end
+ end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 75a52544d3..7334514f9a 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -554,7 +554,7 @@ class DirtyTest < ActiveRecord::TestCase
test "partial insert" do
with_partial_updates Person do
jon = nil
- assert_sql(/first_name/) do
+ assert_sql(/first_name/i) do
jon = Person.create! first_name: 'Jon'
end
diff --git a/activerecord/test/cases/fixtures/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index e623fbe4d1..a029fedbd3 100644
--- a/activerecord/test/cases/fixtures/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
require 'tempfile'
module ActiveRecord
- class Fixtures
+ class FixtureSet
class FileTest < ActiveRecord::TestCase
def test_open
fh = File.open(::File.join(FIXTURES_ROOT, "accounts.yml"))
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index c28f8de682..b0b29f5f42 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -40,7 +40,7 @@ class FixturesTest < ActiveRecord::TestCase
FIXTURES.each do |name|
fixtures = nil
assert_nothing_raised { fixtures = create_fixtures(name).first }
- assert_kind_of(ActiveRecord::Fixtures, fixtures)
+ assert_kind_of(ActiveRecord::FixtureSet, fixtures)
fixtures.each { |_name, fixture|
fixture.each { |key, value|
assert_match(MATCH_ATTRIBUTE_NAME, key)
@@ -57,7 +57,7 @@ class FixturesTest < ActiveRecord::TestCase
dir = File.dirname badyaml.path
name = File.basename badyaml.path, '.yml'
assert_raises(ActiveRecord::Fixture::FormatError) do
- ActiveRecord::Fixtures.create_fixtures(dir, name)
+ ActiveRecord::FixtureSet.create_fixtures(dir, name)
end
ensure
badyaml.close
@@ -65,7 +65,7 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_create_fixtures
- fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, "parrots")
assert Parrot.find_by_name('Curious George'), 'George is not in the database'
assert fixtures.detect { |f| f.name == 'parrots' }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}"
end
@@ -74,11 +74,11 @@ class FixturesTest < ActiveRecord::TestCase
fixtures_array = nil
assert_nothing_raised { fixtures_array = create_fixtures(*FIXTURES) }
assert_kind_of(Array, fixtures_array)
- fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::Fixtures, fixtures) }
+ fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::FixtureSet, fixtures) }
end
def test_create_symbol_fixtures
- fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
assert Course.find_by_name('Collection'), 'course is not in the database'
assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
@@ -102,7 +102,7 @@ class FixturesTest < ActiveRecord::TestCase
if ActiveRecord::Base.connection.supports_migrations?
def test_inserts_with_pre_and_suffix
# Reset cache to make finds on the new table work
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t|
t.column :title, :string
@@ -190,11 +190,11 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_empty_yaml_fixture
- assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts")
+ assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts")
end
def test_empty_yaml_fixture_with_a_comment_in_it
- assert_not_nil ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies")
+ assert_not_nil ActiveRecord::FixtureSet.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies")
end
def test_nonexistent_fixture_file
@@ -204,19 +204,19 @@ class FixturesTest < ActiveRecord::TestCase
assert Dir[nonexistent_fixture_path+"*"].empty?
assert_raise(Errno::ENOENT) do
- ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', nonexistent_fixture_path)
+ ActiveRecord::FixtureSet.new( Account.connection, "companies", 'Company', nonexistent_fixture_path)
end
end
def test_dirty_dirty_yaml_file
assert_raise(ActiveRecord::Fixture::FormatError) do
- ActiveRecord::Fixtures.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
+ ActiveRecord::FixtureSet.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
end
end
def test_omap_fixtures
assert_nothing_raised do
- fixtures = ActiveRecord::Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
+ fixtures = ActiveRecord::FixtureSet.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
fixtures.each.with_index do |(name, fixture), i|
assert_equal "fixture_no_#{i}", name
@@ -254,7 +254,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
def setup
@instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
- ActiveRecord::Fixtures.reset_cache # make sure tables get reinitialized
+ ActiveRecord::FixtureSet.reset_cache # make sure tables get reinitialized
end
def test_resets_to_min_pk_with_specified_pk_and_sequence
@@ -582,13 +582,13 @@ class FasterFixturesTest < ActiveRecord::TestCase
def load_extra_fixture(name)
fixture = create_fixtures(name).first
- assert fixture.is_a?(ActiveRecord::Fixtures)
+ assert fixture.is_a?(ActiveRecord::FixtureSet)
@loaded_fixtures[fixture.table_name] = fixture
end
def test_cache
- assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
- assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
assert_no_queries do
create_fixtures('categories')
@@ -596,7 +596,7 @@ class FasterFixturesTest < ActiveRecord::TestCase
end
load_extra_fixture('posts')
- assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
+ assert ActiveRecord::FixtureSet.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
self.class.setup_fixture_accessors :posts
assert_equal 'Welcome to the weblog', posts(:welcome).title
end
@@ -606,17 +606,17 @@ class FoxyFixturesTest < ActiveRecord::TestCase
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers, :"admin/accounts", :"admin/users"
def test_identifies_strings
- assert_equal(ActiveRecord::Fixtures.identify("foo"), ActiveRecord::Fixtures.identify("foo"))
- assert_not_equal(ActiveRecord::Fixtures.identify("foo"), ActiveRecord::Fixtures.identify("FOO"))
+ assert_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("foo"))
+ assert_not_equal(ActiveRecord::FixtureSet.identify("foo"), ActiveRecord::FixtureSet.identify("FOO"))
end
def test_identifies_symbols
- assert_equal(ActiveRecord::Fixtures.identify(:foo), ActiveRecord::Fixtures.identify(:foo))
+ assert_equal(ActiveRecord::FixtureSet.identify(:foo), ActiveRecord::FixtureSet.identify(:foo))
end
def test_identifies_consistently
- assert_equal 207281424, ActiveRecord::Fixtures.identify(:ruby)
- assert_equal 1066363776, ActiveRecord::Fixtures.identify(:sapphire_2)
+ assert_equal 207281424, ActiveRecord::FixtureSet.identify(:ruby)
+ assert_equal 1066363776, ActiveRecord::FixtureSet.identify(:sapphire_2)
end
TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
@@ -757,7 +757,7 @@ class FixtureLoadingTest < ActiveRecord::TestCase
end
class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
- ActiveRecord::Fixtures.reset_cache
+ ActiveRecord::FixtureSet.reset_cache
set_fixture_class :randomly_named_a9 =>
ClassNameThatDoesNotFollowCONVENTIONS,
@@ -782,7 +782,7 @@ class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
end
def test_table_name_is_defined_in_the_model
- assert_equal 'randomly_named_table', ActiveRecord::Fixtures::all_loaded_fixtures["admin/randomly_named_a9"].table_name
+ assert_equal 'randomly_named_table', ActiveRecord::FixtureSet::all_loaded_fixtures["admin/randomly_named_a9"].table_name
assert_equal 'randomly_named_table', Admin::ClassNameThatDoesNotFollowCONVENTIONS.table_name
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index f39111ba77..cff6689c15 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -80,7 +80,7 @@ class ActiveSupport::TestCase
self.use_transactional_fixtures = true
def create_fixtures(*fixture_set_names, &block)
- ActiveRecord::Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
+ ActiveRecord::FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, fixture_class_names, &block)
end
end
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
new file mode 100644
index 0000000000..1209f5460f
--- /dev/null
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -0,0 +1,350 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/customer'
+
+class MultiParameterAttributeTest < ActiveRecord::TestCase
+ fixtures :topics
+
+ def setup
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ ActiveRecord::Base.default_timezone = :local
+ Time.zone = nil
+ end
+
+ def test_multiparameter_attributes_on_date
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_year
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_month
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_day
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_day_and_year
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_day_and_month
+ attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_empty_year_and_month
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # note that extra #to_date call allows test to pass for Oracle, which
+ # treats dates/times the same
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_date_with_all_empty
+ attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.last_read
+ end
+
+ def test_multiparameter_attributes_on_time
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_with_no_date
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_with_invalid_time_params
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_with_old_date
+ attributes = {
+ "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
+ assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
+ end
+
+ def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ attributes = {
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ end
+ assert_equal("written_on", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
+ "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
+ attributes = {
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
+ attributes = {
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "16", "written_on(5i)" => "24"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+ def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
+ attributes = {
+ "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
+ "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_nil topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_with_utc
+ ActiveRecord::Base.default_timezone = :utc
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+ end
+
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ ActiveRecord::Base.default_timezone = :utc
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
+ assert_equal Time.zone, topic.written_on.time_zone
+ end
+
+ def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
+ end
+
+ def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ ActiveRecord::Base.default_timezone = :utc
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ Topic.skip_time_zone_conversion_for_attributes = [:written_on]
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal false, topic.written_on.respond_to?(:time_zone)
+ ensure
+ Topic.skip_time_zone_conversion_for_attributes = []
+ end
+
+ # Oracle, and Sybase do not have a TIME datatype.
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
+ def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ ActiveRecord::Base.default_timezone = :utc
+ Time.zone = ActiveSupport::TimeZone[-28800]
+ attributes = {
+ "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
+ "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
+ assert topic.bonus_time.utc?
+ end
+ end
+
+ def test_multiparameter_attributes_on_time_with_empty_seconds
+ attributes = {
+ "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
+ "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
+ }
+ topic = Topic.find(1)
+ topic.attributes = attributes
+ assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ end
+
+ def test_multiparameter_attributes_setting_time_attribute
+ return skip "Oracle does not have TIME data type" if current_adapter? :OracleAdapter
+
+ topic = Topic.new( "bonus_time(4i)"=> "01", "bonus_time(5i)" => "05" )
+ assert_equal 1, topic.bonus_time.hour
+ assert_equal 5, topic.bonus_time.min
+ end
+
+ def test_multiparameter_attributes_setting_date_attribute
+ topic = Topic.new( "written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11" )
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ end
+
+ def test_multiparameter_attributes_setting_date_and_time_attribute
+ topic = Topic.new(
+ "written_on(1i)" => "1952",
+ "written_on(2i)" => "3",
+ "written_on(3i)" => "11",
+ "written_on(4i)" => "13",
+ "written_on(5i)" => "55")
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ assert_equal 13, topic.written_on.hour
+ assert_equal 55, topic.written_on.min
+ end
+
+ def test_multiparameter_attributes_setting_time_but_not_date_on_date_field
+ assert_raise( ActiveRecord::MultiparameterAssignmentErrors ) do
+ Topic.new( "written_on(4i)" => "13", "written_on(5i)" => "55" )
+ end
+ end
+
+ def test_multiparameter_assignment_of_aggregation
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
+ customer.attributes = attributes
+ assert_equal address, customer.address
+ end
+
+ def test_multiparameter_assignment_of_aggregation_out_of_order
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
+ customer.attributes = attributes
+ assert_equal address, customer.address
+ end
+
+ def test_multiparameter_assignment_of_aggregation_with_missing_values
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(2)" => address.city, "address(3)" => address.country }
+ customer.attributes = attributes
+ end
+ assert_equal("address", ex.errors[0].attribute)
+ end
+
+ def test_multiparameter_assignment_of_aggregation_with_blank_values
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
+ customer.attributes = attributes
+ assert_equal Address.new(nil, "The City", "The Country"), customer.address
+ end
+
+ def test_multiparameter_assignment_of_aggregation_with_large_index
+ ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
+ customer = Customer.new
+ address = Address.new("The Street", "The City", "The Country")
+ attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
+ customer.attributes = attributes
+ end
+
+ assert_equal("address", ex.errors[0].attribute)
+ end
+end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index dc47d40f41..562ca8d9ff 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -136,7 +136,7 @@ class StoreTest < ActiveRecord::TestCase
end
test "all stored attributes are returned" do
- assert_equal [:color, :homepage, :favorite_food, :phone_number], Admin::User.stored_attributes[:settings]
+ assert_equal [:color, :homepage, :favorite_food], Admin::User.stored_attributes[:settings]
end
test "stores_attributes are class level settings" do
diff --git a/activerecord/test/fixtures/mateys.yml b/activerecord/test/fixtures/mateys.yml
index d3690955fc..3f0405aaf8 100644
--- a/activerecord/test/fixtures/mateys.yml
+++ b/activerecord/test/fixtures/mateys.yml
@@ -1,4 +1,4 @@
blackbeard_to_redbeard:
- pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %>
- target_id: <%= ActiveRecord::Fixtures.identify(:redbeard) %>
+ pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %>
+ target_id: <%= ActiveRecord::FixtureSet.identify(:redbeard) %>
weight: 10
diff --git a/activerecord/test/fixtures/parrots_pirates.yml b/activerecord/test/fixtures/parrots_pirates.yml
index 66472243c7..e1a301b91a 100644
--- a/activerecord/test/fixtures/parrots_pirates.yml
+++ b/activerecord/test/fixtures/parrots_pirates.yml
@@ -1,7 +1,7 @@
george_blackbeard:
- parrot_id: <%= ActiveRecord::Fixtures.identify(:george) %>
- pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %>
+ parrot_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
+ pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %>
louis_blackbeard:
- parrot_id: <%= ActiveRecord::Fixtures.identify(:louis) %>
- pirate_id: <%= ActiveRecord::Fixtures.identify(:blackbeard) %>
+ parrot_id: <%= ActiveRecord::FixtureSet.identify(:louis) %>
+ pirate_id: <%= ActiveRecord::FixtureSet.identify(:blackbeard) %>
diff --git a/activerecord/test/fixtures/peoples_treasures.yml b/activerecord/test/fixtures/peoples_treasures.yml
index a72b190d0c..46abe50e6c 100644
--- a/activerecord/test/fixtures/peoples_treasures.yml
+++ b/activerecord/test/fixtures/peoples_treasures.yml
@@ -1,3 +1,3 @@
michael_diamond:
- rich_person_id: <%= ActiveRecord::Fixtures.identify(:michael) %>
- treasure_id: <%= ActiveRecord::Fixtures.identify(:diamond) %>
+ rich_person_id: <%= ActiveRecord::FixtureSet.identify(:michael) %>
+ treasure_id: <%= ActiveRecord::FixtureSet.identify(:diamond) %>
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 35170faa76..467f3ccd39 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,7 +1,7 @@
class Admin::User < ActiveRecord::Base
belongs_to :account
store :settings, :accessors => [ :color, :homepage ]
- store_accessor :settings, :favorite_food, :phone_number
+ store_accessor :settings, :favorite_food
store :preferences, :accessors => [ :remember_login ]
store :json_data, :accessors => [ :height, :weight ], :coder => JSON
store :json_data_empty, :accessors => [ :is_a_good_guy ], :coder => JSON
diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb
index 4bdb36ea46..bcac4738a3 100644
--- a/activerecord/test/models/subscription.rb
+++ b/activerecord/test/models/subscription.rb
@@ -1,4 +1,4 @@
class Subscription < ActiveRecord::Base
- belongs_to :subscriber
+ belongs_to :subscriber, :counter_cache => :books_count
belongs_to :book
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 798ea20efc..2e4ec96933 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -621,6 +621,7 @@ ActiveRecord::Schema.define do
create_table :subscribers, :force => true, :id => false do |t|
t.string :nick, :null => false
t.string :name
+ t.column :books_count, :integer, :null => false, :default => 0
end
add_index :subscribers, :nick, :unique => true
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 2df3d1f69b..0a12ba6cdd 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,5 +1,7 @@
## Rails 4.0.0 (unreleased) ##
+* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. *Brian Durand*
+
* Tests tag the Rails log with the current test class and test case:
[SessionsControllerTest] [test_0002_sign in] Processing by SessionsController#create as HTML
@@ -237,4 +239,6 @@
* Remove deprecated ActiveSupport::JSON::Variable. *Erich Menge*
+* Optimize log subscribers to check log level before doing any processing. *Brian Durand*
+
Please check [3-2-stable](https://github.com/rails/rails/blob/3-2-stable/activesupport/CHANGELOG.md) for previous changes.
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index 836bc2f9cf..30f4ded005 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
s.add_dependency('i18n', '~> 0.6')
s.add_dependency('multi_json', '~> 1.3')
s.add_dependency('tzinfo', '~> 0.3.33')
- s.add_dependency('minitest', '~> 3.2')
+ s.add_dependency('minitest', '~> 4.1')
end
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index 41d77ab6c1..4e397ea110 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -48,6 +48,7 @@ module ActiveSupport
autoload :Gzip
autoload :Inflector
autoload :JSON
+ autoload :KeyGenerator
autoload :MessageEncryptor
autoload :MessageVerifier
autoload :Multibyte
diff --git a/activesupport/lib/active_support/backtrace_cleaner.rb b/activesupport/lib/active_support/backtrace_cleaner.rb
index 53d05c3817..f1aff8a8e3 100644
--- a/activesupport/lib/active_support/backtrace_cleaner.rb
+++ b/activesupport/lib/active_support/backtrace_cleaner.rb
@@ -34,7 +34,7 @@ module ActiveSupport
# Returns the backtrace after all filters and silencers have been run
# against it. Filters run first, then silencers.
def clean(backtrace, kind = :silent)
- filtered = filter(backtrace)
+ filtered = filter_backtrace(backtrace)
case kind
when :silent
@@ -45,6 +45,7 @@ module ActiveSupport
filtered
end
end
+ alias :filter :clean
# Adds a filter from the block provided. Each line in the backtrace will be
# mapped against this filter.
@@ -76,7 +77,7 @@ module ActiveSupport
end
private
- def filter(backtrace)
+ def filter_backtrace(backtrace)
@filters.each do |f|
backtrace = backtrace.map { |line| f.call(line) }
end
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index f98ba16cdd..732ab4b7df 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -284,7 +284,9 @@ module ActiveSupport
end
if entry && entry.expired?
race_ttl = options[:race_condition_ttl].to_i
- if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
+ if race_ttl && (Time.now - entry.expires_at <= race_ttl)
+ # When an entry has :race_condition_ttl defined, put the stale entry back into the cache
+ # for a brief period while the entry is begin recalculated.
entry.expires_at = Time.now + race_ttl
write_entry(key, entry, :expires_in => race_ttl * 2)
else
@@ -532,102 +534,122 @@ module ActiveSupport
end
end
- # Entry that is put into caches. It supports expiration time on entries and
- # can compress values to save space in the cache.
- class Entry
- attr_reader :created_at, :expires_in
-
+ # This class is used to represent cache entries. Cache entries have a value and an optional
+ # expiration time. The expiration time is used to support the :race_condition_ttl option
+ # on the cache.
+ #
+ # Since cache entries in most instances will be serialized, the internals of this class are highly optimized
+ # using short instance variable names that are lazily defined.
+ class Entry # :nodoc:
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
- class << self
- # Create an entry with internal attributes set. This method is intended
- # to be used by implementations that store cache entries in a native
- # format instead of as serialized Ruby objects.
- def create(raw_value, created_at, options = {})
- entry = new(nil)
- entry.instance_variable_set(:@value, raw_value)
- entry.instance_variable_set(:@created_at, created_at.to_f)
- entry.instance_variable_set(:@compressed, options[:compressed])
- entry.instance_variable_set(:@expires_in, options[:expires_in])
- entry
- end
- end
-
# Create a new cache entry for the specified value. Options supported are
# +:compress+, +:compress_threshold+, and +:expires_in+.
def initialize(value, options = {})
- @compressed = false
- @expires_in = options[:expires_in]
- @expires_in = @expires_in.to_f if @expires_in
- @created_at = Time.now.to_f
- if value.nil?
- @value = nil
+ if should_compress?(value, options)
+ @v = compress(value)
+ @c = true
else
- @value = Marshal.dump(value)
- if should_compress?(@value, options)
- @value = Zlib::Deflate.deflate(@value)
- @compressed = true
- end
+ @v = value
end
- end
-
- # Get the raw value. This value may be serialized and compressed.
- def raw_value
- @value
- end
-
- # Get the value stored in the cache.
- def value
- # If the original value was exactly false @value is still true because
- # it is marshalled and eventually compressed. Both operations yield
- # strings.
- if @value
- Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
+ if expires_in = options[:expires_in]
+ @x = (Time.now + expires_in).to_i
end
end
- def compressed?
- @compressed
+ def value
+ convert_version_3_entry! if defined?(@value)
+ compressed? ? uncompress(@v) : @v
end
# Check if the entry is expired. The +expires_in+ parameter can override
# the value set when the entry was created.
def expired?
- @expires_in && @created_at + @expires_in <= Time.now.to_f
- end
-
- # Set a new time when the entry will expire.
- def expires_at=(time)
- if time
- @expires_in = time.to_f - @created_at
+ convert_version_3_entry! if defined?(@value)
+ if defined?(@x)
+ @x && @x < Time.now.to_i
else
- @expires_in = nil
+ false
end
end
- # Seconds since the epoch when the entry will expire.
def expires_at
- @expires_in ? @created_at + @expires_in : nil
+ Time.at(@x) if defined?(@x)
+ end
+
+ def expires_at=(value)
+ @x = value.to_i
end
# Returns the size of the cached value. This could be less than
# <tt>value.size</tt> if the data is compressed.
def size
- if @value.nil?
- 0
+ if defined?(@s)
+ @s
else
- @value.bytesize
+ case value
+ when NilClass
+ 0
+ when String
+ @v.bytesize
+ else
+ @s = Marshal.dump(@v).bytesize
+ end
+ end
+ end
+
+ # Duplicate the value in a class. This is used by cache implementations that don't natively
+ # serialize entries to protect against accidental cache modifications.
+ def dup_value!
+ convert_version_3_entry! if defined?(@value)
+ if @v && !compressed? && !(@v.is_a?(Numeric) || @v == true || @v == false)
+ if @v.is_a?(String)
+ @v = @v.dup
+ else
+ @v = Marshal.load(Marshal.dump(@v))
+ end
end
end
private
- def should_compress?(serialized_value, options)
- if options[:compress]
+ def should_compress?(value, options)
+ if value && options[:compress]
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
- return true if serialized_value.size >= compress_threshold
+ serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
+ return true if serialized_value_size >= compress_threshold
end
false
end
+
+ def compressed?
+ defined?(@c) ? @c : false
+ end
+
+ def compress(value)
+ Zlib::Deflate.deflate(Marshal.dump(value))
+ end
+
+ def uncompress(value)
+ Marshal.load(Zlib::Inflate.inflate(value))
+ end
+
+ # The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
+ # to ensure that cache entries created under the old version still work with the new class definition.
+ def convert_version_3_entry!
+ if defined?(@value)
+ @v = @value
+ remove_instance_variable(:@value)
+ end
+ if defined?(@compressed)
+ @c = @compressed
+ remove_instance_variable(:@compressed)
+ end
+ if defined?(@expires_in) && defined?(@created_at) && @expires_in && @created_at
+ @x = (@created_at + @expires_in).to_i
+ remove_instance_variable(:@created_at)
+ remove_instance_variable(:@expires_in)
+ end
+ end
end
end
end
diff --git a/activesupport/lib/active_support/cache/memory_store.rb b/activesupport/lib/active_support/cache/memory_store.rb
index 7fd5e3b53d..4d26fb7e42 100644
--- a/activesupport/lib/active_support/cache/memory_store.rb
+++ b/activesupport/lib/active_support/cache/memory_store.rb
@@ -135,6 +135,7 @@ module ActiveSupport
end
def write_entry(key, entry, options) # :nodoc:
+ entry.dup_value!
synchronize do
old_entry = @data[key]
return false if @data.key?(key) && options[:unless_exist]
diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb
index 7f37c459c1..6a0c4a015a 100644
--- a/activesupport/lib/active_support/core_ext/array/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/array/conversions.rb
@@ -1,6 +1,8 @@
require 'active_support/xml_mini'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
class Array
# Converts the array to a comma-separated sentence where the last element is
diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb
index a7551d9c64..02ae57b4a6 100644
--- a/activesupport/lib/active_support/core_ext/date/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/date/calculations.rb
@@ -28,7 +28,7 @@ class Date
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
def find_beginning_of_week!(week_start)
- raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.keys.include?(week_start)
+ raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
week_start
end
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 5ba8197006..828f03ad62 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -1,8 +1,10 @@
require 'active_support/xml_mini'
require 'active_support/time'
+require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/object/to_param'
+require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/reverse_merge'
-require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/inflections'
class Hash
diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb
index ec2157221f..ab01d7787a 100644
--- a/activesupport/lib/active_support/core_ext/object.rb
+++ b/activesupport/lib/active_support/core_ext/object.rb
@@ -1,14 +1,11 @@
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/deep_dup'
-require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/object/inclusion'
-
-require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
-
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/object/to_query'
+require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/with_options'
diff --git a/activesupport/lib/active_support/core_ext/object/conversions.rb b/activesupport/lib/active_support/core_ext/object/conversions.rb
deleted file mode 100644
index 540f7aadb0..0000000000
--- a/activesupport/lib/active_support/core_ext/object/conversions.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'active_support/core_ext/object/to_param'
-require 'active_support/core_ext/object/to_query'
-require 'active_support/core_ext/array/conversions'
-require 'active_support/core_ext/hash/conversions'
diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb
index b3f5fde335..6c15fffc0f 100644
--- a/activesupport/lib/active_support/deprecation.rb
+++ b/activesupport/lib/active_support/deprecation.rb
@@ -1,15 +1,24 @@
-require 'active_support/core_ext/module/deprecation'
-require 'active_support/deprecation/instance_delegator'
-require 'active_support/deprecation/behaviors'
-require 'active_support/deprecation/reporting'
-require 'active_support/deprecation/method_wrappers'
-require 'active_support/deprecation/proxy_wrappers'
require 'singleton'
module ActiveSupport
# \Deprecation specifies the API used by Rails to deprecate methods, instance
# variables, objects and constants.
class Deprecation
+ # active_support.rb sets an autoload for ActiveSupport::Deprecation.
+ #
+ # If these requires were at the top of the file the constant would not be
+ # defined by the time their files were loaded. Since some of them reopen
+ # ActiveSupport::Deprecation its autoload would be triggered, resulting in
+ # a circular require warning for active_support/deprecation.rb.
+ #
+ # So, we define the constant first, and load dependencies later.
+ require 'active_support/deprecation/instance_delegator'
+ require 'active_support/deprecation/behaviors'
+ require 'active_support/deprecation/reporting'
+ require 'active_support/deprecation/method_wrappers'
+ require 'active_support/deprecation/proxy_wrappers'
+ require 'active_support/core_ext/module/deprecation'
+
include Singleton
include InstanceDelegator
include Behavior
diff --git a/activesupport/lib/active_support/key_generator.rb b/activesupport/lib/active_support/key_generator.rb
new file mode 100644
index 0000000000..04d170f801
--- /dev/null
+++ b/activesupport/lib/active_support/key_generator.rb
@@ -0,0 +1,23 @@
+require 'openssl'
+
+module ActiveSupport
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
+ # It can be used to derive a number of keys for various purposes from a given secret.
+ # This lets rails applications have a single secure secret, but avoid reusing that
+ # key in multiple incompatible contexts.
+ class KeyGenerator
+ def initialize(secret, options = {})
+ @secret = secret
+ # The default iterations are higher than required for our key derivation uses
+ # on the off chance someone uses this for password storage
+ @iterations = options[:iterations] || 2**16
+ end
+
+ # Returns a derived key suitable for use. The default key_size is chosen
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
+ # i.e. OpenSSL::Digest::SHA1#block_length
+ def generate_key(salt, key_size=64)
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
+ end
+ end
+end
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index 71cd9d81b3..febf0eeeff 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -224,25 +224,22 @@ module CacheStoreBehavior
end
def test_read_multi_with_expires
- @cache.write('foo', 'bar', :expires_in => 0.001)
+ time = Time.now
+ @cache.write('foo', 'bar', :expires_in => 10)
@cache.write('fu', 'baz')
@cache.write('fud', 'biz')
- sleep(0.002)
+ Time.stubs(:now).returns(time + 11)
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
end
def test_read_and_write_compressed_small_data
@cache.write('foo', 'bar', :compress => true)
- raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
assert_equal 'bar', @cache.read('foo')
- assert_equal 'bar', Marshal.load(raw_value)
end
def test_read_and_write_compressed_large_data
@cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
- raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
assert_equal 'bar', @cache.read('foo')
- assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
end
def test_read_and_write_compressed_nil
@@ -301,14 +298,6 @@ module CacheStoreBehavior
assert !@cache.exist?('foo')
end
- def test_read_should_return_a_different_object_id_each_time_it_is_called
- @cache.write('foo', 'bar')
- assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
- value = @cache.read('foo')
- value << 'bingo'
- assert_not_equal value, @cache.read('foo')
- end
-
def test_original_store_objects_should_not_be_immutable
bar = 'bar'
@cache.write('foo', bar)
@@ -363,7 +352,7 @@ module CacheStoreBehavior
rescue ArgumentError
end
assert_equal "bar", @cache.read('foo')
- Time.stubs(:now).returns(time + 71)
+ Time.stubs(:now).returns(time + 91)
assert_nil @cache.read('foo')
end
@@ -627,7 +616,7 @@ end
class MemoryStoreTest < ActiveSupport::TestCase
def setup
- @record_size = Marshal.dump("aaaaaaaaaa").bytesize
+ @record_size = ActiveSupport::Cache::Entry.new("aaaaaaaaaa").size
@cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10)
end
@@ -646,9 +635,9 @@ class MemoryStoreTest < ActiveSupport::TestCase
@cache.prune(@record_size * 3)
assert @cache.exist?(5)
assert @cache.exist?(4)
- assert !@cache.exist?(3)
+ assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert !@cache.exist?(1), "no entry"
end
def test_prune_size_on_write
@@ -670,12 +659,12 @@ class MemoryStoreTest < ActiveSupport::TestCase
assert @cache.exist?(9)
assert @cache.exist?(8)
assert @cache.exist?(7)
- assert !@cache.exist?(6)
- assert !@cache.exist?(5)
+ assert !@cache.exist?(6), "no entry"
+ assert !@cache.exist?(5), "no entry"
assert @cache.exist?(4)
- assert !@cache.exist?(3)
+ assert !@cache.exist?(3), "no entry"
assert @cache.exist?(2)
- assert !@cache.exist?(1)
+ assert !@cache.exist?(1), "no entry"
end
def test_pruning_is_capped_at_a_max_time
@@ -764,6 +753,14 @@ class MemCacheStoreTest < ActiveSupport::TestCase
assert_equal [], cache.read("foo")
end
end
+
+ def test_read_should_return_a_different_object_id_each_time_it_is_called
+ @cache.write('foo', 'bar')
+ assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
+ value = @cache.read('foo')
+ value << 'bingo'
+ assert_not_equal value, @cache.read('foo')
+ end
end
class NullStoreTest < ActiveSupport::TestCase
@@ -844,15 +841,6 @@ class CacheStoreLoggerTest < ActiveSupport::TestCase
end
class CacheEntryTest < ActiveSupport::TestCase
- def test_create_raw_entry
- time = Time.now
- entry = ActiveSupport::Cache::Entry.create("raw", time, :compress => false, :expires_in => 300)
- assert_equal "raw", entry.raw_value
- assert_equal time.to_f, entry.created_at
- assert !entry.compressed?
- assert_equal 300, entry.expires_in
- end
-
def test_expired
entry = ActiveSupport::Cache::Entry.new("value")
assert !entry.expired?, 'entry not expired'
@@ -864,16 +852,43 @@ class CacheEntryTest < ActiveSupport::TestCase
end
def test_compress_values
- entry = ActiveSupport::Cache::Entry.new("value", :compress => true, :compress_threshold => 1)
- assert_equal "value", entry.value
- assert entry.compressed?
- assert_equal "value", Marshal.load(Zlib::Inflate.inflate(entry.raw_value))
+ value = "value" * 100
+ entry = ActiveSupport::Cache::Entry.new(value, :compress => true, :compress_threshold => 1)
+ assert_equal value, entry.value
+ assert(value.bytesize > entry.size, "value is compressed")
end
def test_non_compress_values
- entry = ActiveSupport::Cache::Entry.new("value")
- assert_equal "value", entry.value
- assert_equal "value", Marshal.load(entry.raw_value)
- assert !entry.compressed?
+ value = "value" * 100
+ entry = ActiveSupport::Cache::Entry.new(value)
+ assert_equal value, entry.value
+ assert_equal value.bytesize, entry.size
+ end
+
+ def test_restoring_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, "hello")
+ version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ assert_equal false, entry.expired?
+ end
+
+ def test_restoring_compressed_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, Zlib::Deflate.deflate(Marshal.dump("hello")))
+ version_3_entry.instance_variable_set(:@compressed, true)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ end
+
+ def test_restoring_expired_version_3_entries
+ version_3_entry = ActiveSupport::Cache::Entry.allocate
+ version_3_entry.instance_variable_set(:@value, "hello")
+ version_3_entry.instance_variable_set(:@created_at, Time.now - 60)
+ version_3_entry.instance_variable_set(:@expires_in, 58.9)
+ entry = Marshal.load(Marshal.dump(version_3_entry))
+ assert_equal "hello", entry.value
+ assert_equal true, entry.expired?
end
end
diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb
index 9dfa2cbf11..0b33a63460 100644
--- a/activesupport/test/core_ext/array_ext_test.rb
+++ b/activesupport/test/core_ext/array_ext_test.rb
@@ -1,7 +1,6 @@
require 'abstract_unit'
require 'active_support/core_ext/array'
require 'active_support/core_ext/big_decimal'
-require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions
require 'active_support/hash_with_indifferent_access'
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 01934dd2c3..53ea2aad16 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -3,7 +3,6 @@ require 'active_support/core_ext/hash'
require 'bigdecimal'
require 'active_support/core_ext/string/access'
require 'active_support/ordered_hash'
-require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/deep_dup'
require 'active_support/inflections'
diff --git a/activesupport/test/key_generator_test.rb b/activesupport/test/key_generator_test.rb
new file mode 100644
index 0000000000..525082d313
--- /dev/null
+++ b/activesupport/test/key_generator_test.rb
@@ -0,0 +1,32 @@
+require 'abstract_unit'
+
+begin
+ require 'openssl'
+ OpenSSL::PKCS5
+rescue LoadError, NameError
+ $stderr.puts "Skipping KeyGenerator test: broken OpenSSL install"
+else
+
+require 'active_support/time'
+require 'active_support/json'
+
+class KeyGeneratorTest < ActiveSupport::TestCase
+ def setup
+ @secret = SecureRandom.hex(64)
+ @generator = ActiveSupport::KeyGenerator.new(@secret, :iterations=>2)
+ end
+
+ test "Generating a key of the default length" do
+ derived_key = @generator.generate_key("some_salt")
+ assert_kind_of String, derived_key
+ assert_equal OpenSSL::Digest::SHA1.new.block_length, derived_key.length, "Should have generated a key of the default size"
+ end
+
+ test "Generating a key of an alternative length" do
+ derived_key = @generator.generate_key("some_salt", 32)
+ assert_kind_of String, derived_key
+ assert_equal 32, derived_key.length, "Should have generated a key of the right size"
+ end
+end
+
+end
diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb
index ac85ba1f21..14ba4e0076 100644
--- a/activesupport/test/ordered_hash_test.rb
+++ b/activesupport/test/ordered_hash_test.rb
@@ -216,7 +216,6 @@ class OrderedHashTest < ActiveSupport::TestCase
alternate = ActiveSupport::OrderedHash[ [
[1, 2],
[3, 4],
- "bad key value pair",
[ 'missing value' ]
]]
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index d19aab1438..9516556844 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -100,11 +100,11 @@ class AssertPresentTest < ActiveSupport::TestCase
BLANK = [ EmptyTrue.new, nil, false, '', ' ', " \n\t \r ", [], {} ]
NOT_BLANK = [ EmptyFalse.new, Object.new, true, 0, 1, 'x', [nil], { nil => 0 } ]
- def test_assert_blank_true
+ def test_assert_present_true
NOT_BLANK.each { |v| assert_present v }
end
- def test_assert_blank_false
+ def test_assert_present_false
BLANK.each { |v|
begin
assert_present v
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md
new file mode 100644
index 0000000000..a1a480e911
--- /dev/null
+++ b/guides/CHANGELOG.md
@@ -0,0 +1,5 @@
+## Rails 4.0.0 (unreleased) ##
+
+* Guides updated to reflect new test locations. *Mike Moore*
+
+* Guides have a responsive design. *Joe Fiorini*
diff --git a/guides/assets/javascripts/guides.js b/guides/assets/javascripts/guides.js
index c4e4d459ea..7e494fb6d8 100644
--- a/guides/assets/javascripts/guides.js
+++ b/guides/assets/javascripts/guides.js
@@ -5,3 +5,53 @@ function guideMenu(){
document.getElementById('guides').style.display = "none";
}
}
+
+$.fn.selectGuide = function(guide){
+ $("select", this).val(guide);
+}
+
+guidesIndex = {
+ bind: function(){
+ var currentGuidePath = window.location.pathname;
+ var currentGuide = currentGuidePath.substring(currentGuidePath.lastIndexOf("/")+1);
+ $(".guides-index-small").
+ on("change", "select", guidesIndex.navigate).
+ selectGuide(currentGuide);
+ $(".more-info-button:visible").click(function(e){
+ e.stopPropagation();
+ if($(".more-info-links").is(":visible")){
+ $(".more-info-links").addClass("s-hidden").unwrap();
+ } else {
+ $(".more-info-links").wrap("<div class='more-info-container'></div>").removeClass("s-hidden");
+ }
+ $(document).on("click", function(e){
+ var $button = $(".more-info-button");
+ var element;
+
+ // Cross browser find the element that had the event
+ if (e.target) element = e.target;
+ else if (e.srcElement) element = e.srcElement;
+
+ // Defeat the older Safari bug:
+ // http://www.quirksmode.org/js/events_properties.html
+ if (element.nodeType == 3) element = element.parentNode;
+
+ var $element = $(element);
+
+ var $container = $element.parents(".more-info-container");
+
+ // We've captured a click outside the popup
+ if($container.length == 0){
+ $container = $button.next(".more-info-container");
+ $container.find(".more-info-links").addClass("s-hidden").unwrap();
+ $(document).off("click");
+ }
+ });
+ });
+ },
+ navigate: function(e){
+ var $list = $(e.target);
+ url = $list.val();
+ window.location = url;
+ }
+}
diff --git a/guides/assets/javascripts/jquery.min.js b/guides/assets/javascripts/jquery.min.js
new file mode 100644
index 0000000000..93adea19fd
--- /dev/null
+++ b/guides/assets/javascripts/jquery.min.js
@@ -0,0 +1,4 @@
+/*! jQuery v1.7.2 jquery.com | jquery.org/license */
+(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"<!doctype html>":"")+"<html><body>"),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function ca(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function b_(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bD.test(a)?d(a,e):b_(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&f.type(b)==="object")for(var e in b)b_(a+"["+e+"]",b[e],c,d);else d(a,b)}function b$(a,c){var d,e,g=f.ajaxSettings.flatOptions||{};for(d in c)c[d]!==b&&((g[d]?a:e||(e={}))[d]=c[d]);e&&f.extend(!0,a,e)}function bZ(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bS,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bZ(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bZ(a,c,d,e,"*",g));return l}function bY(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bO),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bB(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?1:0,g=4;if(d>0){if(c!=="border")for(;e<g;e+=2)c||(d-=parseFloat(f.css(a,"padding"+bx[e]))||0),c==="margin"?d+=parseFloat(f.css(a,c+bx[e]))||0:d-=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0;return d+"px"}d=by(a,b);if(d<0||d==null)d=a.style[b];if(bt.test(d))return d;d=parseFloat(d)||0;if(c)for(;e<g;e+=2)d+=parseFloat(f.css(a,"padding"+bx[e]))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+bx[e]+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+bx[e]))||0);return d+"px"}function bo(a){var b=c.createElement("div");bh.appendChild(b),b.innerHTML=a.outerHTML;return b.firstChild}function bn(a){var b=(a.nodeName||"").toLowerCase();b==="input"?bm(a):b!=="script"&&typeof a.getElementsByTagName!="undefined"&&f.grep(a.getElementsByTagName("input"),bm)}function bm(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bl(a){return typeof a.getElementsByTagName!="undefined"?a.getElementsByTagName("*"):typeof a.querySelectorAll!="undefined"?a.querySelectorAll("*"):[]}function bk(a,b){var c;b.nodeType===1&&(b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase(),c==="object"?b.outerHTML=a.outerHTML:c!=="input"||a.type!=="checkbox"&&a.type!=="radio"?c==="option"?b.selected=a.defaultSelected:c==="input"||c==="textarea"?b.defaultValue=a.defaultValue:c==="script"&&b.text!==a.text&&(b.text=a.text):(a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value)),b.removeAttribute(f.expando),b.removeAttribute("_submit_attached"),b.removeAttribute("_change_attached"))}function bj(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c,d,e,g=f._data(a),h=f._data(b,g),i=g.events;if(i){delete h.handle,h.events={};for(c in i)for(d=0,e=i[c].length;d<e;d++)f.event.add(b,c,i[c][d])}h.data&&(h.data=f.extend({},h.data))}}function bi(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function U(a){var b=V.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function T(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(O.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c<d;c++)b[a[c]]=!0;return b}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b,c){var d;if(b){if(H)return H.call(b,a,c);d=b.length,c=c?c<0?Math.max(0,d+c):c:0;for(;c<d;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h,i){var j,k=d==null,l=0,m=a.length;if(d&&typeof d=="object"){for(l in d)e.access(a,c,l,d[l],1,h,f);g=1}else if(f!==b){j=i===b&&e.isFunction(f),k&&(j?(j=c,c=function(a,b,c){return j.call(e(a),c)}):(c.call(a,f),c=null));if(c)for(;l<m;l++)c(a[l],d,j?f.call(a[l],l,c(a[l],d)):f,i);g=1}return g?a:k?c.call(a):m?c(a[0],d):h},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g={};f.Callbacks=function(a){a=a?g[a]||h(a):{};var c=[],d=[],e,i,j,k,l,m,n=function(b){var d,e,g,h,i;for(d=0,e=b.length;d<e;d++)g=b[d],h=f.type(g),h==="array"?n(g):h==="function"&&(!a.unique||!p.has(g))&&c.push(g)},o=function(b,f){f=f||[],e=!a.memory||[b,f],i=!0,j=!0,m=k||0,k=0,l=c.length;for(;c&&m<l;m++)if(c[m].apply(b,f)===!1&&a.stopOnFalse){e=!0;break}j=!1,c&&(a.once?e===!0?p.disable():c=[]:d&&d.length&&(e=d.shift(),p.fireWith(e[0],e[1])))},p={add:function(){if(c){var a=c.length;n(arguments),j?l=c.length:e&&e!==!0&&(k=a,o(e[0],e[1]))}return this},remove:function(){if(c){var b=arguments,d=0,e=b.length;for(;d<e;d++)for(var f=0;f<c.length;f++)if(b[d]===c[f]){j&&f<=l&&(l--,f<=m&&m--),c.splice(f--,1);if(a.unique)break}}return this},has:function(a){if(c){var b=0,d=c.length;for(;b<d;b++)if(a===c[b])return!0}return!1},empty:function(){c=[];return this},disable:function(){c=d=e=b;return this},disabled:function(){return!c},lock:function(){d=b,(!e||e===!0)&&p.disable();return this},locked:function(){return!d},fireWith:function(b,c){d&&(j?a.once||d.push([b,c]):(!a.once||!e)&&o(b,c));return this},fire:function(){p.fireWith(this,arguments);return this},fired:function(){return!!i}};return p};var i=[].slice;f.extend({Deferred:function(a){var b=f.Callbacks("once memory"),c=f.Callbacks("once memory"),d=f.Callbacks("memory"),e="pending",g={resolve:b,reject:c,notify:d},h={done:b.add,fail:c.add,progress:d.add,state:function(){return e},isResolved:b.fired,isRejected:c.fired,then:function(a,b,c){i.done(a).fail(b).progress(c);return this},always:function(){i.done.apply(i,arguments).fail.apply(i,arguments);return this},pipe:function(a,b,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[b,"reject"],progress:[c,"notify"]},function(a,b){var c=b[0],e=b[1],g;f.isFunction(c)?i[a](function(){g=c.apply(this,arguments),g&&f.isFunction(g.promise)?g.promise().then(d.resolve,d.reject,d.notify):d[e+"With"](this===i?d:this,[g])}):i[a](d[e])})}).promise()},promise:function(a){if(a==null)a=h;else for(var b in h)a[b]=h[b];return a}},i=h.promise({}),j;for(j in g)i[j]=g[j].fire,i[j+"With"]=g[j].fireWith;i.done(function(){e="resolved"},c.disable,d.lock).fail(function(){e="rejected"},b.disable,d.lock),a&&a.call(i,i);return i},when:function(a){function m(a){return function(b){e[a]=arguments.length>1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c<d;c++)b[c]&&b[c].promise&&f.isFunction(b[c].promise)?b[c].promise().then(l(c),j.reject,m(c)):--g;g||j.resolveWith(j,b)}else j!==a&&j.resolveWith(j,d?[a]:[]);return k}}),f.support=function(){var b,d,e,g,h,i,j,k,l,m,n,o,p=c.createElement("div"),q=c.documentElement;p.setAttribute("className","t"),p.innerHTML=" <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav></:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="<div "+n+"display:block;'><div style='"+t+"0;display:block;overflow:hidden;'></div></div>"+"<table "+n+"' cellpadding='0' cellspacing='0'>"+"<tr><td></td></tr></table>",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="<table><tr><td style='"+t+"0;display:none'></td><td>t</td></tr></table>",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="<div style='width:5px;'></div>",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e<g;e++)delete d[b[e]];if(!(c?m:f.isEmptyObject)(d))return}}if(!c){delete j[k].data;if(!m(j[k]))return}f.support.deleteExpando||!j.setInterval?delete j[k]:j[k]=null,i&&(f.support.deleteExpando?delete a[h]:a.removeAttribute?a.removeAttribute(h):a[h]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d,e,g,h,i,j=this[0],k=0,m=null;if(a===b){if(this.length){m=f.data(j);if(j.nodeType===1&&!f._data(j,"parsedAttrs")){g=j.attributes;for(i=g.length;k<i;k++)h=g[k].name,h.indexOf("data-")===0&&(h=f.camelCase(h.substring(5)),l(j,h,m[h]));f._data(j,"parsedAttrs",!0)}}return m}if(typeof a=="object")return this.each(function(){f.data(this,a)});d=a.split(".",2),d[1]=d[1]?"."+d[1]:"",e=d[1]+"!";return f.access(this,function(c){if(c===b){m=this.triggerHandler("getData"+e,[d[0]]),m===b&&j&&(m=f.data(j,a),m=l(j,a,m));return m===b&&d[1]?this.data(d[0]):m}d[1]=c,this.each(function(){var b=f(this);b.triggerHandler("setData"+e,d),f.data(this,a,c),b.triggerHandler("changeData"+e,d)})},null,c,arguments.length>1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length<d)return f.queue(this[0],a);return c===b?this:this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f.Callbacks("once memory"),!0))h++,l.add(m);m();return d.promise(c)}});var o=/[\n\t\r]/g,p=/\s+/,q=/\r/g,r=/^(?:button|input)$/i,s=/^(?:button|input|object|select|textarea)$/i,t=/^a(?:rea)?$/i,u=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,v=f.support.getSetAttribute,w,x,y;f.fn.extend({attr:function(a,b){return f.access(this,f.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(p);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(o," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(p);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c<d;c++)if(this[c].nodeType===1&&(" "+this[c].className+" ").replace(o," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c<d;c++){e=i[c];if(e.selected&&(f.support.optDisabled?!e.disabled:e.getAttribute("disabled")===null)&&(!e.parentNode.disabled||!f.nodeName(e.parentNode,"optgroup"))){b=f(e).val();if(j)return b;h.push(b)}}if(j&&!h.length&&i.length)return f(i[g]).val();return h},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i<g;i++)e=d[i],e&&(c=f.propFix[e]||e,h=u.test(e),h||f.attr(a,e,""),a.removeAttribute(v?e:c),h&&c in a&&(a[c]=!1))}},attrHooks:{type:{set:function(a,b){if(r.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},value:{get:function(a,b){if(w&&f.nodeName(a,"button"))return w.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(w&&f.nodeName(a,"button"))return w.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e,g,h,i=a.nodeType;if(!!a&&i!==3&&i!==8&&i!==2){h=i!==1||!f.isXMLDoc(a),h&&(c=f.propFix[c]||c,g=f.propHooks[c]);return d!==b?g&&"set"in g&&(e=g.set(a,d,c))!==b?e:a[c]=d:g&&"get"in g&&(e=g.get(a,c))!==null?e:a[c]}},propHooks:{tabIndex:{get:function(a){var c=a.getAttributeNode("tabindex");return c&&c.specified?parseInt(c.value,10):s.test(a.nodeName)||t.test(a.nodeName)&&a.href?0:b}}}}),f.attrHooks.tabindex=f.propHooks.tabIndex,x={get:function(a,c){var d,e=f.prop(a,c);return e===!0||typeof e!="boolean"&&(d=a.getAttributeNode(c))&&d.nodeValue!==!1?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},v||(y={name:!0,id:!0,coords:!0},w=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&(y[c]?d.nodeValue!=="":d.specified)?d.nodeValue:b},set:function(a,b,d){var e=a.getAttributeNode(d);e||(e=c.createAttribute(d),a.setAttributeNode(e));return e.nodeValue=b+""}},f.attrHooks.tabindex.set=w.set,f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})}),f.attrHooks.contenteditable={get:w.get,set:function(a,b,c){b===""&&(b="false"),w.set(a,b,c)}}),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex);return null}})),f.support.enctype||(f.propFix.enctype="encoding"),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function(
+a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k<c.length;k++){l=A.exec(c[k])||[],m=l[1],n=(l[2]||"").split(".").sort(),s=f.event.special[m]||{},m=(g?s.delegateType:s.bindType)||m,s=f.event.special[m]||{},o=f.extend({type:m,origType:l[1],data:e,handler:d,guid:d.guid,selector:g,quick:g&&G(g),namespace:n.join(".")},p),r=j[m];if(!r){r=j[m]=[],r.delegateCount=0;if(!s.setup||s.setup.call(a,e,n,i)===!1)a.addEventListener?a.addEventListener(m,i,!1):a.attachEvent&&a.attachEvent("on"+m,i)}s.add&&(s.add.call(a,o),o.handler.guid||(o.handler.guid=d.guid)),g?r.splice(r.delegateCount++,0,o):r.push(o),f.event.global[m]=!0}a=null}},global:{},remove:function(a,b,c,d,e){var g=f.hasData(a)&&f._data(a),h,i,j,k,l,m,n,o,p,q,r,s;if(!!g&&!!(o=g.events)){b=f.trim(I(b||"")).split(" ");for(h=0;h<b.length;h++){i=A.exec(b[h])||[],j=k=i[1],l=i[2];if(!j){for(j in o)f.event.remove(a,j+b[h],c,d,!0);continue}p=f.event.special[j]||{},j=(d?p.delegateType:p.bindType)||j,r=o[j]||[],m=r.length,l=l?new RegExp("(^|\\.)"+l.split(".").sort().join("\\.(?:.*\\.)?")+"(\\.|$)"):null;for(n=0;n<r.length;n++)s=r[n],(e||k===s.origType)&&(!c||c.guid===s.guid)&&(!l||l.test(s.namespace))&&(!d||d===s.selector||d==="**"&&s.selector)&&(r.splice(n--,1),s.selector&&r.delegateCount--,p.remove&&p.remove.call(a,s));r.length===0&&m!==r.length&&((!p.teardown||p.teardown.call(a,l)===!1)&&f.removeEvent(a,j,g.handle),delete o[j])}f.isEmptyObject(o)&&(q=g.handle,q&&(q.elem=null),f.removeData(a,["events","handle"],!0))}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){if(!e||e.nodeType!==3&&e.nodeType!==8){var h=c.type||c,i=[],j,k,l,m,n,o,p,q,r,s;if(E.test(h+f.event.triggered))return;h.indexOf("!")>=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;l<r.length&&!c.isPropagationStopped();l++)m=r[l][0],c.type=r[l][1],q=(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preventDefault();c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this)if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;k<e;k++)s=d[k],t=s.selector,p[t]===b&&(p[t]=s.quick?H(m,s.quick):n.is(t)),p[t]&&r.push(s);r.length&&j.push({elem:m,matches:r})}}d.length>e&&j.push({elem:this,matches:d.slice(e)});for(k=0;k<j.length&&!c.isPropagationStopped();k++){q=j[k],c.currentTarget=q.elem;for(l=0;l<q.matches.length&&!c.isImmediatePropagationStopped();l++){s=q.matches[l];if(h||!c.namespace&&!s.namespace||c.namespace_re&&c.namespace_re.test(s.namespace))c.data=s.data,c.handleObj=s,o=((f.event.special[s.origType]||{}).handle||s.handler).apply(q.elem,g),o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()))}}i.postDispatch&&i.postDispatch.call(this,c);return c.result}},props:"attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){a.which==null&&(a.which=b.charCode!=null?b.charCode:b.keyCode);return a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,d){var e,f,g,h=d.button,i=d.fromElement;a.pageX==null&&d.clientX!=null&&(e=a.target.ownerDocument||c,f=e.documentElement,g=e.body,a.pageX=d.clientX+(f&&f.scrollLeft||g&&g.scrollLeft||0)-(f&&f.clientLeft||g&&g.clientLeft||0),a.pageY=d.clientY+(f&&f.scrollTop||g&&g.scrollTop||0)-(f&&f.clientTop||g&&g.clientTop||0)),!a.relatedTarget&&i&&(a.relatedTarget=i===a.target?d.toElement:i),!a.which&&h!==b&&(a.which=h&1?1:h&2?3:h&4?2:0);return a}},fix:function(a){if(a[f.expando])return a;var d,e,g=a,h=f.event.fixHooks[a.type]||{},i=h.props?this.props.concat(h.props):this.props;a=f.Event(g);for(d=i.length;d;)e=i[--d],a[e]=g[e];a.target||(a.target=g.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),a.metaKey===b&&(a.metaKey=a.ctrlKey);return h.filter?h.filter(a,g):a},special:{ready:{setup:f.bindReady},load:{noBubble:!0},focus:{delegateType:"focusin"},blur:{delegateType:"focusout"},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}},simulate:function(a,b,c,d){var e=f.extend(new f.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?f.event.trigger(e,null,b):f.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},f.event.handle=f.event.dispatch,f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!(this instanceof f.Event))return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?K:J):this.type=a,b&&f.extend(this,b),this.timeStamp=a&&a.timeStamp||f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=K;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=K;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=K,this.stopPropagation()},isDefaultPrevented:J,isPropagationStopped:J,isImmediatePropagationStopped:J},f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c=this,d=a.relatedTarget,e=a.handleObj,g=e.selector,h;if(!d||d!==c&&!f.contains(c,d))a.type=e.origType,h=e.handler.apply(this,arguments),a.type=b;return h}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(){if(f.nodeName(this,"form"))return!1;f.event.add(this,"click._submit keypress._submit",function(a){var c=a.target,d=f.nodeName(c,"input")||f.nodeName(c,"button")?c.form:b;d&&!d._submit_attached&&(f.event.add(d,"submit._submit",function(a){a._submit_bubble=!0}),d._submit_attached=!0)})},postDispatch:function(a){a._submit_bubble&&(delete a._submit_bubble,this.parentNode&&!a.isTrigger&&f.event.simulate("submit",this.parentNode,a,!0))},teardown:function(){if(f.nodeName(this,"form"))return!1;f.event.remove(this,"._submit")}}),f.support.changeBubbles||(f.event.special.change={setup:function(){if(z.test(this.nodeName)){if(this.type==="checkbox"||this.type==="radio")f.event.add(this,"propertychange._change",function(a){a.originalEvent.propertyName==="checked"&&(this._just_changed=!0)}),f.event.add(this,"click._change",function(a){this._just_changed&&!a.isTrigger&&(this._just_changed=!1,f.event.simulate("change",this,a,!0))});return!1}f.event.add(this,"beforeactivate._change",function(a){var b=a.target;z.test(b.nodeName)&&!b._change_attached&&(f.event.add(b,"change._change",function(a){this.parentNode&&!a.isSimulated&&!a.isTrigger&&f.event.simulate("change",this.parentNode,a,!0)}),b._change_attached=!0)})},handle:function(a){var b=a.target;if(this!==b||a.isSimulated||a.isTrigger||b.type!=="radio"&&b.type!=="checkbox")return a.handleObj.handler.apply(this,arguments)},teardown:function(){f.event.remove(this,"._change");return z.test(this.nodeName)}}),f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){var d=0,e=function(a){f.event.simulate(b,a.target,f.event.fix(a),!0)};f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.fn.extend({on:function(a,c,d,e,g){var h,i;if(typeof a=="object"){typeof c!="string"&&(d=d||c,c=b);for(i in a)this.on(i,c,d,a[i],g);return this}d==null&&e==null?(e=c,d=c=b):e==null&&(typeof c=="string"?(e=d,d=b):(e=d,d=c,c=b));if(e===!1)e=J;else if(!e)return this;g===1&&(h=e,e=function(a){f().off(a);return h.apply(this,arguments)},e.guid=h.guid||(h.guid=f.guid++));return this.each(function(){f.event.add(this,a,e,d,c)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,c,d){if(a&&a.preventDefault&&a.handleObj){var e=a.handleObj;f(a.delegateTarget).off(e.namespace?e.origType+"."+e.namespace:e.origType,e.selector,e.handler);return this}if(typeof a=="object"){for(var g in a)this.off(g,c,a[g]);return this}if(c===!1||typeof c=="function")d=c,c=b;d===!1&&(d=J);return this.each(function(){f.event.remove(this,a,d,c)})},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},live:function(a,b,c){f(this.context).on(a,this.selector,b,c);return this},die:function(a,b){f(this.context).off(a,this.selector||"**",b);return this},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return arguments.length==1?this.off(a,"**"):this.off(b,a,c)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f._data(this,"lastToggle"+a.guid)||0)%d;f._data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}if(j.nodeType===1){g||(j[d]=c,j.sizset=h);if(typeof b!="string"){if(j===b){k=!0;break}}else if(m.filter(b,[j]).length>0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h<i;h++){var j=e[h];if(j){var k=!1;j=j[a];while(j){if(j[d]===c){k=e[j.sizset];break}j.nodeType===1&&!g&&(j[d]=c,j.sizset=h);if(j.nodeName.toLowerCase()===b){k=j;break}j=j[a]}e[h]=k}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},m.matches=function(a,b){return m(a,null,null,b)},m.matchesSelector=function(a,b){return m(b,null,null,[a]).length>0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e<f;e++){h=o.order[e];if(g=o.leftMatch[h].exec(a)){i=g[1],g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(j,""),d=o.find[h](g,b,c);if(d!=null){a=a.replace(o.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},m.filter=function(a,c,d,e){var f,g,h,i,j,k,l,n,p,q=a,r=[],s=c,t=c&&c[0]&&m.isXML(c[0]);while(a&&c.length){for(h in o.filter)if((f=o.leftMatch[h].exec(a))!=null&&f[2]){k=o.filter[h],l=f[1],g=!1,f.splice(1,1);if(l.substr(l.length-1)==="\\")continue;s===r&&(r=[]);if(o.preFilter[h]){f=o.preFilter[h](f,s,d,r,e,t);if(!f)g=i=!0;else if(f===!0)continue}if(f)for(n=0;(j=s[n])!=null;n++)j&&(i=k(j,f,n,s),p=e^i,d&&i!=null?p?g=!0:s[n]=!1:p&&(r.push(j),g=!0));if(i!==b){d||(s=r),a=a.replace(o.match[h],"");if(!g)return[];break}}if(a===q)if(g==null)m.error(a);else break;q=a}return s},m.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)};var n=m.getText=function(a){var b,c,d=a.nodeType,e="";if(d){if(d===1||d===9||d===11){if(typeof a.textContent=="string")return a.textContent;if(typeof a.innerText=="string")return a.innerText.replace(k,"");for(a=a.firstChild;a;a=a.nextSibling)e+=n(a)}else if(d===3||d===4)return a.nodeValue}else for(b=0;c=a[b];b++)c.nodeType!==8&&(e+=n(c));return e},o=m.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!l.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&m.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&m.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=x;typeof b=="string"&&!l.test(b)&&(b=b.toLowerCase(),d=b,g=w),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(j,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}m.error(e)},CHILD:function(a,b){var c,e,f,g,h,i,j,k=b[1],l=a;switch(k){case"only":case"first":while(l=l.previousSibling)if(l.nodeType===1)return!1;if(k==="first")return!0;l=a;case"last":while(l=l.nextSibling)if(l.nodeType===1)return!1;return!0;case"nth":c=b[2],e=b[3];if(c===1&&e===0)return!0;f=b[0],g=a.parentNode;if(g&&(g[d]!==f||!a.nodeIndex)){i=0;for(l=g.firstChild;l;l=l.nextSibling)l.nodeType===1&&(l.nodeIndex=++i);g[d]=f}j=a.nodeIndex-e;return c===0?j===0:j%c===0&&j/c>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var u,v;c.documentElement.compareDocumentPosition?u=function(a,b){if(a===b){h=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(u=function(a,b){if(a===b){h=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],g=a.parentNode,i=b.parentNode,j=g;if(g===i)return v(a,b);if(!g)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return v(e[k],f[k]);return k===c?v(a,f[k],-1):v(e[k],b,1)},v=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h<i;h++)m(a,g[h],e,c);return m.filter(f,e)};m.attr=f.attr,m.selectors.attrMap={},f.find=m,f.expr=m.selectors,f.expr[":"]=f.expr.filters,f.unique=m.uniqueSort,f.text=m.getText,f.isXMLDoc=m.isXML,f.contains=m.contains}();var L=/Until$/,M=/^(?:parents|prevUntil|prevAll)/,N=/,/,O=/^.[^:#\[\.,]*$/,P=Array.prototype.slice,Q=f.expr.match.globalPOS,R={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(T(this,a,!1),"not",a)},filter:function(a){return this.pushStack(T(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?Q.test(a)?f(a,this.context).index(this[0])>=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d<a.length;d++)f(g).is(a[d])&&c.push({selector:a[d],elem:g,level:h});g=g.parentNode,h++}return c}var i=Q.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(i?i.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/<tbody/i,_=/<|&#?\w+;/,ba=/<(?:script|style)/i,bb=/<(?:script|object|embed|option|style)/i,bc=new RegExp("<(?:"+V+")[\\s/>]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*<!(?:\[CDATA\[|\-\-)/,bg={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f
+.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1></$2>");try{for(;d<e;d++)c=this[d]||{},c.nodeType===1&&(f.cleanData(c.getElementsByTagName("*")),c.innerHTML=a);c=0}catch(g){}}c&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bd.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bi(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,function(a,b){b.src?f.ajax({type:"GET",global:!1,url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(bf,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)})}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i,j=a[0];b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof j=="string"&&j.length<512&&i===c&&j.charAt(0)==="<"&&!bb.test(j)&&(f.support.checkClone||!bd.test(j))&&(f.support.html5Clone||!bc.test(j))&&(g=!0,h=f.fragments[j],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[j]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1></$2>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]==="<table>"&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i<u;i++)bn(l[i]);else bn(l);l.nodeType?j.push(l):j=f.merge(j,l)}if(d){g=function(a){return!a.type||be.test(a.type)};for(k=0;j[k];k++){h=j[k];if(e&&f.nodeName(h,"script")&&(!h.type||be.test(h.type)))e.push(h.parentNode?h.parentNode.removeChild(h):h);else{if(h.nodeType===1){var v=f.grep(h.getElementsByTagName("script"),g);j.splice.apply(j,[k+1,0].concat(v))}d.appendChild(h)}}}return j},cleanData:function(a){var b,c,d=f.cache,e=f.event.special,g=f.support.deleteExpando;for(var h=0,i;(i=a[h])!=null;h++){if(i.nodeName&&f.noData[i.nodeName.toLowerCase()])continue;c=i[f.expando];if(c){b=d[c];if(b&&b.events){for(var j in b.events)e[j]?f.event.remove(i,j):f.removeEvent(i,j,b.handle);b.handle&&(b.handle.elem=null)}g?delete i[f.expando]:i.removeAttribute&&i.removeAttribute(f.expando),delete d[c]}}}});var bp=/alpha\([^)]*\)/i,bq=/opacity=([^)]*)/,br=/([A-Z]|^ms)/g,bs=/^[\-+]?(?:\d*\.)?\d+$/i,bt=/^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i,bu=/^([\-+])=([\-+.\de]+)/,bv=/^margin/,bw={position:"absolute",visibility:"hidden",display:"block"},bx=["Top","Right","Bottom","Left"],by,bz,bA;f.fn.css=function(a,c){return f.access(this,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)},a,c,arguments.length>1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),(e===""&&f.css(d,"display")==="none"||!f.contains(d.ownerDocument.documentElement,d))&&f._data(d,"olddisplay",cu(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(ct("hide",3),a,b,c);var d,e,g=0,h=this.length;for(;g<h;g++)d=this[g],d.style&&(e=f.css(d,"display"),e!=="none"&&!f._data(d,"olddisplay")&&f._data(d,"olddisplay",e));for(g=0;g<h;g++)this[g].style&&(this[g].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(ct("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){function g(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o,p,q;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]);if((k=f.cssHooks[g])&&"expand"in k){l=k.expand(a[g]),delete a[g];for(i in l)i in a||(a[i]=l[i])}}for(g in a){h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(!f.support.inlineBlockNeedsLayout||cu(this.nodeName)==="inline"?this.style.display="inline-block":this.style.zoom=1))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)j=new f.fx(this,b,i),h=a[i],cm.test(h)?(q=f._data(this,"toggle"+i)||(h==="toggle"?d?"show":"hide":0),q?(f._data(this,"toggle"+i,q==="show"?"hide":"show"),j[q]()):j[h]()):(m=cn.exec(h),n=j.cur(),m?(o=parseFloat(m[2]),p=m[3]||(f.cssNumber[i]?"":"px"),p!=="px"&&(f.style(this,i,(o||1)+p),n=(o||1)/j.cur()*n,f.style(this,i,n+p)),m[1]&&(o=(m[1]==="-="?-1:1)*o+n),j.custom(n,o,p)):j.custom(n,h,""));return!0}var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return e.queue===!1?this.each(g):this.queue(e.queue,g)},stop:function(a,c,d){typeof a!="string"&&(d=c,c=a,a=b),c&&a!==!1&&this.queue(a||"fx",[]);return this.each(function(){function h(a,b,c){var e=b[c];f.removeData(a,c,!0),e.stop(d)}var b,c=!1,e=f.timers,g=f._data(this);d||f._unmark(!0,this);if(a==null)for(b in g)g[b]&&g[b].stop&&b.indexOf(".run")===b.length-4&&h(this,g,b);else g[b=a+".run"]&&g[b].stop&&h(this,g,b);for(b=e.length;b--;)e[b].elem===this&&(a==null||e[b].queue===a)&&(d?e[b](!0):e[b].saveState(),c=!0,e.splice(b,1));(!d||!c)&&f.dequeue(this,a)})}}),f.each({slideDown:ct("show",1),slideUp:ct("hide",1),slideToggle:ct("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default;if(d.queue==null||d.queue===!0)d.queue="fx";d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue?f.dequeue(this,d.queue):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a){return a},swing:function(a){return-Math.cos(a*Math.PI)/2+.5}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,c,d){function h(a){return e.step(a)}var e=this,g=f.fx;this.startTime=cq||cr(),this.end=c,this.now=this.start=a,this.pos=this.state=0,this.unit=d||this.unit||(f.cssNumber[this.prop]?"":"px"),h.queue=this.options.queue,h.elem=this.elem,h.saveState=function(){f._data(e.elem,"fxshow"+e.prop)===b&&(e.options.hide?f._data(e.elem,"fxshow"+e.prop,e.start):e.options.show&&f._data(e.elem,"fxshow"+e.prop,e.end))},h()&&f.timers.push(h)&&!co&&(co=setInterval(g.tick,g.interval))},show:function(){var a=f._data(this.elem,"fxshow"+this.prop);this.options.orig[this.prop]=a||f.style(this.elem,this.prop),this.options.show=!0,a!==b?this.custom(this.cur(),a):this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f._data(this.elem,"fxshow"+this.prop)||f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b,c,d,e=cq||cr(),g=!0,h=this.elem,i=this.options;if(a||e>=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c<b.length;c++)a=b[c],!a()&&b[c]===a&&b.splice(c--,1);b.length||f.fx.stop()},interval:13,stop:function(){clearInterval(co),co=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=a.now+a.unit:a.elem[a.prop]=a.now}}}),f.each(cp.concat.apply([],cp),function(a,b){b.indexOf("margin")&&(f.fx.step[b]=function(a){f.style(a.elem,b,Math.max(0,a.now)+a.unit)})}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var cv,cw=/^t(?:able|d|h)$/i,cx=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?cv=function(a,b,c,d){try{d=a.getBoundingClientRect()}catch(e){}if(!d||!f.contains(c,a))return d?{top:d.top,left:d.left}:{top:0,left:0};var g=b.body,h=cy(b),i=c.clientTop||g.clientTop||0,j=c.clientLeft||g.clientLeft||0,k=h.pageYOffset||f.support.boxModel&&c.scrollTop||g.scrollTop,l=h.pageXOffset||f.support.boxModel&&c.scrollLeft||g.scrollLeft,m=d.top+k-i,n=d.left+l-j;return{top:m,left:n}}:cv=function(a,b,c){var d,e=a.offsetParent,g=a,h=b.body,i=b.defaultView,j=i?i.getComputedStyle(a,null):a.currentStyle,k=a.offsetTop,l=a.offsetLeft;while((a=a.parentNode)&&a!==h&&a!==c){if(f.support.fixedPosition&&j.position==="fixed")break;d=i?i.getComputedStyle(a,null):a.currentStyle,k-=a.scrollTop,l-=a.scrollLeft,a===e&&(k+=a.offsetTop,l+=a.offsetLeft,f.support.doesNotAddBorder&&(!f.support.doesAddBorderForTableAndCells||!cw.test(a.nodeName))&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),g=e,e=a.offsetParent),f.support.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"&&(k+=parseFloat(d.borderTopWidth)||0,l+=parseFloat(d.borderLeftWidth)||0),j=d}if(j.position==="relative"||j.position==="static")k+=h.offsetTop,l+=h.offsetLeft;f.support.fixedPosition&&j.position==="fixed"&&(k+=Math.max(c.scrollTop,h.scrollTop),l+=Math.max(c.scrollLeft,h.scrollLeft));return{top:k,left:l}},f.fn.offset=function(a){if(arguments.length)return a===b?this:this.each(function(b){f.offset.setOffset(this,a,b)});var c=this[0],d=c&&c.ownerDocument;if(!d)return null;if(c===d.body)return f.offset.bodyOffset(c);return cv(c,d,d.documentElement)},f.offset={bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.support.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file
diff --git a/guides/assets/javascripts/responsive-tables.js b/guides/assets/javascripts/responsive-tables.js
new file mode 100755
index 0000000000..8554a1343b
--- /dev/null
+++ b/guides/assets/javascripts/responsive-tables.js
@@ -0,0 +1,43 @@
+$(document).ready(function() {
+ var switched = false;
+ $("table").not(".syntaxhighlighter").addClass("responsive");
+ var updateTables = function() {
+ if (($(window).width() < 767) && !switched ){
+ switched = true;
+ $("table.responsive").each(function(i, element) {
+ splitTable($(element));
+ });
+ return true;
+ }
+ else if (switched && ($(window).width() > 767)) {
+ switched = false;
+ $("table.responsive").each(function(i, element) {
+ unsplitTable($(element));
+ });
+ }
+ };
+
+ $(window).load(updateTables);
+ $(window).bind("resize", updateTables);
+
+
+ function splitTable(original)
+ {
+ original.wrap("<div class='table-wrapper' />");
+
+ var copy = original.clone();
+ copy.find("td:not(:first-child), th:not(:first-child)").css("display", "none");
+ copy.removeClass("responsive");
+
+ original.closest(".table-wrapper").append(copy);
+ copy.wrap("<div class='pinned' />");
+ original.wrap("<div class='scrollable' />");
+ }
+
+ function unsplitTable(original) {
+ original.closest(".table-wrapper").find(".pinned").remove();
+ original.unwrap();
+ original.unwrap();
+ }
+
+});
diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css
index a30e12fe7e..9f5e101d1c 100644
--- a/guides/assets/stylesheets/main.css
+++ b/guides/assets/stylesheets/main.css
@@ -9,6 +9,9 @@
.left {float: left; margin-right: 1em;}
.right {float: right; margin-left: 1em;}
+@media screen and (max-width: 480px) {
+ .left, .right { float: none; }
+}
.small {font-size: smaller;}
.large {font-size: larger;}
.hide {display: none;}
@@ -29,6 +32,13 @@ pre,code {
font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace;
line-height: 1.5;
}
+pre,tt,code,.note>p {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+}
abbr, acronym { border-bottom: 1px dotted #666; }
address { margin: 0 0 1.5em; font-style: italic; }
@@ -52,23 +62,23 @@ textarea, select {
}
table {
- margin: 0 0 1.5em;
- border: 2px solid #CCC;
- background: #FFF;
- border-collapse: collapse;
+ margin: 0 0 1.5em;
+ border: 2px solid #CCC;
+ background: #FFF;
+ border-collapse: collapse;
}
table th, table td {
- padding: 0.25em 1em;
- border: 1px solid #CCC;
- border-collapse: collapse;
+ padding: 0.25em 1em;
+ border: 1px solid #CCC;
+ border-collapse: collapse;
}
table th {
- border-bottom: 2px solid #CCC;
- background: #EEE;
- font-weight: bold;
- padding: 0.5em 1em;
+ border-bottom: 2px solid #CCC;
+ background: #EEE;
+ font-weight: bold;
+ padding: 0.5em 1em;
}
@@ -76,84 +86,205 @@ table th {
--------------------------------------- */
body {
- text-align: center;
- font-family: Helvetica, Arial, sans-serif;
- font-size: 87.5%;
- line-height: 1.5em;
- background: #fff;
- min-width: 69em;
- color: #999;
- }
+ text-align: center;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 87.5%;
+ line-height: 1.5em;
+ background: #fff;
+ color: #999;
+ }
.wrapper {
- text-align: left;
- margin: 0 auto;
- width: 69em;
- }
+ text-align: left;
+ margin: 0 auto;
+ max-width: 960px;
+ padding: 0 1em;
+ }
+
+.red-button {
+ display: inline-block;
+ border-top: 1px solid rgba(255,255,255,.5);
+ background: #751913;
+ background: -webkit-gradient(linear, left top, left bottom, from(#c52f24), to(#751913));
+ background: -webkit-linear-gradient(top, #c52f24, #751913);
+ background: -moz-linear-gradient(top, #c52f24, #751913);
+ background: -ms-linear-gradient(top, #c52f24, #751913);
+ background: -o-linear-gradient(top, #c52f24, #751913);
+ padding: 9px 18px;
+ -webkit-border-radius: 11px;
+ -moz-border-radius: 11px;
+ border-radius: 11px;
+ -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0;
+ -moz-box-shadow: rgba(0,0,0,1) 0 1px 0;
+ box-shadow: rgba(0,0,0,1) 0 1px 0;
+ text-shadow: rgba(0,0,0,.4) 0 1px 0;
+ color: white;
+ font-size: 15px;
+ font-family: Helvetica, Arial, Sans-Serif;
+ text-decoration: none;
+ vertical-align: middle;
+}
+.red-button:active {
+ border-top: none;
+ padding-top: 10px;
+ background: -webkit-gradient(linear, left top, left bottom, from(#751913), to(#c52f24));
+ background: -webkit-linear-gradient(top, #751913, #c52f24);
+ background: -moz-linear-gradient(top, #751913, #c52f24);
+ background: -ms-linear-gradient(top, #751913, #c52f24);
+ background: -o-linear-gradient(top, #751913, #c52f24);
+}
#topNav {
- padding: 1em 0;
- color: #565656;
- background: #222;
+ padding: 1em 0;
+ color: #565656;
+ background: #222;
+}
+
+.s-hidden {
+ display: none;
+}
+
+@media screen and (min-width: 1025px) {
+ .more-info-button {
+ display: none;
+ }
+ .more-info-links {
+ list-style: none;
+ display: inline;
+ margin: 0;
+ }
+
+ .more-info {
+ display: inline-block;
+ }
+ .more-info:after {
+ content: " |";
+ }
+
+ .more-info:last-child:after {
+ content: "";
+ }
+
+}
+
+@media screen and (max-width: 1024px) {
+ #topNav .wrapper { text-align: center; }
+ .more-info-button {
+ position: relative;
+ z-index: 25;
+ }
+
+ .more-info-label {
+ display: none;
+ }
+
+ .more-info-container {
+ position: absolute;
+ top: .5em;
+ z-index: 20;
+ margin: 0 auto;
+ left: 0;
+ right: 0;
+ width: 20em;
+ }
+
+ .more-info-links {
+ display: block;
+ list-style: none;
+ background-color: #c52f24;
+ border-radius: 5px;
+ padding-top: 5.25em;
+ border: 1px #980905 solid;
+ }
+ .more-info-links.s-hidden {
+ display: none;
+ }
+ .more-info {
+ padding: .75em;
+ border-top: 1px #980905 solid;
+ }
+ .more-info a, .more-info a:link, .more-info a:visited {
+ display: block;
+ color: white;
+ width: 100%;
+ height: 100%;
+ text-decoration: none;
+ text-transform: uppercase;
+ }
}
#header {
- background: #c52f24 url(../images/header_tile.gif) repeat-x;
- color: #FFF;
- padding: 1.5em 0;
- position: relative;
- z-index: 99;
- }
+ background: #c52f24 url(../images/header_tile.gif) repeat-x;
+ color: #FFF;
+ padding: 1.5em 0;
+ z-index: 99;
+ }
#feature {
- background: #d5e9f6 url(../images/feature_tile.gif) repeat-x;
- color: #333;
- padding: 0.5em 0 1.5em;
+ background: #d5e9f6 url(../images/feature_tile.gif) repeat-x;
+ color: #333;
+ padding: 0.5em 0 1.5em;
}
#container {
- color: #333;
- padding: 0.5em 0 1.5em 0;
- }
+ color: #333;
+ padding: 0.5em 0 1.5em 0;
+ }
#mainCol {
- width: 45em;
- margin-left: 2em;
- }
+ max-width: 630px;
+ margin-left: 2em;
+ }
#subCol {
- position: absolute;
- z-index: 0;
- top: 0;
- right: 0;
- background: #FFF;
- padding: 1em 1.5em 1em 1.25em;
- width: 17em;
- font-size: 0.9285em;
- line-height: 1.3846em;
- }
+ position: absolute;
+ z-index: 0;
+ top: 0;
+ right: 0;
+ background: #FFF;
+ padding: 1em 1.5em 1em 1.25em;
+ width: 17em;
+ font-size: 0.9285em;
+ line-height: 1.3846em;
+ margin-right: 1em;
+ }
+
+
+@media screen and (max-width: 800px) {
+ #subCol {
+ position: static;
+ width: inherit;
+ margin-left: -1em;
+ margin-right: 0;
+ padding-right: 1.25em;
+ }
+}
#extraCol {display: none;}
#footer {
- padding: 2em 0;
- background: #222 url(../images/footer_tile.gif) repeat-x;
- }
+ padding: 2em 0;
+ background: #222 url(../images/footer_tile.gif) repeat-x;
+ }
#footer .wrapper {
- padding-left: 2em;
- width: 67em;
+ padding-left: 1em;
+ max-width: 960px;
}
-#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; width: 68em;}
-#feature .wrapper {width: 45em; padding-right: 23em; position: relative; z-index: 0;}
+#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; max-width: 960px;}
+#feature .wrapper {max-width: 640px; padding-right: 23em; position: relative; z-index: 0;}
+
+@media screen and (max-width: 800px) {
+ #feature .wrapper { padding-right: 0; }
+}
/* Links
--------------------------------------- */
a, a:link, a:visited {
- color: #ee3f3f;
- text-decoration: underline;
- }
+ color: #ee3f3f;
+ text-decoration: underline;
+ }
#mainCol a, #subCol a, #feature a {color: #980905;}
#mainCol a code, #subCol a code, #feature a code {color: #980905;}
@@ -162,201 +293,284 @@ a, a:link, a:visited {
/* Navigation
--------------------------------------- */
-.nav {margin: 0; padding: 0;}
-.nav li {display: inline; list-style: none;}
-
-#header .nav {
- float: right;
- margin-top: 1.5em;
- font-size: 1.2857em;
-}
-
-#header .nav li {margin: 0 0 0 0.5em;}
-#header .nav a {color: #FFF; text-decoration: none;}
-#header .nav a:hover {text-decoration: underline;}
-
-#header .nav .index {
- padding: 0.5em 1.5em;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- background: #980905;
- position: relative;
-}
-
-#header .nav .index a {
- background: #980905 url(../images/nav_arrow.gif) no-repeat right top;
- padding-right: 1em;
- position: relative;
- z-index: 15;
- padding-bottom: 0.125em;
+.nav {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ float: right;
+ margin-top: 1.5em;
+ font-size: 1.2857em;
+}
+
+.nav .nav-item {color: #FFF; text-decoration: none;}
+.nav .nav-item:hover {text-decoration: underline;}
+
+.guides-index-large, .guides-index-small .guides-index-item {
+ padding: 0.5em 1.5em;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ background: #980905;
+ position: relative;
+ color: white;
+ cursor: pointer;
+}
+
+.guides-index .guides-index-item {
+ background: #980905 url(../images/nav_arrow.gif) no-repeat right top;
+ padding-right: 1em;
+ position: relative;
+ z-index: 15;
+ padding-bottom: 0.125em;
+}
+
+.guides-index:hover .guides-index-item, .guides-index .guides-index-item:hover {
+ background-position: right -81px;
+ text-decoration: underline !important;
+}
+
+@media screen and (min-width: 481px) {
+ .nav {
+ float: right;
+ margin-top: 1.5em;
+ font-size: 1.2857em;
+ }
+ .nav>li {
+ display: inline;
+ margin-left: 0.5em;
+ }
+ .guides-index.guides-index-small {
+ display: none;
+ }
+}
+
+@media screen and (max-width: 480px) {
+
+ .nav {
+ float: none;
+ width: 100%;
+ text-align: center;
+ }
+ .nav .nav-item {
+ display: block;
+ margin: 0;
+ width: 100%;
+ background-color: #980905;
+ border: solid 1px #620c04;
+ border-top: 0;
+ padding: 15px 0;
+ text-align: center;
+ }
+ .nav .nav-item, .nav-item.guides-index-item {
+ text-transform: uppercase;
+ }
+ .nav .nav-item:first-child, .nav-item.guides-index-small {
+ border-top: solid 1px #620c04;
+ }
+ .guides-index.guides-index-small {
+ display: block;
+ margin-top: 1.5em;
+ }
+ .guides-index.guides-index-large {
+ display: none;
+ }
+ .guides-index-small .guides-index-item {
+ font: inherit;
+ padding-left: .75em;
+ font-size: .95em;
+ background-position: 96% -65px;
+ -webkit-appearance: none;
+ }
}
-#header .nav .index:hover a, #header .nav .index a:hover {background-position: right -81px;}
#guides {
- width: 27em;
- display: block;
- background: #980905;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
- -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
- color: #f1938c;
- padding: 1.5em 2em;
- position: absolute;
- z-index: 10;
- top: -0.25em;
- right: 0;
- padding-top: 2em;
+ width: 27em;
+ display: block;
+ background: #980905;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
+ -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
+ color: #f1938c;
+ padding: 1.5em 2em;
+ position: absolute;
+ z-index: 10;
+ top: -0.25em;
+ right: 0;
+ padding-top: 2em;
}
#guides dt, #guides dd {
- font-weight: normal;
- font-size: 0.722em;
- margin: 0;
- padding: 0;
+ font-weight: normal;
+ font-size: 0.722em;
+ margin: 0;
+ padding: 0;
}
#guides dt {padding:0; margin: 0.5em 0 0;}
#guides a {color: #FFF; background: none !important;}
#guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;}
#guides .R {float: right;}
#guides hr {
- display: block;
- border: none;
- height: 1px;
- color: #f1938c;
- background: #f1938c;
+ display: block;
+ border: none;
+ height: 1px;
+ color: #f1938c;
+ background: #f1938c;
}
/* Headings
--------------------------------------- */
h1 {
- font-size: 2.5em;
- line-height: 1em;
- margin: 0.6em 0 .2em;
- font-weight: bold;
- }
+ font-size: 2.5em;
+ line-height: 1em;
+ margin: 0.6em 0 .2em;
+ font-weight: bold;
+ }
h2 {
- font-size: 2.1428em;
- line-height: 1em;
- margin: 0.7em 0 .2333em;
- font-weight: bold;
- }
+ font-size: 2.1428em;
+ line-height: 1em;
+ margin: 0.7em 0 .2333em;
+ font-weight: bold;
+ }
+
+@media screen and (max-width: 480px) {
+ h2 {
+ font-size: 1.45em;
+ }
+}
h3 {
- font-size: 1.7142em;
- line-height: 1.286em;
- margin: 0.875em 0 0.2916em;
- font-weight: bold;
- }
+ font-size: 1.7142em;
+ line-height: 1.286em;
+ margin: 0.875em 0 0.2916em;
+ font-weight: bold;
+ }
+
+@media screen and (max-width: 480px) {
+ h3 {
+ font-size: 1.45em;
+ }
+}
h4 {
- font-size: 1.2857em;
- line-height: 1.2em;
- margin: 1.6667em 0 .3887em;
- font-weight: bold;
- }
+ font-size: 1.2857em;
+ line-height: 1.2em;
+ margin: 1.6667em 0 .3887em;
+ font-weight: bold;
+ }
h5 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: bold;
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: bold;
}
h6 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: normal;
- }
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: normal;
+ }
.section {
- padding-bottom: 0.25em;
- border-bottom: 1px solid #999;
+ padding-bottom: 0.25em;
+ border-bottom: 1px solid #999;
}
/* Content
--------------------------------------- */
.pic {
- margin: 0 2em 2em 0;
+ margin: 0 2em 2em 0;
}
#topNav strong {color: #999; margin-right: 0.5em;}
#topNav strong a {color: #FFF;}
#header h1 {
- float: left;
- background: url(../images/rails_guides_logo.gif) no-repeat;
- width: 297px;
- text-indent: -9999em;
- margin: 0;
- padding: 0;
+ float: left;
+ background: url(../images/rails_guides_logo.gif) no-repeat;
+ width: 297px;
+ text-indent: -9999em;
+ margin: 0;
+ padding: 0;
+}
+
+@media screen and (max-width: 480px) {
+ #header h1 {
+ float: none;
+ }
}
#header h1 a {
- text-decoration: none;
- display: block;
- height: 77px;
+ text-decoration: none;
+ display: block;
+ height: 77px;
}
#feature p {
- font-size: 1.2857em;
- margin-bottom: 0.75em;
+ font-size: 1.2857em;
+ margin-bottom: 0.75em;
+}
+
+@media screen and (max-width: 480px) {
+ #feature p {
+ font-size: 1em;
+ }
}
#feature ul {margin-left: 0;}
#feature ul li {
- list-style: none;
- background: url(../images/check_bullet.gif) no-repeat left 0.5em;
- padding: 0.5em 1.75em 0.5em 1.75em;
- font-size: 1.1428em;
- font-weight: bold;
+ list-style: none;
+ background: url(../images/check_bullet.gif) no-repeat left 0.5em;
+ padding: 0.5em 1.75em 0.5em 1.75em;
+ font-size: 1.1428em;
+ font-weight: bold;
}
#mainCol dd, #subCol dd {
- padding: 0.25em 0 1em;
- border-bottom: 1px solid #CCC;
- margin-bottom: 1em;
- margin-left: 0;
- /*padding-left: 28px;*/
- padding-left: 0;
+ padding: 0.25em 0 1em;
+ border-bottom: 1px solid #CCC;
+ margin-bottom: 1em;
+ margin-left: 0;
+ /*padding-left: 28px;*/
+ padding-left: 0;
}
#mainCol dt, #subCol dt {
- font-size: 1.2857em;
- padding: 0.125em 0 0.25em 0;
- margin-bottom: 0;
- /*background: url(../images/book_icon.gif) no-repeat left top;
- padding: 0.125em 0 0.25em 28px;*/
+ font-size: 1em;
+ padding: 0.125em 0 0.25em 0;
+ margin-bottom: 0;
+ /*background: url(../images/book_icon.gif) no-repeat left top;
+ padding: 0.125em 0 0.25em 28px;*/
}
#mainCol dd.work-in-progress, #subCol dd.work-in-progress {
- background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#mainCol dd.kindle, #subCol dd.kindle {
- background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#mainCol div.warning, #subCol dd.warning {
- background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1.25em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1.25em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#subCol .chapters {color: #980905;}
@@ -367,31 +581,31 @@ h6 {
#subCol h3.chapter img {vertical-align: text-bottom;}
#subCol .chapters ul {margin-left: 0; margin-top: 0.5em;}
#subCol .chapters ul li {
- list-style: none;
- padding: 0 0 0 1em;
- background: url(../images/bullet.gif) no-repeat left 0.45em;
- margin-left: 0;
- font-size: 1em;
- font-weight: normal;
+ list-style: none;
+ padding: 0 0 0 1em;
+ background: url(../images/bullet.gif) no-repeat left 0.45em;
+ margin-left: 0;
+ font-size: 1em;
+ font-weight: normal;
}
div.code_container {
- background: #EEE url(../images/tab_grey.gif) no-repeat left top;
- padding: 0.25em 1em 0.5em 48px;
+ background: #EEE url(../images/tab_grey.gif) no-repeat left top;
+ padding: 0.25em 1em 0.5em 48px;
}
.note {
- background: #fff9d8 url(../images/tab_note.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin: 0.25em 0 1.5em 0;
+ background: #fff9d8 url(../images/tab_note.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin: 0.25em 0 1.5em 0;
}
.info {
- background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin: 0.25em 0 1.5em 0;
+ background: #d5e9f6 url(../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin: 0.25em 0 1.5em 0;
}
#mainCol div.todo {
@@ -404,22 +618,22 @@ div.code_container {
.note code, .info code, .todo code {border:none; background: none; padding: 0;}
#mainCol ul li {
- list-style:none;
- background: url(../images/grey_bullet.gif) no-repeat left 0.5em;
- padding-left: 1em;
- margin-left: 0;
+ list-style:none;
+ background: url(../images/grey_bullet.gif) no-repeat left 0.5em;
+ padding-left: 1em;
+ margin-left: 0;
}
#subCol .content {
- font-size: 0.7857em;
- line-height: 1.5em;
+ font-size: 0.7857em;
+ line-height: 1.5em;
}
#subCol .content li {
- font-weight: normal;
- background: none;
- padding: 0 0 1em;
- font-size: 1.1667em;
+ font-weight: normal;
+ background: none;
+ padding: 0 0 1em;
+ font-size: 1.1667em;
}
/* Clearing
@@ -460,3 +674,28 @@ div.important p, div.caution p, div.warning p, div.note p, div.info p {
z-index: 100;
border: none;
}
+
+/* Foundation v2.1.4 http://foundation.zurb.com */
+/* Artfully masterminded by ZURB */
+
+table th { font-weight: bold; }
+table td, table th { padding: 9px 10px; text-align: left; }
+
+/* Mobile */
+@media only screen and (max-width: 767px) {
+
+ table.responsive { margin-bottom: 0; }
+
+ .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; }
+ .pinned table { border-right: none; border-left: none; width: 100%; }
+ .pinned table th, .pinned table td { white-space: nowrap; }
+ .pinned td:last-child { border-bottom: 0; }
+
+ div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; }
+ div.table-wrapper div.scrollable table { margin-left: 35%; }
+ div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; }
+
+ table.responsive td, table.responsive th { position: relative; white-space: nowrap; overflow: hidden; }
+ table.responsive th:first-child, table.responsive td:first-child, table.responsive td:first-child, table.responsive.pinned td { display: none; }
+
+}
diff --git a/guides/assets/stylesheets/responsive-tables.css b/guides/assets/stylesheets/responsive-tables.css
new file mode 100755
index 0000000000..f5fbcbf948
--- /dev/null
+++ b/guides/assets/stylesheets/responsive-tables.css
@@ -0,0 +1,50 @@
+/* Foundation v2.1.4 http://foundation.zurb.com */
+/* Artfully masterminded by ZURB */
+
+/* --------------------------------------------------
+ Table of Contents
+-----------------------------------------------------
+:: Shared Styles
+:: Page Name 1
+:: Page Name 2
+*/
+
+
+/* -----------------------------------------
+ Shared Styles
+----------------------------------------- */
+
+table th { font-weight: bold; }
+table td, table th { padding: 9px 10px; text-align: left; }
+
+/* Mobile */
+@media only screen and (max-width: 767px) {
+
+ table { margin-bottom: 0; }
+
+ .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; }
+ .pinned table { border-right: none; border-left: none; width: 100%; }
+ .pinned table th, .pinned table td { white-space: nowrap; }
+ .pinned td:last-child { border-bottom: 0; }
+
+ div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; }
+ div.table-wrapper div.scrollable table { margin-left: 35%; }
+ div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; }
+
+ table td, table th { position: relative; white-space: nowrap; overflow: hidden; }
+ table th:first-child, table td:first-child, table td:first-child, table.pinned td { display: none; }
+
+}
+
+/* -----------------------------------------
+ Page Name 1
+----------------------------------------- */
+
+
+
+
+/* -----------------------------------------
+ Page Name 2
+----------------------------------------- */
+
+
diff --git a/guides/code/getting_started/app/assets/javascripts/application.js b/guides/code/getting_started/app/assets/javascripts/application.js
index 9097d830e2..93cdae76ca 100644
--- a/guides/code/getting_started/app/assets/javascripts/application.js
+++ b/guides/code/getting_started/app/assets/javascripts/application.js
@@ -5,7 +5,7 @@
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// the compiled file.
+// compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
diff --git a/guides/rails_guides/helpers.rb b/guides/rails_guides/helpers.rb
index e6ef656474..a288d0f0f4 100644
--- a/guides/rails_guides/helpers.rb
+++ b/guides/rails_guides/helpers.rb
@@ -24,8 +24,14 @@ module RailsGuides
documents.reject { |document| document['work_in_progress'] }
end
- def docs_for_menu(position)
- position == 'L' ? documents_by_section.to(3) : documents_by_section.from(4)
+ def docs_for_menu(position=nil)
+ if position.nil?
+ documents_by_section
+ elsif position == 'L'
+ documents_by_section.to(3)
+ else
+ documents_by_section.from(4)
+ end
end
def author(name, nick, image = 'credits_pic_blank.gif', &block)
diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md
index cce5bc5331..54fe49252f 100644
--- a/guides/source/4_0_release_notes.md
+++ b/guides/source/4_0_release_notes.md
@@ -141,6 +141,8 @@ Action Mailer
end
```
+* Allow for callbacks in mailers similar to ActionController::Base. You can now set up headers/attachments using `before_filter` or `after_filter`. You could also change delivery settings or prevent delivery in an after filter based on instance variables set in your mailer action. You have access to `ActionMailer::Base` instance methods like `message`, `attachments`, `headers`.
+
Action Pack
-----------
diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md
index e6a6b05166..824ffb5d7a 100644
--- a/guides/source/action_controller_overview.md
+++ b/guides/source/action_controller_overview.md
@@ -174,8 +174,8 @@ Your application has a session for each user in which you can store small amount
* ActionDispatch::Session::CookieStore - Stores everything on the client.
* ActionDispatch::Session::CacheStore - Stores the data in the Rails cache.
-* ActionDispatch::Session::ActiveRecordStore - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
-* ActionDispatch::Session::MemCacheStore - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
+* @ActionDispatch::Session::ActiveRecordStore@ - Stores the data in a database using Active Record. (require `activerecord-session_store` gem).
+* @ActionDispatch::Session::MemCacheStore@ - Stores the data in a memcached cluster (this is a legacy implementation; consider using CacheStore instead).
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md
index 3c3ab83c37..a938db6265 100644
--- a/guides/source/action_mailer_basics.md
+++ b/guides/source/action_mailer_basics.md
@@ -27,7 +27,7 @@ create app/mailers/user_mailer.rb
invoke erb
create app/views/user_mailer
invoke test_unit
-create test/functional/user_mailer_test.rb
+create test/mailers/user_mailer_test.rb
```
So we got the mailer, the views, and the tests.
diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md
index 2a84242b9c..645498437d 100644
--- a/guides/source/active_support_core_extensions.md
+++ b/guides/source/active_support_core_extensions.md
@@ -3097,7 +3097,8 @@ Time.local(2000, 11, 31).next_quarter # => Wed, 28 Feb 2001
The methods `beginning_of_week` and `end_of_week` return the dates for the
beginning and end of the week, respectively. Weeks are assumed to start on
-Monday, but that can be changed passing an argument.
+Monday, but that can be changed passing an argument, setting thread local
+`Date.beginning_of_week` or `config.beginning_of_week`.
```ruby
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
@@ -3111,18 +3112,24 @@ d.end_of_week(:sunday) # => Sat, 08 May 2010
##### `monday`, `sunday`
-The methods `monday` and `sunday` return the dates for the beginning and
-end of the week, respectively. Weeks are assumed to start on Monday.
+The methods `monday` and `sunday` return the dates for the previous Monday and
+next Sunday, respectively.
```ruby
d = Date.new(2010, 5, 8) # => Sat, 08 May 2010
d.monday # => Mon, 03 May 2010
d.sunday # => Sun, 09 May 2010
+
+d = Date.new(2012, 9, 10) # => Mon, 10 Sep 2012
+d.monday # => Mon, 10 Sep 2012
+
+d = Date.new(2012, 9, 16) # => Sun, 16 Sep 2012
+d.sunday # => Sun, 16 Sep 2012
```
##### `prev_week`, `next_week`
-The method `next_week` receives a symbol with a day name in English (in lowercase, default is `:monday`) and it returns the date corresponding to that day:
+The method `next_week` receives a symbol with a day name in English (default is the thread local `Date.beginning_of_week`, or `config.beginning_of_week`, or `:monday`) and it returns the date corresponding to that day.
```ruby
d = Date.new(2010, 5, 9) # => Sun, 09 May 2010
@@ -3140,6 +3147,8 @@ d.prev_week(:friday) # => Fri, 30 Apr 2010
`prev_week` is aliased to `last_week`.
+Both `next_week` and `prev_week` work as expected when `Date.beginning_of_week` or `config.beginning_of_week` are set.
+
##### `beginning_of_month`, `end_of_month`
The methods `beginning_of_month` and `end_of_month` return the dates for the beginning and end of the month:
@@ -3617,6 +3626,8 @@ now = Time.current
# => Mon, 09 Aug 2010 23:20:05 UTC +00:00
now.all_week
# => Mon, 09 Aug 2010 00:00:00 UTC +00:00..Sun, 15 Aug 2010 23:59:59 UTC +00:00
+now.all_week(:sunday)
+# => Sun, 16 Sep 2012 00:00:00 UTC +00:00..Sat, 22 Sep 2012 23:59:59 UTC +00:00
now.all_month
# => Sat, 01 Aug 2010 00:00:00 UTC +00:00..Tue, 31 Aug 2010 23:59:59 UTC +00:00
now.all_quarter
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index b66b30a117..0338ef5ad0 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -134,10 +134,10 @@ Example:
`rails generate controller CreditCard open debit credit close`
Credit card controller with URLs like /credit_card/debit.
- Controller: app/controllers/credit_card_controller.rb
- Functional Test: test/functional/credit_card_controller_test.rb
- Views: app/views/credit_card/debit.html.erb [...]
- Helper: app/helpers/credit_card_helper.rb
+ Controller: app/controllers/credit_card_controller.rb
+ Test: test/controllers/credit_card_controller_test.rb
+ Views: app/views/credit_card/debit.html.erb [...]
+ Helper: app/helpers/credit_card_helper.rb
```
The controller generator is expecting parameters in the form of `generate controller ControllerName action1 action2`. Let's make a `Greetings` controller with an action of **hello**, which will say something nice to us.
@@ -150,11 +150,11 @@ $ rails generate controller Greetings hello
create app/views/greetings
create app/views/greetings/hello.html.erb
invoke test_unit
- create test/functional/greetings_controller_test.rb
+ create test/controllers/greetings_controller_test.rb
invoke helper
create app/helpers/greetings_helper.rb
invoke test_unit
- create test/unit/helpers/greetings_helper_test.rb
+ create test/helpers/greetings_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/greetings.js.coffee
@@ -223,7 +223,7 @@ $ rails generate scaffold HighScore game:string score:integer
create db/migrate/20120528060026_create_high_scores.rb
create app/models/high_score.rb
invoke test_unit
- create test/unit/high_score_test.rb
+ create test/models/high_score_test.rb
create test/fixtures/high_scores.yml
route resources :high_scores
invoke scaffold_controller
@@ -236,11 +236,11 @@ $ rails generate scaffold HighScore game:string score:integer
create app/views/high_scores/new.html.erb
create app/views/high_scores/_form.html.erb
invoke test_unit
- create test/functional/high_scores_controller_test.rb
+ create test/controllers/high_scores_controller_test.rb
invoke helper
create app/helpers/high_scores_helper.rb
invoke test_unit
- create test/unit/helpers/high_scores_helper_test.rb
+ create test/helpers/high_scores_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/high_scores.js.coffee
@@ -327,7 +327,7 @@ $ rails generate model Oops
create db/migrate/20120528062523_create_oops.rb
create app/models/oops.rb
invoke test_unit
- create test/unit/oops_test.rb
+ create test/models/oops_test.rb
create test/fixtures/oops.yml
```
```bash
@@ -336,7 +336,7 @@ $ rails destroy model Oops
remove db/migrate/20120528062523_create_oops.rb
remove app/models/oops.rb
invoke test_unit
- remove test/unit/oops_test.rb
+ remove test/models/oops_test.rb
remove test/fixtures/oops.yml
```
@@ -480,15 +480,9 @@ The `tmp:` namespaced tasks will help you clear the `Rails.root/tmp` directory:
* `rake secret` will give you a pseudo-random key to use for your session secret.
* `rake time:zones:all` lists all the timezones Rails knows about.
-### Writing Rake Tasks
+### Custom Rake Tasks
-If you have (or want to write) any automation scripts outside your app (data import, checks, etc), you can make them as rake tasks. It's easy.
-
-INFO: [Complete guide about how to write tasks](http://rake.rubyforge.org/files/doc/rakefile_rdoc.html) is available in the official documentation.
-
-Tasks should be placed in `Rails.root/lib/tasks` and should have a `.rake` extension.
-
-Each task should be defined in next format (dependencies are optional):
+Custom rake tasks have a `.rake` extension and are placed in `Rails.root/lib/tasks`.
```ruby
desc "I am short, but comprehensive description for my cool task"
@@ -498,7 +492,7 @@ task :task_name => [:prerequisite_task, :another_task_we_depend_on] do
end
```
-If you need to pass parameters, you can use next format (both arguments and dependencies are optional):
+To pass arguments to your custom rake task:
```ruby
task :task_name, [:arg_1] => [:pre_1, :pre_2] do |t, args|
@@ -509,7 +503,7 @@ end
You can group tasks by placing them in namespaces:
```ruby
-namespace :do
+namespace :db do
desc "This task does nothing"
task :nothing do
# Seriously, nothing
@@ -517,13 +511,13 @@ namespace :do
end
```
-You can see your tasks to be listed by `rake -T` command. And, according to the examples above, you can invoke them as follows:
+Invocation of the tasks will look like:
```bash
rake task_name
rake "task_name[value 1]" # entire argument string should be quoted
-rake do:nothing
-```
+rake db:nothing
+```
NOTE: If your need to interact with your application models, perform database queries and so on, your task should depend on the `environment` task, which will load your application code.
diff --git a/guides/source/engines.md b/guides/source/engines.md
index 9adb5a1725..97af423f3e 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -169,7 +169,7 @@ end
This line mounts the engine at the path `/blorgh`, which will make it accessible through the application only at that path.
-Also in the test directory is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory also. For example, you may wish to create a `test/unit` directory for your unit tests.
+Also in the test directory is the `test/integration` directory, where integration tests for the engine should be placed. Other directories can be created in the `test` directory also. For example, you may wish to create a `test/models` directory for your models tests.
Providing engine functionality
------------------------------
@@ -191,7 +191,7 @@ invoke active_record
create db/migrate/[timestamp]_create_blorgh_posts.rb
create app/models/blorgh/post.rb
invoke test_unit
-create test/unit/blorgh/post_test.rb
+create test/models/blorgh/post_test.rb
create test/fixtures/blorgh/posts.yml
route resources :posts
invoke scaffold_controller
@@ -204,11 +204,11 @@ create app/views/blorgh/posts/show.html.erb
create app/views/blorgh/posts/new.html.erb
create app/views/blorgh/posts/_form.html.erb
invoke test_unit
-create test/functional/blorgh/posts_controller_test.rb
+create test/controllers/blorgh/posts_controller_test.rb
invoke helper
create app/helpers/blorgh/posts_helper.rb
invoke test_unit
-create test/unit/helpers/blorgh/posts_helper_test.rb
+create test/helpers/blorgh/posts_helper_test.rb
invoke assets
invoke js
create app/assets/javascripts/blorgh/posts.js
@@ -220,7 +220,7 @@ create app/assets/stylesheets/scaffold.css
The first thing that the scaffold generator does is invoke the `active_record` generator, which generates a migration and a model for the resource. Note here, however, that the migration is called `create_blorgh_posts` rather than the usual `create_posts`. This is due to the `isolate_namespace` method called in the `Blorgh::Engine` class's definition. The model here is also namespaced, being placed at `app/models/blorgh/post.rb` rather than `app/models/post.rb` due to the `isolate_namespace` call within the `Engine` class.
-Next, the `test_unit` generator is invoked for this model, generating a unit test at `test/unit/blorgh/post_test.rb` (rather than `test/unit/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml` (rather than `test/fixtures/posts.yml`).
+Next, the `test_unit` generator is invoked for this model, generating a model test at `test/models/blorgh/post_test.rb` (rather than `test/models/post_test.rb`) and a fixture at `test/fixtures/blorgh/posts.yml` (rather than `test/fixtures/posts.yml`).
After that, a line for the resource is inserted into the `config/routes.rb` file for the engine. This line is simply `resources :posts`, turning the `config/routes.rb` file for the engine into this:
@@ -232,7 +232,7 @@ end
Note here that the routes are drawn upon the `Blorgh::Engine` object rather than the `YourApp::Application` class. This is so that the engine routes are confined to the engine itself and can be mounted at a specific point as shown in the [test directory](#test-directory) section. This is also what causes the engine's routes to be isolated from those routes that are within the application. This is discussed further in the [Routes](#routes) section of this guide.
-Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::PostsController` (at `app/controllers/blorgh/posts_controller.rb`) and its related views at `app/views/blorgh/posts`. This generator also generates a functional test for the controller (`test/functional/blorgh/posts_controller_test.rb`) and a helper (`app/helpers/blorgh/posts_controller.rb`).
+Next, the `scaffold_controller` generator is invoked, generating a controller called `Blorgh::PostsController` (at `app/controllers/blorgh/posts_controller.rb`) and its related views at `app/views/blorgh/posts`. This generator also generates a test for the controller (`test/controllers/blorgh/posts_controller_test.rb`) and a helper (`app/helpers/blorgh/posts_controller.rb`).
Everything this generator has created is neatly namespaced. The controller's class is defined within the `Blorgh` module:
@@ -300,7 +300,7 @@ invoke active_record
create db/migrate/[timestamp]_create_blorgh_comments.rb
create app/models/blorgh/comment.rb
invoke test_unit
-create test/unit/blorgh/comment_test.rb
+create test/models/blorgh/comment_test.rb
create test/fixtures/blorgh/comments.yml
```
@@ -373,11 +373,11 @@ create app/controllers/blorgh/comments_controller.rb
invoke erb
exist app/views/blorgh/comments
invoke test_unit
-create test/functional/blorgh/comments_controller_test.rb
+create test/controllers/blorgh/comments_controller_test.rb
invoke helper
create app/helpers/blorgh/comments_helper.rb
invoke test_unit
-create test/unit/helpers/blorgh/comments_helper_test.rb
+create test/helpers/blorgh/comments_helper_test.rb
invoke assets
invoke js
create app/assets/javascripts/blorgh/comments.js
diff --git a/guides/source/generators.md b/guides/source/generators.md
index 0bcfa1dc68..d56bbe853c 100644
--- a/guides/source/generators.md
+++ b/guides/source/generators.md
@@ -176,7 +176,7 @@ $ rails generate scaffold User name:string
create db/migrate/20091120125558_create_users.rb
create app/models/user.rb
invoke test_unit
- create test/unit/user_test.rb
+ create test/models/user_test.rb
create test/fixtures/users.yml
route resources :users
invoke scaffold_controller
@@ -189,11 +189,11 @@ $ rails generate scaffold User name:string
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
- create test/functional/users_controller_test.rb
+ create test/controllers/users_controller_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
- create test/unit/helpers/users_helper_test.rb
+ create test/helpers/users_helper_test.rb
invoke stylesheets
create app/assets/stylesheets/scaffold.css
```
@@ -350,7 +350,7 @@ $ rails generate scaffold Comment body:text
create db/migrate/20091120151323_create_comments.rb
create app/models/comment.rb
invoke shoulda
- create test/unit/comment_test.rb
+ create test/models/comment_test.rb
create test/fixtures/comments.yml
route resources :comments
invoke scaffold_controller
@@ -364,11 +364,11 @@ $ rails generate scaffold Comment body:text
create app/views/comments/_form.html.erb
create app/views/layouts/comments.html.erb
invoke shoulda
- create test/functional/comments_controller_test.rb
+ create test/controllers/comments_controller_test.rb
invoke my_helper
create app/helpers/comments_helper.rb
invoke shoulda
- create test/unit/helpers/comments_helper_test.rb
+ create test/helpers/comments_helper_test.rb
```
Fallbacks allow your generators to have a single responsibility, increasing code reuse and reducing the amount of duplication.
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index c0760915ba..9f23f9fc42 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -192,11 +192,11 @@ invoke erb
create app/views/welcome
create app/views/welcome/index.html.erb
invoke test_unit
-create test/functional/welcome_controller_test.rb
+create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
invoke test_unit
-create test/unit/helpers/welcome_helper_test.rb
+create test/helpers/welcome_helper_test.rb
invoke assets
invoke coffee
create app/assets/javascripts/welcome.js.coffee
@@ -1246,7 +1246,7 @@ This command will generate four files:
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| db/migrate/20100207235629_create_comments.rb | Migration to create the comments table in your database (your name will include a different timestamp) |
| app/models/comment.rb | The Comment model |
-| test/unit/comment_test.rb | Unit testing harness for the comments model |
+| test/models/comment_test.rb | Testing harness for the comments model |
| test/fixtures/comments.yml | Sample comments for use in testing |
First, take a look at `comment.rb`:
@@ -1367,15 +1367,15 @@ $ rails generate controller Comments
This creates six files and one empty directory:
-| File/Directory | Purpose |
-| ------------------------------------------- | ---------------------------------------- |
-| app/controllers/comments_controller.rb | The Comments controller |
-| app/views/comments/ | Views of the controller are stored here |
-| test/functional/comments_controller_test.rb | The functional tests for the controller |
-| app/helpers/comments_helper.rb | A view helper file |
-| test/unit/helpers/comments_helper_test.rb | The unit tests for the helper |
-| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller |
-| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller |
+| File/Directory | Purpose |
+| -------------------------------------------- | ---------------------------------------- |
+| app/controllers/comments_controller.rb | The Comments controller |
+| app/views/comments/ | Views of the controller are stored here |
+| test/controllers/comments_controller_test.rb | The test for the controller |
+| app/helpers/comments_helper.rb | A view helper file |
+| test/helpers/comments_helper_test.rb | The test for the helper |
+| app/assets/javascripts/comment.js.coffee | CoffeeScript for the controller |
+| app/assets/stylesheets/comment.css.scss | Cascading style sheet for the controller |
Like with any blog, our readers will create their comments directly after
reading the post, and once they have added their comment, will be sent back to
diff --git a/guides/source/initialization.md b/guides/source/initialization.md
index c760a18dda..c582acd3a3 100644
--- a/guides/source/initialization.md
+++ b/guides/source/initialization.md
@@ -367,7 +367,7 @@ on your needs.
### `Rails::Server#start`
-After `congif/application` is loaded, `server.start` is called. This method is defined like this:
+After `config/application` is loaded, `server.start` is called. This method is defined like this:
```ruby
def start
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index 0a8daf7ae5..397dd62638 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -4,9 +4,9 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+<meta name="viewport" content="width=device-width, initial-scale=1">
<title><%= yield(:page_title) || 'Ruby on Rails Guides' %></title>
-
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
@@ -25,24 +25,30 @@
<% end %>
<div id="topNav">
<div class="wrapper">
- <strong>More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
- <a href="http://rubyonrails.org/">Overview</a> |
- <a href="http://rubyonrails.org/download">Download</a> |
- <a href="http://rubyonrails.org/deploy">Deploy</a> |
- <a href="https://github.com/rails/rails">Code</a> |
- <a href="http://rubyonrails.org/screencasts">Screencasts</a> |
- <a href="http://rubyonrails.org/documentation">Documentation</a> |
- <a href="http://rubyonrails.org/ecosystem">Ecosystem</a> |
- <a href="http://rubyonrails.org/community">Community</a> |
- <a href="http://weblog.rubyonrails.org/">Blog</a>
+ <strong class="more-info-label">More at <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
+ <span class="red-button more-info-button">
+ More Ruby on Rails
+ </span>
+ <ul class="more-info-links s-hidden">
+ <li class="more-info"><a href="http://rubyonrails.org/">Overview</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/download">Download</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/deploy">Deploy</a></li>
+ <li class="more-info"><a href="https://github.com/rails/rails">Code</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/screencasts">Screencasts</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/documentation">Documentation</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/ecosystem">Ecosystem</a></li>
+ <li class="more-info"><a href="http://rubyonrails.org/community">Community</a></li>
+ <li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li>
+ </ul>
</div>
</div>
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="Return to home page">Guides.rubyonrails.org</a></h1>
<ul class="nav">
- <li><a href="index.html">Home</a></li>
- <li class="index"><a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu">Guides Index</a>
+ <li><a class="nav-item" href="index.html">Home</a></li>
+ <li class="guides-index guides-index-large">
+ <a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu" class="guides-index-item nav-item">Guides Index</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<% ['L', 'R'].each do |position| %>
@@ -57,9 +63,22 @@
<% end %>
</div>
</li>
- <li><a href="contributing_to_ruby_on_rails.html">Contribute</a></li>
- <li><a href="credits.html">Credits</a></li>
+ <li><a class="nav-item" href="contributing_to_ruby_on_rails.html">Contribute</a></li>
+ <li><a class="nav-item" href="credits.html">Credits</a></li>
+ <li class="guides-index guides-index-small">
+ <select class="guides-index-item nav-item">
+ <option value="index.html">Guides Index</option>
+ <% docs_for_menu.each do |section| %>
+ <optgroup label="<%= section['name'] %>">
+ <% finished_documents(section['documents']).each do |document| %>
+ <option value="<%= document['url'] %>"><%= document['name'] %></option>
+ <% end %>
+ </optgroup>
+ <% end %>
+ </select>
+ </li>
</ul>
+ </div>
</div>
</div>
<hr class="hide" />
@@ -113,6 +132,8 @@
</div>
</div>
+ <script type="text/javascript" src="javascripts/jquery.min.js"></script>
+ <script type="text/javascript" src="javascripts/responsive-tables.js"></script>
<script type="text/javascript" src="javascripts/guides.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shCore.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushRuby.js"></script>
@@ -121,6 +142,7 @@
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushPlain.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all()
+ $(guidesIndex.bind);
</script>
</body>
</html>
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index f02e86b420..67538b85e8 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -332,7 +332,7 @@ end
As always, what has been generated for you is just a starting point. You can add
or remove from it as you see fit by editing the
-db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb file.
+@db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb@ file.
NOTE: The generated migration file for destructive migrations will still be
old-style using the `up` and `down` methods. This is because Rails needs to know
diff --git a/guides/source/testing.md b/guides/source/testing.md
index 2680525928..b45aba8d55 100644
--- a/guides/source/testing.md
+++ b/guides/source/testing.md
@@ -121,18 +121,18 @@ For this guide we will be using Rails _scaffolding_. It will create the model, a
NOTE: For more information on Rails <i>scaffolding</i>, refer to [Getting Started with Rails](getting_started.html)
-When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/unit` folder:
+When you use `rails generate scaffold`, for a resource among other things it creates a test stub in the `test/models` folder:
```bash
$ rails generate scaffold post title:string body:text
...
create app/models/post.rb
-create test/unit/post_test.rb
+create test/models/post_test.rb
create test/fixtures/posts.yml
...
```
-The default test stub in `test/unit/post_test.rb` looks like this:
+The default test stub in `test/models/post_test.rb` looks like this:
```ruby
require 'test_helper'
@@ -225,9 +225,9 @@ TIP: You can see all these rake tasks and their descriptions by running `rake --
Running a test is as simple as invoking the file containing the test cases through Ruby:
```bash
-$ ruby -Itest test/unit/post_test.rb
+$ ruby -Itest test/models/post_test.rb
-Loaded suite unit/post_test
+Loaded suite models/post_test
Started
.
Finished in 0.023513 seconds.
@@ -240,9 +240,9 @@ This will run all the test methods from the test case. Note that `test_helper.rb
You can also run a particular test method from the test case by using the `-n` switch with the `test method name`.
```bash
-$ ruby -Itest test/unit/post_test.rb -n test_the_truth
+$ ruby -Itest test/models/post_test.rb -n test_the_truth
-Loaded suite unit/post_test
+Loaded suite models/post_test
Started
.
Finished in 0.023513 seconds.
@@ -271,7 +271,7 @@ F
Finished in 0.102072 seconds.
1) Failure:
-test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
+test_should_not_save_post_without_title(PostTest) [/test/models/post_test.rb:6]:
<false> is not true.
1 tests, 1 assertions, 1 failures, 0 errors
@@ -290,7 +290,7 @@ Running this test shows the friendlier assertion message:
```bash
1) Failure:
-test_should_not_save_post_without_title(PostTest) [/test/unit/post_test.rb:6]:
+test_should_not_save_post_without_title(PostTest) [/test/models/post_test.rb:6]:
Saved the post without a title.
<false> is not true.
```
@@ -341,7 +341,7 @@ Finished in 0.082603 seconds.
1) Error:
test_should_report_error(PostTest):
NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x249d354>
- /test/unit/post_test.rb:6:in `test_should_report_error'
+ /test/models/post_test.rb:6:in `test_should_report_error'
1 tests, 0 assertions, 0 failures, 1 errors
```
@@ -420,7 +420,7 @@ You should test for things such as:
* was the correct object stored in the response template?
* was the appropriate message displayed to the user in the view?
-Now that we have used Rails scaffold generator for our `Post` resource, it has already created the controller code and functional tests. You can take look at the file `posts_controller_test.rb` in the `test/functional` directory.
+Now that we have used Rails scaffold generator for our `Post` resource, it has already created the controller code and tests. You can take look at the file `posts_controller_test.rb` in the `test/controllers` directory.
Let me take you through one such test, `test_should_get_index` from the file `posts_controller_test.rb`.
@@ -762,12 +762,16 @@ You don't need to set up and run your tests by hand on a test-by-test basis. Rai
| ------------------------------- | ----------- |
| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake` as the _test_ target is the default.|
| `rake test:benchmark` | Benchmark the performance tests|
-| `rake test:functionals` | Runs all the functional tests from `test/functional`|
+| `rake test:controllers` | Runs all the controller tests from `test/controllers`|
+| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional`|
+| `rake test:helpers` | Runs all the helper tests from `test/helpers`|
| `rake test:integration` | Runs all the integration tests from `test/integration`|
+| `rake test:mailers` | Runs all the mailer tests from `test/mailers`|
+| `rake test:models` | Runs all the model tests from `test/models`|
| `rake test:profile` | Profile the performance tests|
| `rake test:recent` | Tests recent changes|
| `rake test:uncommitted` | Runs all the tests which are uncommitted. Supports Subversion and Git|
-| `rake test:units` | Runs all the unit tests from `test/unit`|
+| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit`|
Brief Note About `Test::Unit`
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 68fe509a33..148316d170 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -69,6 +69,13 @@ in the `config/initializers/wrap_parameters.rb` file:
### Action Pack
+Rails 4.0 has deprecated `ActionController::Base.page_cache_extension` option. Use
+`ActionController::Base.default_static_extension` instead.
+
+Rails 4.0 has removed Action and Page caching from ActionPack. You will need to
+add the `actionpack-action_caching` gem in order to use `caches_action` and
+the `actionpack-page_caching` to use `caches_pages` in your controllers.
+
Rails 4.0 changed how `assert_generates`, `assert_recognizes`, and `assert_routing` work. Now all these assertions raise `Assertion` instead of `ActionController::RoutingError`.
Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, for example:
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index 8c2b64d543..c9fa541db8 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,7 +1,17 @@
## Rails 4.0.0 (unreleased) ##
+* New test locations `test/models`, `test/helpers`, `test/controllers`, and
+ `test/mailers`. Corresponding rake tasks added as well. *Mike Moore*
+
+* Set a different cache per environment for assets pipeline
+ through `config.assets.cache`.
+
+ *Guillermo Iguaran*
+
+* `Rails.public_path` now returns a Pathname object. *Prem Sichanugrist*
+
* Remove highly uncommon `config.assets.manifest` option for moving the manifest path.
- This option is now unsupported in sprockets-rails.
+ This option is now unsupported in sprockets-rails.
*Guillermo Iguaran & Dmitry Vorotilin*
@@ -10,7 +20,7 @@
*Guillermo Iguaran*
-* Remove `config.active_record.whitelist_attributes` and
+* Remove `config.active_record.whitelist_attributes` and
`config.active_record.mass_assignment_sanitizer` from new applications since
MassAssignmentSecurity has been extracted from Rails.
diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb
index a15965a9da..d7e22cc839 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -127,7 +127,7 @@ module Rails
end
def public_path
- application && application.paths["public"].first
+ application && Pathname.new(application.paths["public"].first)
end
end
end
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 050190cba6..b30e6ff615 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -101,6 +101,14 @@ module Rails
routes_reloader.reload!
end
+
+ # Return the application's KeyGenerator
+ def key_generator
+ # number of iterations selected based on consultation with the google security
+ # team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
+ @key_generator ||= ActiveSupport::KeyGenerator.new(config.secret_token, :iterations=>1000)
+ end
+
# Stores some of the Rails initial environment parameters which
# will be used by middlewares and engines to configure themselves.
# Currently stores:
@@ -121,7 +129,8 @@ module Rails
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
"action_dispatch.logger" => Rails.logger,
- "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner
+ "action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
+ "action_dispatch.key_generator" => key_generator
})
end
@@ -282,6 +291,21 @@ module Rails
ActionDispatch::MiddlewareStack.new.tap do |middleware|
app = self
if rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
+ begin
+ require 'rack/cache'
+ rescue LoadError => error
+ error.message << ' Be sure to add rack-cache to your Gemfile'
+ raise
+ end
+
+ if rack_cache == true
+ rack_cache = {
+ :metastore => "rails:/",
+ :entitystore => "rails:/",
+ :verbose => false
+ }
+ end
+
require "action_dispatch/http/rack_cache"
middleware.use ::Rack::Cache, rack_cache
end
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 613c5b25f0..a7a35c2685 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -57,7 +57,7 @@ module Rails
@assets.debug = false
@assets.compile = true
@assets.digest = false
- @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/" ]
+ @assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/#{Rails.env}/" ]
@assets.js_compressor = nil
@assets.css_compressor = nil
@assets.initialize_on_precompile = true
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 4829f3fd45..e761e26b04 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -154,10 +154,8 @@ module Rails
GEMFILE
else
<<-GEMFILE.strip_heredoc
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '#{Rails::VERSION::STRING}'
-
- # Bundle edge Rails instead:
- # gem 'rails', github: 'rails/rails'
GEMFILE
end
end
@@ -226,7 +224,14 @@ module Rails
end
def javascript_gemfile_entry
- "gem '#{options[:javascript]}-rails'" unless options[:skip_javascript]
+ unless options[:skip_javascript]
+ <<-GEMFILE.strip_heredoc
+ gem '#{options[:javascript]}-rails'
+
+ # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
+ gem 'turbolinks'
+ GEMFILE
+ end
end
def javascript_runtime_gemfile_entry
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 417bddaa9d..67e7c4d54f 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -113,9 +113,11 @@ module Rails
def test
empty_directory_with_keep_file 'test/fixtures'
- empty_directory_with_keep_file 'test/functional'
+ empty_directory_with_keep_file 'test/controllers'
+ empty_directory_with_keep_file 'test/mailers'
+ empty_directory_with_keep_file 'test/models'
+ empty_directory_with_keep_file 'test/helpers'
empty_directory_with_keep_file 'test/integration'
- empty_directory_with_keep_file 'test/unit'
template 'test/performance/browsing_test.rb'
template 'test/test_helper.rb'
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 55a6b3f4f2..30f8a5f75e 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -12,7 +12,7 @@ source 'https://rubygems.org'
# To use ActiveModel has_secure_password
# gem 'bcrypt-ruby', '~> 3.0.0'
-# To use Jbuilder templates for JSON
+# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
# gem 'jbuilder'
# Use unicorn as the app server
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
index f33a7f4ac2..7342bffd9d 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
@@ -5,7 +5,7 @@
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-// the compiled file.
+// compiled file.
//
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
// GO AFTER THE REQUIRES BELOW.
@@ -13,5 +13,6 @@
<% unless options[:skip_javascript] -%>
//= require <%= options[:javascript] %>
//= require <%= options[:javascript] %>_ujs
+//= require turbolinks
<% end -%>
//= require_tree .
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 39275e4285..fb43c90e21 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -11,12 +11,8 @@ require "action_mailer/railtie"
<%= comment_if :skip_test_unit %>require "rails/test_unit/railtie"
<% end -%>
-if defined?(Bundler)
- # If you precompile assets before deploying to production, use this line.
- Bundler.require(*Rails.groups(assets: %w(development test)))
- # If you want your assets lazily compiled in production, use this line.
- # Bundler.require(:default, :assets, Rails.env)
-end
+# Assets should be precompiled for production (so we don't need the gems loaded then)
+Bundler.require(*Rails.groups(assets: %w(development test)))
module <%= app_const_base %>
class Application < Rails::Application
@@ -27,17 +23,6 @@ module <%= app_const_base %>
# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
- # config.time_zone = 'Central Time (US & Canada)'
-
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
- # config.i18n.default_locale = :de
-
- # Configure the default encoding used in templates for Ruby 1.9.
- config.encoding = "utf-8"
-
# Configure sensitive parameters which will be filtered from the log file.
config.filter_parameters += [:password]
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index ddfdd9720d..74457b0efd 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -14,6 +14,11 @@
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
+ # Enable Rack::Cache to put a simple HTTP cache in front of your application
+ # Add `rack-cache` to your Gemfile before enabling this.
+ # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
+ # config.action_dispatch.rack_cache = true
+
# Disable Rails's static asset server (Apache or nginx will already do this).
config.serve_static_assets = false
diff --git a/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb b/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
new file mode 100644
index 0000000000..a8285f88ca
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/config/initializers/locale.rb
@@ -0,0 +1,7 @@
+# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
+# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
+# Rails.application.config.time_zone = 'Central Time (US & Canada)'
+
+# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
+# Rails.application.config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
+# Rails.application.config.i18n.default_locale = :de
diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
index f6b1ef1feb..631543c705 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb
@@ -1,23 +1,20 @@
<%= app_const %>.routes.draw do
- # The priority is based upon order of creation:
- # first created -> highest priority.
+ # The priority is based upon order of creation: first created -> highest priority.
+ # See how all your routes lay out with "rake routes".
- # You can have the root of your site routed with "root"
- # just remember to delete public/index.html.
+ # You can have the root of your site routed with "root" just remember to delete public/index.html.
# root to: 'welcome#index'
- # Sample of regular route:
+ # Example of regular route:
# get 'products/:id' => 'catalog#view'
- # Keep in mind you can assign values other than :controller and :action.
- # Sample of named route:
+ # Example of named route that can be invoked with purchase_url(id: product.id)
# get 'products/:id/purchase' => 'catalog#purchase', as: :purchase
- # This route can be invoked with purchase_url(id: product.id).
- # Sample resource route (maps HTTP verbs to controller actions automatically):
+ # Example resource route (maps HTTP verbs to controller actions automatically):
# resources :products
- # Sample resource route with options:
+ # Example resource route with options:
# resources :products do
# member do
# get 'short'
@@ -29,13 +26,13 @@
# end
# end
- # Sample resource route with sub-resources:
+ # Example resource route with sub-resources:
# resources :products do
# resources :comments, :sales
# resource :seller
# end
- # Sample resource route with more complex sub-resources:
+ # Example resource route with more complex sub-resources:
# resources :products do
# resources :comments
# resources :sales do
@@ -43,13 +40,10 @@
# end
# end
- # Sample resource route within a namespace:
+ # Example resource route within a namespace:
# namespace :admin do
# # Directs /admin/products/* to Admin::ProductsController
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
-
-
- # See how all your routes lay out with "rake routes".
end
diff --git a/railties/lib/rails/generators/rails/controller/USAGE b/railties/lib/rails/generators/rails/controller/USAGE
index b658777b12..9def4af65c 100644
--- a/railties/lib/rails/generators/rails/controller/USAGE
+++ b/railties/lib/rails/generators/rails/controller/USAGE
@@ -12,7 +12,7 @@ Example:
`rails generate controller CreditCards open debit credit close`
CreditCards controller with URLs like /credit_cards/debit.
- Controller: app/controllers/credit_cards_controller.rb
- Functional Test: test/functional/credit_cards_controller_test.rb
- Views: app/views/credit_cards/debit.html.erb [...]
- Helper: app/helpers/credit_cards_helper.rb
+ Controller: app/controllers/credit_cards_controller.rb
+ Test: test/controllers/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/debit.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
diff --git a/railties/lib/rails/generators/rails/helper/USAGE b/railties/lib/rails/generators/rails/helper/USAGE
index c0ddb0f606..30e323a858 100644
--- a/railties/lib/rails/generators/rails/helper/USAGE
+++ b/railties/lib/rails/generators/rails/helper/USAGE
@@ -13,5 +13,5 @@ Example:
Credit card helper.
Helper: app/helpers/credit_card_helper.rb
- Test: test/unit/helpers/credit_card_helper_test.rb
+ Test: test/helpers/credit_card_helper_test.rb
diff --git a/railties/lib/rails/generators/rails/model/USAGE b/railties/lib/rails/generators/rails/model/USAGE
index c46c86076e..e29e19490e 100644
--- a/railties/lib/rails/generators/rails/model/USAGE
+++ b/railties/lib/rails/generators/rails/model/USAGE
@@ -74,7 +74,7 @@ Examples:
For ActiveRecord and TestUnit it creates:
Model: app/models/account.rb
- Test: test/unit/account_test.rb
+ Test: test/models/account_test.rb
Fixtures: test/fixtures/accounts.yml
Migration: db/migrate/XXX_add_accounts.rb
@@ -88,7 +88,7 @@ Examples:
Module: app/models/admin.rb
Model: app/models/admin/account.rb
- Test: test/unit/admin/account_test.rb
+ Test: test/models/admin/account_test.rb
Fixtures: test/fixtures/admin/accounts.yml
Migration: db/migrate/XXX_add_admin_accounts.rb
diff --git a/railties/lib/rails/generators/rails/observer/USAGE b/railties/lib/rails/generators/rails/observer/USAGE
index d8f32a6a48..177ff49e4a 100644
--- a/railties/lib/rails/generators/rails/observer/USAGE
+++ b/railties/lib/rails/generators/rails/observer/USAGE
@@ -9,4 +9,4 @@ Example:
For ActiveRecord and TestUnit it creates:
Observer: app/models/account_observer.rb
- TestUnit: test/unit/account_observer_test.rb
+ TestUnit: test/models/account_observer_test.rb
diff --git a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
index d5c2cf4c93..121205b254 100644
--- a/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
+++ b/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb
@@ -1,13 +1,50 @@
module Rails
module Generators
class ResourceRouteGenerator < NamedBase # :nodoc:
+
+ # Properly nests namespaces passed into a generator
+ #
+ # $ rails generate resource admin/users/products
+ #
+ # should give you
+ #
+ # namespace :admin do
+ # namespace :users
+ # resources :products
+ # end
+ # end
def add_resource_route
return if options[:actions].present?
- route_config = regular_class_path.collect{ |namespace| "namespace :#{namespace} do " }.join(" ")
- route_config << "resources :#{file_name.pluralize}"
- route_config << " end" * regular_class_path.size
- route route_config
+
+ # iterates over all namespaces and opens up blocks
+ regular_class_path.each_with_index do |namespace, index|
+ write("namespace :#{namespace} do", index + 1)
+ end
+
+ # inserts the primary resource
+ write("resources :#{file_name.pluralize}", route_length + 1)
+
+ # ends blocks
+ regular_class_path.each_index do |index|
+ write("end", route_length - index)
+ end
+
+ # route prepends two spaces onto the front of the string that is passed, this corrects that
+ route route_string[2..-1]
end
+
+ private
+ def route_string
+ @route_string ||= ""
+ end
+
+ def write(str, indent)
+ route_string << "#{" " * indent}#{str}\n"
+ end
+
+ def route_length
+ regular_class_path.length
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/scaffold_controller/USAGE b/railties/lib/rails/generators/rails/scaffold_controller/USAGE
index 5cd51b62d4..8ba4c5ccbc 100644
--- a/railties/lib/rails/generators/rails/scaffold_controller/USAGE
+++ b/railties/lib/rails/generators/rails/scaffold_controller/USAGE
@@ -13,7 +13,7 @@ Example:
`rails generate scaffold_controller CreditCard`
Credit card controller with URLs like /credit_card/debit.
- Controller: app/controllers/credit_cards_controller.rb
- Functional Test: test/functional/credit_cards_controller_test.rb
- Views: app/views/credit_cards/index.html.erb [...]
- Helper: app/helpers/credit_cards_helper.rb
+ Controller: app/controllers/credit_cards_controller.rb
+ Test: test/controllers/credit_cards_controller_test.rb
+ Views: app/views/credit_cards/index.html.erb [...]
+ Helper: app/helpers/credit_cards_helper.rb
diff --git a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
index 2611d61b7a..c53930f994 100644
--- a/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/test_unit/controller/controller_generator.rb
@@ -8,7 +8,7 @@ module TestUnit # :nodoc:
def create_test_files
template 'functional_test.rb',
- File.join('test/functional', class_path, "#{file_name}_controller_test.rb")
+ File.join('test/controllers', class_path, "#{file_name}_controller_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
index 44de759a5b..bcd370098e 100644
--- a/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
+++ b/railties/lib/rails/generators/test_unit/helper/helper_generator.rb
@@ -6,7 +6,7 @@ module TestUnit # :nodoc:
check_class_collision :suffix => "HelperTest"
def create_helper_files
- template 'helper_test.rb', File.join('test/unit/helpers', class_path, "#{file_name}_helper_test.rb")
+ template 'helper_test.rb', File.join('test/helpers', class_path, "#{file_name}_helper_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
index ddc639ae7e..570a733227 100644
--- a/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/mailer/mailer_generator.rb
@@ -7,7 +7,7 @@ module TestUnit # :nodoc:
check_class_collision :suffix => "Test"
def create_test_files
- template "functional_test.rb", File.join('test/functional', class_path, "#{file_name}_test.rb")
+ template "functional_test.rb", File.join('test/mailers', class_path, "#{file_name}_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/model/model_generator.rb b/railties/lib/rails/generators/test_unit/model/model_generator.rb
index f373f43f11..9b73f3561f 100644
--- a/railties/lib/rails/generators/test_unit/model/model_generator.rb
+++ b/railties/lib/rails/generators/test_unit/model/model_generator.rb
@@ -9,7 +9,7 @@ module TestUnit # :nodoc:
check_class_collision :suffix => "Test"
def create_test_file
- template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_test.rb")
+ template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_test.rb")
end
hook_for :fixture_replacement
diff --git a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
index f9b0234b11..8bfb749743 100644
--- a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
+++ b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb
@@ -6,7 +6,7 @@ module TestUnit # :nodoc:
check_class_collision :suffix => "ObserverTest"
def create_test_files
- template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_observer_test.rb")
+ template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_observer_test.rb")
end
end
end
diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
index a9946b9791..0462c15224 100644
--- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb
@@ -12,7 +12,7 @@ module TestUnit # :nodoc:
def create_test_files
template "functional_test.rb",
- File.join("test/functional", controller_class_path, "#{controller_file_name}_controller_test.rb")
+ File.join("test/controllers", controller_class_path, "#{controller_file_name}_controller_test.rb")
end
private
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index 3c2210aaf9..9826aecb54 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -115,7 +115,6 @@ module Rails
class Path
include Enumerable
- attr_reader :path
attr_accessor :glob
def initialize(root, current, paths, options = {})
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index d0d053e7ed..3f59bb8733 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -12,9 +12,6 @@ module Rails
def call(env)
request = ActionDispatch::Request.new(env)
- # Put some space between requests in development logs.
- Rails.logger.info "\n\n" if Rails.env.development?
-
if Rails.logger.respond_to?(:tagged)
Rails.logger.tagged(compute_tags(request)) { call_app(request, env) }
else
@@ -25,6 +22,12 @@ module Rails
protected
def call_app(request, env)
+ # Put some space between requests in development logs.
+ if Rails.env.development?
+ Rails.logger.info ''
+ Rails.logger.info ''
+ end
+
Rails.logger.info started_request_message(request)
@app.call(env)
ensure
diff --git a/railties/lib/rails/tasks/statistics.rake b/railties/lib/rails/tasks/statistics.rake
index 67a6d2d2ac..c1674c72ad 100644
--- a/railties/lib/rails/tasks/statistics.rake
+++ b/railties/lib/rails/tasks/statistics.rake
@@ -6,9 +6,13 @@ STATS_DIRECTORIES = [
%w(Javascripts app/assets/javascripts),
%w(Libraries lib/),
%w(APIs app/apis),
+ %w(Controller\ tests test/controllers),
+ %w(Helper\ tests test/helpers),
+ %w(Model\ tests test/models),
+ %w(Mailer\ tests test/mailers),
%w(Integration\ tests test/integration),
- %w(Functional\ tests test/functional),
- %w(Unit\ tests test/unit)
+ %w(Functional\ tests\ (old) test/functional),
+ %w(Unit\ tests \ (old) test/unit)
].collect { |name, dir| [ name, "#{Rails.root}/#{dir}" ] }.select { |name, dir| File.directory?(dir) }
desc "Report code statistics (KLOCs, etc) from the application"
diff --git a/railties/lib/rails/tasks/tmp.rake b/railties/lib/rails/tasks/tmp.rake
index 0968765b4f..0bb64ced95 100644
--- a/railties/lib/rails/tasks/tmp.rake
+++ b/railties/lib/rails/tasks/tmp.rake
@@ -6,7 +6,9 @@ namespace :tmp do
'tmp/cache',
'tmp/sockets',
'tmp/pids',
- 'tmp/cache/assets' ]
+ 'tmp/cache/assets/development',
+ 'tmp/cache/assets/test',
+ 'tmp/cache/assets/production' ]
tmp_dirs.each { |d| directory d }
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 581ceaf9ce..aed7fd4b14 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -7,6 +7,10 @@ require 'active_support/test_case'
require 'action_controller/test_case'
require 'action_dispatch/testing/integration'
+# Config Rails backtrace in tests.
+require 'rails/backtrace_cleaner'
+MiniTest.backtrace_filter = Rails.backtrace_cleaner
+
# Enable turn if it is available
begin
require 'turn'
@@ -25,8 +29,8 @@ if defined?(ActiveRecord::Base)
ActionDispatch::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path
- def create_fixtures(*table_names, &block)
- Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
+ def create_fixtures(*fixture_set_names, &block)
+ FixtureSet.create_fixtures(ActiveSupport::TestCase.fixture_path, fixture_set_names, {}, &block)
end
end
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 0de4afe905..63cb955d44 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -15,11 +15,11 @@ def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago)
# Support subdirs in app/models and app/controllers
modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path
- # For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb
+ # For modified files in app/ run the tests for it. ex. /test/controllers/account_controller.rb
test = "#{modified_test_path}/#{source_file}_test.rb"
tests.push test if File.exist?(test)
- # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb
+ # For modified files in app, run tests in subdirs too. ex. /test/controllers/account/*_test.rb
test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}"
FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exist?(test)
@@ -74,7 +74,9 @@ namespace :test do
Rake::TestTask.new(:recent => "test:prepare") do |t|
since = TEST_CHANGES_SINCE
touched = FileList['test/**/*_test.rb'].select { |path| File.mtime(path) > since } +
+ recent_tests('app/models/**/*.rb', 'test/models', since) +
recent_tests('app/models/**/*.rb', 'test/unit', since) +
+ recent_tests('app/controllers/**/*.rb', 'test/controllers', since) +
recent_tests('app/controllers/**/*.rb', 'test/functional', since)
t.libs << 'test'
@@ -95,8 +97,10 @@ namespace :test do
models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ }
controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ }
- unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
- functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
+ unit_tests = models.map { |model| "test/models/#{File.basename(model, '.rb')}_test.rb" } +
+ models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" } +
+ functional_tests = controllers.map { |controller| "test/controllers/#{File.basename(controller, '.rb')}_test.rb" } +
+ controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
(unit_tests + functional_tests).uniq.select { |file| File.exist?(file) }
end
@@ -108,14 +112,34 @@ namespace :test do
t.libs << "test"
end
+ Rails::SubTestTask.new(:models => "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/models/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(:helpers => "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/helpers/**/*_test.rb'
+ end
+
Rails::SubTestTask.new(:units => "test:prepare") do |t|
t.libs << "test"
- t.pattern = 'test/unit/**/*_test.rb'
+ t.pattern = 'test/{models,helpers,unit}/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(:controllers => "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/controllers/**/*_test.rb'
+ end
+
+ Rails::SubTestTask.new(:mailers => "test:prepare") do |t|
+ t.libs << "test"
+ t.pattern = 'test/mailers/**/*_test.rb'
end
Rails::SubTestTask.new(:functionals => "test:prepare") do |t|
t.libs << "test"
- t.pattern = 'test/functional/**/*_test.rb'
+ t.pattern = 'test/{controllers,mailers,functional}/**/*_test.rb'
end
Rails::SubTestTask.new(:integration => "test:prepare") do |t|
diff --git a/railties/test/application/asset_debugging_test.rb b/railties/test/application/asset_debugging_test.rb
index ecacb34cb2..ebafc7d670 100644
--- a/railties/test/application/asset_debugging_test.rb
+++ b/railties/test/application/asset_debugging_test.rb
@@ -50,7 +50,7 @@ module ApplicationTests
end
test "assets aren't concatened when compile is true is on and debug_assets params is true" do
- app_file "config/initializers/compile.rb", "Rails.application.config.assets.compile = true"
+ add_to_env_config "production", "config.assets.compile = true"
ENV["RAILS_ENV"] = "production"
require "#{app_path}/config/environment"
diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb
index 6e9ae6b74e..be97855e1a 100644
--- a/railties/test/application/assets_test.rb
+++ b/railties/test/application/assets_test.rb
@@ -323,7 +323,8 @@ module ApplicationTests
clean_assets!
- files = Dir["#{app_path}/public/assets/**/*", "#{app_path}/tmp/cache/assets/*"]
+ files = Dir["#{app_path}/public/assets/**/*", "#{app_path}/tmp/cache/assets/development/*",
+ "#{app_path}/tmp/cache/assets/test/*", "#{app_path}/tmp/cache/assets/production/*"]
assert_equal 0, files.length, "Expected no assets, but found #{files.join(', ')}"
end
@@ -395,21 +396,6 @@ module ApplicationTests
assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"><\/script>/, last_response.body)
end
- test "assets aren't concatened when compile is true is on and debug_assets params is true" do
- app_with_assets_in_view
- add_to_env_config "production", "config.assets.compile = true"
- add_to_env_config "production", "config.assets.allow_debugging = true"
-
- ENV["RAILS_ENV"] = "production"
- require "#{app_path}/config/environment"
-
- class ::PostsController < ActionController::Base ; end
-
- get '/posts?debug_assets=true'
- assert_match(/<script src="\/assets\/application-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
- assert_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js\?body=1"><\/script>/, last_response.body)
- end
-
test "assets can access model information when precompiling" do
app_file "app/models/post.rb", "class Post; end"
app_file "app/assets/javascripts/application.js", "//= require_tree ."
diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb
index d014e5e362..07d47dc67b 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -139,6 +139,14 @@ module ApplicationTests
assert_instance_of Pathname, Rails.root
end
+ test "Rails.public_path should be a Pathname" do
+ add_to_config <<-RUBY
+ config.paths["public"] = "somewhere"
+ RUBY
+ require "#{app_path}/config/environment"
+ assert_instance_of Pathname, Rails.public_path
+ end
+
test "initialize an eager loaded, cache classes app" do
add_to_config <<-RUBY
config.eager_load = true
@@ -227,7 +235,7 @@ module ApplicationTests
RUBY
require "#{app_path}/config/application"
- assert_equal File.join(app_path, "somewhere"), Rails.public_path
+ assert_equal Pathname.new(app_path).join("somewhere"), Rails.public_path
end
test "config.secret_token is sent in env" do
@@ -324,27 +332,6 @@ module ApplicationTests
assert last_response.body =~ /_xsrf_token_here/
end
- test "config.action_controller.perform_caching = true" do
- make_basic_app do |app|
- app.config.action_controller.perform_caching = true
- end
-
- class ::OmgController < ActionController::Base
- @@count = 0
-
- caches_action :index
- def index
- @@count += 1
- render :text => @@count
- end
- end
-
- get "/"
- res = last_response.body
- get "/"
- assert_equal res, last_response.body # value should be unchanged
- end
-
test "sets ActionDispatch.test_app" do
make_basic_app
assert_equal Rails.application, ActionDispatch.test_app
@@ -454,27 +441,6 @@ module ApplicationTests
end
end
- test "config.action_controller.perform_caching = false" do
- make_basic_app do |app|
- app.config.action_controller.perform_caching = false
- end
-
- class ::OmgController < ActionController::Base
- @@count = 0
-
- caches_action :index
- def index
- @@count += 1
- render :text => @@count
- end
- end
-
- get "/"
- res = last_response.body
- get "/"
- assert_not_equal res, last_response.body
- end
-
test "config.asset_path is not passed through env" do
make_basic_app do |app|
app.config.asset_path = "/omg%s"
@@ -634,6 +600,7 @@ module ApplicationTests
assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions
assert_equal app.env_config['action_dispatch.logger'], Rails.logger
assert_equal app.env_config['action_dispatch.backtrace_cleaner'], Rails.backtrace_cleaner
+ assert_equal app.env_config['action_dispatch.key_generator'], Rails.application.key_generator
end
test "config.colorize_logging default is true" do
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 5268257d62..81f6096be8 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -175,7 +175,7 @@ module ApplicationTests
Dir.chdir("#{app_path}/app") do
require "#{app_path}/config/environment"
- assert_raises(NoMethodError) { [1,2,3].forty_two }
+ assert_raises(NoMethodError) { "hello".exclude? "lo" }
end
end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
index 19d3f3a397..fffe79f9cc 100644
--- a/railties/test/application/middleware/cache_test.rb
+++ b/railties/test/application/middleware/cache_test.rb
@@ -49,6 +49,8 @@ module ApplicationTests
get ':controller(/:action)'
end
RUBY
+
+ add_to_config "config.action_dispatch.rack_cache = true"
end
def test_cache_keeps_if_modified_since
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index 3e096e99f2..b2443e6503 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -56,11 +56,20 @@ module ApplicationTests
assert !middleware.include?("Rack::Sendfile"), "Rack::Sendfile is not included in the default stack unless you set config.action_dispatch.x_sendfile_header"
end
- test "Rack::Cache is present when action_controller.perform_caching is set" do
+ test "Rack::Cache is not included by default" do
add_to_config "config.action_controller.perform_caching = true"
boot!
+ assert !middleware.include?("Rack::Cache"), "Rack::Cache is not included in the default stack unless you set config.action_dispatch.rack_cache"
+ end
+
+ test "Rack::Cache is present when action_controller.perform_caching is set and action_dispatch.rack_cache is set" do
+ add_to_config "config.action_controller.perform_caching = true"
+ add_to_config "config.action_dispatch.rack_cache = true"
+
+ boot!
+
assert_equal "Rack::Cache", middleware.first
end
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index b05fe3aed5..2e7426150c 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -86,12 +86,12 @@ module ApplicationTests
def test_rake_test_error_output
Dir.chdir(app_path){ `rake db:migrate` }
- app_file "test/unit/one_unit_test.rb", <<-RUBY
- raise 'unit'
+ app_file "test/models/one_model_test.rb", <<-RUBY
+ raise 'models'
RUBY
- app_file "test/functional/one_functional_test.rb", <<-RUBY
- raise 'functional'
+ app_file "test/controllers/one_controller_test.rb", <<-RUBY
+ raise 'controllers'
RUBY
app_file "test/integration/one_integration_test.rb", <<-RUBY
@@ -100,8 +100,8 @@ module ApplicationTests
silence_stderr do
output = Dir.chdir(app_path) { `rake test 2>&1` }
- assert_match 'unit', output
- assert_match 'functional', output
+ assert_match 'models', output
+ assert_match 'controllers', output
assert_match 'integration', output
end
end
diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb
index 91ede1cb68..69e89d87ae 100644
--- a/railties/test/commands/console_test.rb
+++ b/railties/test/commands/console_test.rb
@@ -68,40 +68,40 @@ class Rails::ConsoleTest < ActiveSupport::TestCase
def test_default_environment_with_no_rails_env
with_rails_env nil do
start
- assert_match /\sdevelopment\s/, output
+ assert_match(/\sdevelopment\s/, output)
end
end
def test_default_environment_with_rails_env
with_rails_env 'special-production' do
start
- assert_match /\sspecial-production\s/, output
+ assert_match(/\sspecial-production\s/, output)
end
end
def test_e_option
start ['-e', 'special-production']
- assert_match /\sspecial-production\s/, output
+ assert_match(/\sspecial-production\s/, output)
end
def test_environment_option
start ['--environment=special-production']
- assert_match /\sspecial-production\s/, output
+ assert_match(/\sspecial-production\s/, output)
end
def test_rails_env_is_production_when_first_argument_is_p
start ['p']
- assert_match /\sproduction\s/, output
+ assert_match(/\sproduction\s/, output)
end
def test_rails_env_is_test_when_first_argument_is_t
start ['t']
- assert_match /\stest\s/, output
+ assert_match(/\stest\s/, output)
end
def test_rails_env_is_development_when_argument_is_d
start ['d']
- assert_match /\sdevelopment\s/, output
+ assert_match(/\sdevelopment\s/, output)
end
private
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 856a0e163b..91575a38b6 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -26,10 +26,12 @@ DEFAULT_APP_FILES = %w(
log
script/rails
test/fixtures
- test/functional
+ test/controllers
+ test/models
+ test/helpers
+ test/mailers
test/integration
test/performance
- test/unit
vendor
vendor/assets
tmp/cache
@@ -300,6 +302,11 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile", /# gem 'debugger'/
end
+ def test_inclusion_of_rack_cache
+ run_generator
+ assert_file "Gemfile", /gem 'rack-cache'/
+ end
+
def test_template_from_dir_pwd
FileUtils.cd(Rails.root)
assert_match(/It works from file!/, run_generator([destination_root, "-m", "lib/template.rb"]))
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index c3fa9ebb03..5205deafd9 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -28,13 +28,13 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_invokes_helper
run_generator
assert_file "app/helpers/account_helper.rb"
- assert_file "test/unit/helpers/account_helper_test.rb"
+ assert_file "test/helpers/account_helper_test.rb"
end
def test_does_not_invoke_helper_if_required
run_generator ["account", "--skip-helper"]
assert_no_file "app/helpers/account_helper.rb"
- assert_no_file "test/unit/helpers/account_helper_test.rb"
+ assert_no_file "test/helpers/account_helper_test.rb"
end
def test_invokes_assets
@@ -45,12 +45,12 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/account_controller_test.rb"
+ assert_file "test/controllers/account_controller_test.rb"
end
def test_does_not_invoke_test_framework_if_required
run_generator ["account", "--no-test-framework"]
- assert_no_file "test/functional/account_controller_test.rb"
+ assert_no_file "test/controllers/account_controller_test.rb"
end
def test_invokes_default_template_engine
diff --git a/railties/test/generators/helper_generator_test.rb b/railties/test/generators/helper_generator_test.rb
index 8da3aa61a4..81d4fcb129 100644
--- a/railties/test/generators/helper_generator_test.rb
+++ b/railties/test/generators/helper_generator_test.rb
@@ -15,7 +15,7 @@ class HelperGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/helpers/admin_helper_test.rb", /class AdminHelperTest < ActionView::TestCase/
+ assert_file "test/helpers/admin_helper_test.rb", /class AdminHelperTest < ActionView::TestCase/
end
def test_logs_if_the_test_framework_cannot_be_found
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index c501780e7f..6b2351fc1a 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -29,7 +29,7 @@ class MailerGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/notifier_test.rb" do |test|
+ assert_file "test/mailers/notifier_test.rb" do |test|
assert_match(/class NotifierTest < ActionMailer::TestCase/, test)
assert_match(/test "foo"/, test)
assert_match(/test "bar"/, test)
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 436de26826..a90ad5cde0 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -157,7 +157,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/create_table :products/, up)
assert_match(/t\.string :name/, up)
assert_match(/t\.integer :supplier_id/, up)
-
+
assert_match(/add_index :products, :name/, up)
assert_match(/add_index :products, :supplier_id/, up)
assert_no_match(/add_index :products, :year/, up)
@@ -181,7 +181,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
assert_match(/add_index :products, :discount, unique: true/, content)
end
end
-
+
def test_migration_without_timestamps
ActiveRecord::Base.timestamped_migrations = false
run_generator ["account"]
@@ -269,7 +269,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
+ assert_file "test/models/account_test.rb", /class AccountTest < ActiveSupport::TestCase/
assert_file "test/fixtures/accounts.yml", /name: MyString/, /age: 1/
end
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index db2b8af217..d48712e51f 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -25,7 +25,7 @@ class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase
/module TestApp/,
/ class AccountController < ApplicationController/
- assert_file "test/functional/test_app/account_controller_test.rb",
+ assert_file "test/controllers/test_app/account_controller_test.rb",
/module TestApp/,
/ class AccountControllerTest/
end
@@ -46,12 +46,12 @@ class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase
def test_helpr_is_also_namespaced
run_generator
assert_file "app/helpers/test_app/account_helper.rb", /module TestApp/, / module AccountHelper/
- assert_file "test/unit/helpers/test_app/account_helper_test.rb", /module TestApp/, / class AccountHelperTest/
+ assert_file "test/helpers/test_app/account_helper_test.rb", /module TestApp/, / class AccountHelperTest/
end
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/test_app/account_controller_test.rb"
+ assert_file "test/controllers/test_app/account_controller_test.rb"
end
def test_invokes_default_template_engine
@@ -136,7 +136,7 @@ class NamespacedModelGeneratorTest < NamespacedGeneratorTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/test_app/account_test.rb", /module TestApp/, /class AccountTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/account_test.rb", /module TestApp/, /class AccountTest < ActiveSupport::TestCase/
assert_file "test/fixtures/test_app/accounts.yml", /name: MyString/, /age: 1/
end
end
@@ -158,7 +158,7 @@ class NamespacedObserverGeneratorTest < NamespacedGeneratorTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/test_app/account_observer_test.rb", /module TestApp/, / class AccountObserverTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/account_observer_test.rb", /module TestApp/, / class AccountObserverTest < ActiveSupport::TestCase/
end
end
@@ -186,7 +186,7 @@ class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/functional/test_app/notifier_test.rb" do |test|
+ assert_file "test/mailers/test_app/notifier_test.rb" do |test|
assert_match(/module TestApp/, test)
assert_match(/class NotifierTest < ActionMailer::TestCase/, test)
assert_match(/test "foo"/, test)
@@ -225,7 +225,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Model
assert_file "app/models/test_app/product_line.rb", /module TestApp\n class ProductLine < ActiveRecord::Base/
- assert_file "test/unit/test_app/product_line_test.rb", /module TestApp\n class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/product_line_test.rb", /module TestApp\n class ProductLineTest < ActiveSupport::TestCase/
assert_file "test/fixtures/test_app/product_lines.yml"
assert_migration "db/migrate/create_test_app_product_lines.rb"
@@ -240,7 +240,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
/module TestApp/,
/class ProductLinesController < ApplicationController/
- assert_file "test/functional/test_app/product_lines_controller_test.rb",
+ assert_file "test/controllers/test_app/product_lines_controller_test.rb",
/module TestApp\n class ProductLinesControllerTest < ActionController::TestCase/
# Views
@@ -255,7 +255,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_file "app/helpers/test_app/product_lines_helper.rb"
- assert_file "test/unit/helpers/test_app/product_lines_helper_test.rb"
+ assert_file "test/helpers/test_app/product_lines_helper_test.rb"
# Stylesheets
assert_file "app/assets/stylesheets/scaffold.css"
@@ -267,7 +267,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Model
assert_no_file "app/models/test_app/product_line.rb"
- assert_no_file "test/unit/test_app/product_line_test.rb"
+ assert_no_file "test/models/test_app/product_line_test.rb"
assert_no_file "test/fixtures/test_app/product_lines.yml"
assert_no_migration "db/migrate/create_test_app_product_lines.rb"
@@ -278,7 +278,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Controller
assert_no_file "app/controllers/test_app/product_lines_controller.rb"
- assert_no_file "test/functional/test_app/product_lines_controller_test.rb"
+ assert_no_file "test/controllers/test_app/product_lines_controller_test.rb"
# Views
assert_no_file "app/views/test_app/product_lines"
@@ -286,7 +286,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_no_file "app/helpers/test_app/product_lines_helper.rb"
- assert_no_file "test/unit/helpers/test_app/product_lines_helper_test.rb"
+ assert_no_file "test/helpers/test_app/product_lines_helper_test.rb"
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
@@ -298,13 +298,13 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Model
assert_file "app/models/test_app/admin.rb", /module TestApp\n module Admin/
assert_file "app/models/test_app/admin/role.rb", /module TestApp\n class Admin::Role < ActiveRecord::Base/
- assert_file "test/unit/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/models/test_app/admin/role_test.rb", /module TestApp\n class Admin::RoleTest < ActiveSupport::TestCase/
assert_file "test/fixtures/test_app/admin/roles.yml"
assert_migration "db/migrate/create_test_app_admin_roles.rb"
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller
@@ -312,7 +312,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
assert_match(/module TestApp\n class Admin::RolesController < ApplicationController/, content)
end
- assert_file "test/functional/test_app/admin/roles_controller_test.rb",
+ assert_file "test/controllers/test_app/admin/roles_controller_test.rb",
/module TestApp\n class Admin::RolesControllerTest < ActionController::TestCase/
# Views
@@ -327,7 +327,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_file "app/helpers/test_app/admin/roles_helper.rb"
- assert_file "test/unit/helpers/test_app/admin/roles_helper_test.rb"
+ assert_file "test/helpers/test_app/admin/roles_helper_test.rb"
# Stylesheets
assert_file "app/assets/stylesheets/scaffold.css"
@@ -340,18 +340,18 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Model
assert_file "app/models/test_app/admin.rb" # ( should not be remove )
assert_no_file "app/models/test_app/admin/role.rb"
- assert_no_file "test/unit/test_app/admin/role_test.rb"
+ assert_no_file "test/models/test_app/admin/role_test.rb"
assert_no_file "test/fixtures/test_app/admin/roles.yml"
assert_no_migration "db/migrate/create_test_app_admin_roles.rb"
# Route
assert_file "config/routes.rb" do |route|
- assert_no_match(/namespace :admin do resources :roles end$/, route)
+ assert_no_match(/^ namespace :admin do\n resources :roles\n end$$/, route)
end
# Controller
assert_no_file "app/controllers/test_app/admin/roles_controller.rb"
- assert_no_file "test/functional/test_app/admin/roles_controller_test.rb"
+ assert_no_file "test/controllers/test_app/admin/roles_controller_test.rb"
# Views
assert_no_file "app/views/test_app/admin/roles"
@@ -359,7 +359,79 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
# Helpers
assert_no_file "app/helpers/test_app/admin/roles_helper.rb"
- assert_no_file "test/unit/helpers/test_app/admin/roles_helper_test.rb"
+ assert_no_file "test/helpers/test_app/admin/roles_helper_test.rb"
+
+ # Stylesheets (should not be removed)
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_invoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb", /module TestApp\n module Admin/
+ assert_file "app/models/test_app/admin/user/special/role.rb", /module TestApp\n class Admin::User::Special::Role < ActiveRecord::Base/
+ assert_file "test/models/test_app/admin/user/special/role_test.rb", /module TestApp\n class Admin::User::Special::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_file "app/controllers/test_app/admin/user/special/roles_controller.rb" do |content|
+ assert_match(/module TestApp\n class Admin::User::Special::RolesController < ApplicationController/, content)
+ end
+
+ assert_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb",
+ /module TestApp\n class Admin::User::Special::RolesControllerTest < ActionController::TestCase/
+
+ # Views
+ %w(
+ index
+ edit
+ new
+ show
+ _form
+ ).each { |view| assert_file "app/views/test_app/admin/user/special/roles/#{view}.html.erb" }
+ assert_no_file "app/views/layouts/admin/user/special/roles.html.erb"
+
+ # Helpers
+ assert_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_file "test/helpers/test_app/admin/user/special/roles_helper_test.rb"
+
+ # Stylesheets
+ assert_file "app/assets/stylesheets/scaffold.css"
+ end
+
+ def test_scaffold_with_nested_namespace_on_revoke
+ run_generator [ "admin/user/special/role", "name:string", "description:string" ]
+ run_generator [ "admin/user/special/role" ], :behavior => :revoke
+
+ # Model
+ assert_file "app/models/test_app/admin/user/special.rb" # ( should not be remove )
+ assert_no_file "app/models/test_app/admin/user/special/role.rb"
+ assert_no_file "test/models/test_app/admin/user/special/role_test.rb"
+ assert_no_file "test/fixtures/test_app/admin/user/special/roles.yml"
+ assert_no_migration "db/migrate/create_test_app_admin_user_special_roles.rb"
+
+ # Route
+ assert_file "config/routes.rb" do |route|
+ assert_no_match(/^ namespace :admin do\n namespace :user do\n namespace :special do\n resources :roles\n end\n end\n end$/, route)
+ end
+
+ # Controller
+ assert_no_file "app/controllers/test_app/admin/user/special/roles_controller.rb"
+ assert_no_file "test/controllers/test_app/admin/user/special/roles_controller_test.rb"
+
+ # Views
+ assert_no_file "app/views/test_app/admin/user/special/roles"
+
+ # Helpers
+ assert_no_file "app/helpers/test_app/admin/user/special/roles_helper.rb"
+ assert_no_file "test/helpers/test_app/admin/user/special/roles_helper_test.rb"
# Stylesheets (should not be removed)
assert_file "app/assets/stylesheets/scaffold.css"
diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb
index afcee0a2dc..1231827466 100644
--- a/railties/test/generators/observer_generator_test.rb
+++ b/railties/test/generators/observer_generator_test.rb
@@ -17,7 +17,7 @@ class ObserverGeneratorTest < Rails::Generators::TestCase
def test_invokes_default_test_framework
run_generator
- assert_file "test/unit/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/
+ assert_file "test/models/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/
end
def test_logs_if_the_test_framework_cannot_be_found
diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb
index 73804dae45..0ae0841442 100644
--- a/railties/test/generators/resource_generator_test.rb
+++ b/railties/test/generators/resource_generator_test.rb
@@ -18,7 +18,7 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
%w(
app/models/account.rb
- test/unit/account_test.rb
+ test/models/account_test.rb
test/fixtures/accounts.yml
).each { |path| assert_file path }
@@ -33,10 +33,10 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
def test_resource_controller_with_pluralized_class_name
run_generator
assert_file "app/controllers/accounts_controller.rb", /class AccountsController < ApplicationController/
- assert_file "test/functional/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/
+ assert_file "test/controllers/accounts_controller_test.rb", /class AccountsControllerTest < ActionController::TestCase/
assert_file "app/helpers/accounts_helper.rb", /module AccountsHelper/
- assert_file "test/unit/helpers/accounts_helper_test.rb", /class AccountsHelperTest < ActionView::TestCase/
+ assert_file "test/helpers/accounts_helper_test.rb", /class AccountsHelperTest < ActionView::TestCase/
end
def test_resource_controller_with_actions
@@ -62,14 +62,14 @@ class ResourceGeneratorTest < Rails::Generators::TestCase
def test_plural_names_are_singularized
content = run_generator ["accounts".freeze]
assert_file "app/models/account.rb", /class Account < ActiveRecord::Base/
- assert_file "test/unit/account_test.rb", /class AccountTest/
+ assert_file "test/models/account_test.rb", /class AccountTest/
assert_match(/Plural version of the model detected, using singularized version. Override with --force-plural./, content)
end
def test_plural_names_can_be_forced
content = run_generator ["accounts", "--force-plural"]
assert_file "app/models/accounts.rb", /class Accounts < ActiveRecord::Base/
- assert_file "test/unit/accounts_test.rb", /class AccountsTest/
+ assert_file "test/models/accounts_test.rb", /class AccountsTest/
assert_no_match(/Plural version of the model detected/, content)
end
diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb
index aa09343346..38454dfb8b 100644
--- a/railties/test/generators/scaffold_controller_generator_test.rb
+++ b/railties/test/generators/scaffold_controller_generator_test.rb
@@ -57,7 +57,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_helper_are_invoked_with_a_pluralized_name
run_generator
assert_file "app/helpers/users_helper.rb", /module UsersHelper/
- assert_file "test/unit/helpers/users_helper_test.rb", /class UsersHelperTest < ActionView::TestCase/
+ assert_file "test/helpers/users_helper_test.rb", /class UsersHelperTest < ActionView::TestCase/
end
def test_views_are_generated
@@ -75,7 +75,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_functional_tests
run_generator
- assert_file "test/functional/users_controller_test.rb" do |content|
+ assert_file "test/controllers/users_controller_test.rb" do |content|
assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
assert_match(/test "should get index"/, content)
assert_match(/post :create, user: \{ age: @user.age, name: @user.name \}/, content)
@@ -86,7 +86,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_functional_tests_without_attributes
run_generator ["User"]
- assert_file "test/functional/users_controller_test.rb" do |content|
+ assert_file "test/controllers/users_controller_test.rb" do |content|
assert_match(/class UsersControllerTest < ActionController::TestCase/, content)
assert_match(/test "should get index"/, content)
assert_match(/post :create, user: \{ \}/, content)
@@ -97,7 +97,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
def test_skip_helper_if_required
run_generator ["User", "name:string", "age:integer", "--no-helper"]
assert_no_file "app/helpers/users_helper.rb"
- assert_no_file "test/unit/helpers/users_helper_test.rb"
+ assert_no_file "test/helpers/users_helper_test.rb"
end
def test_skip_layout_if_required
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index 40c5188042..dc825c7c99 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -12,7 +12,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_file "app/models/product_line.rb", /class ProductLine < ActiveRecord::Base/
- assert_file "test/unit/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
+ assert_file "test/models/product_line_test.rb", /class ProductLineTest < ActiveSupport::TestCase/
assert_file "test/fixtures/product_lines.yml"
assert_migration "db/migrate/create_product_lines.rb", /belongs_to :product, index: true/
assert_migration "db/migrate/create_product_lines.rb", /references :user, index: true/
@@ -60,7 +60,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
- assert_file "test/functional/product_lines_controller_test.rb" do |test|
+ assert_file "test/controllers/product_lines_controller_test.rb" do |test|
assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test)
assert_match(/post :create, product_line: \{ title: @product_line.title \}/, test)
assert_match(/put :update, id: @product_line, product_line: \{ title: @product_line.title \}/, test)
@@ -78,7 +78,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_file "app/helpers/product_lines_helper.rb"
- assert_file "test/unit/helpers/product_lines_helper_test.rb"
+ assert_file "test/helpers/product_lines_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
@@ -89,7 +89,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
def test_functional_tests_without_attributes
run_generator ["product_line"]
- assert_file "test/functional/product_lines_controller_test.rb" do |content|
+ assert_file "test/controllers/product_lines_controller_test.rb" do |content|
assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, content)
assert_match(/test "should get index"/, content)
assert_match(/post :create, product_line: \{ \}/, content)
@@ -103,7 +103,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_no_file "app/models/product_line.rb"
- assert_no_file "test/unit/product_line_test.rb"
+ assert_no_file "test/models/product_line_test.rb"
assert_no_file "test/fixtures/product_lines.yml"
assert_no_migration "db/migrate/create_product_lines.rb"
@@ -114,7 +114,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Controller
assert_no_file "app/controllers/product_lines_controller.rb"
- assert_no_file "test/functional/product_lines_controller_test.rb"
+ assert_no_file "test/controllers/product_lines_controller_test.rb"
# Views
assert_no_file "app/views/product_lines"
@@ -122,7 +122,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_no_file "app/helpers/product_lines_helper.rb"
- assert_no_file "test/unit/helpers/product_lines_helper_test.rb"
+ assert_no_file "test/helpers/product_lines_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
@@ -136,13 +136,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_file "app/models/admin.rb", /module Admin/
assert_file "app/models/admin/role.rb", /class Admin::Role < ActiveRecord::Base/
- assert_file "test/unit/admin/role_test.rb", /class Admin::RoleTest < ActiveSupport::TestCase/
+ assert_file "test/models/admin/role_test.rb", /class Admin::RoleTest < ActiveSupport::TestCase/
assert_file "test/fixtures/admin/roles.yml"
assert_migration "db/migrate/create_admin_roles.rb"
# Route
assert_file "config/routes.rb" do |route|
- assert_match(/namespace :admin do resources :roles end$/, route)
+ assert_match(/^ namespace :admin do\n resources :roles\n end$/, route)
end
# Controller
@@ -183,7 +183,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
end
end
- assert_file "test/functional/admin/roles_controller_test.rb",
+ assert_file "test/controllers/admin/roles_controller_test.rb",
/class Admin::RolesControllerTest < ActionController::TestCase/
# Views
@@ -198,7 +198,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_file "app/helpers/admin/roles_helper.rb"
- assert_file "test/unit/helpers/admin/roles_helper_test.rb"
+ assert_file "test/helpers/admin/roles_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css", /:visited/
@@ -213,7 +213,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Model
assert_file "app/models/admin.rb" # ( should not be remove )
assert_no_file "app/models/admin/role.rb"
- assert_no_file "test/unit/admin/role_test.rb"
+ assert_no_file "test/models/admin/role_test.rb"
assert_no_file "test/fixtures/admin/roles.yml"
assert_no_migration "db/migrate/create_admin_roles.rb"
@@ -224,7 +224,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Controller
assert_no_file "app/controllers/admin/roles_controller.rb"
- assert_no_file "test/functional/admin/roles_controller_test.rb"
+ assert_no_file "test/controllers/admin/roles_controller_test.rb"
# Views
assert_no_file "app/views/admin/roles"
@@ -232,7 +232,7 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
# Helpers
assert_no_file "app/helpers/admin/roles_helper.rb"
- assert_no_file "test/unit/helpers/admin/roles_helper_test.rb"
+ assert_no_file "test/helpers/admin/roles_helper_test.rb"
# Assets
assert_file "app/assets/stylesheets/scaffold.css"
diff --git a/tools/profile b/tools/profile
index 6cc935bce5..d4cfa13f0a 100755
--- a/tools/profile
+++ b/tools/profile
@@ -1,6 +1,6 @@
#!/usr/bin/env ruby
# Example:
-# tools/profile_requires activesupport/lib/active_support.rb
+# tools/profile activesupport/lib/active_support.rb
abort 'Use REE so you can profile memory and object allocation' unless GC.respond_to?(:enable_stats)
ENV['NO_RELOAD'] ||= '1'