aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Rakefile9
-rw-r--r--actionpack/CHANGELOG.md7
-rw-r--r--actionpack/RUNNING_UNIT_TESTS.rdoc10
-rw-r--r--actionpack/Rakefile2
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb2
-rw-r--r--actionpack/test/dispatch/request/json_params_parsing_test.rb7
-rw-r--r--actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb52
-rw-r--r--actionview/CHANGELOG.md40
-rw-r--r--actionview/RUNNING_UNIT_TESTS.rdoc (renamed from actionview/RUNNING_UNIT_TESTS)0
-rw-r--r--actionview/Rakefile1
-rw-r--r--actionview/lib/action_view/digestor.rb10
-rw-r--r--actionview/lib/action_view/helpers/form_tag_helper.rb4
-rw-r--r--actionview/lib/action_view/helpers/url_helper.rb2
-rw-r--r--actionview/test/fixtures/digestor/level/_recursion.html.erb1
-rw-r--r--actionview/test/fixtures/digestor/level/recursion.html.erb1
-rw-r--r--actionview/test/template/digestor_test.rb25
-rw-r--r--actionview/test/template/form_tag_helper_test.rb17
-rw-r--r--actionview/test/template/url_helper_test.rb10
-rw-r--r--activerecord/CHANGELOG.md7
-rw-r--r--activerecord/examples/performance.rb14
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb4
-rw-r--r--activerecord/lib/active_record/relation.rb15
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb43
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb6
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb2
-rw-r--r--activerecord/test/cases/base_test.rb14
-rw-r--r--activerecord/test/cases/connection_pool_test.rb1
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb20
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb16
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb7
-rw-r--r--activerecord/test/cases/transactions_test.rb24
-rw-r--r--activerecord/test/models/author.rb6
-rw-r--r--activesupport/CHANGELOG.md11
-rw-r--r--activesupport/lib/active_support/cache.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/time/calculations.rb11
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-rw-r--r--activesupport/test/caching_test.rb4
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb6
-rw-r--r--activesupport/test/core_ext/time_ext_test.rb54
-rw-r--r--guides/assets/javascripts/guides.js64
-rw-r--r--guides/assets/stylesheets/main.css1
-rw-r--r--guides/code/getting_started/Gemfile.lock182
-rw-r--r--guides/code/getting_started/app/controllers/comments_controller.rb8
-rw-r--r--guides/code/getting_started/app/controllers/posts_controller.rb10
-rw-r--r--guides/code/getting_started/app/views/welcome/index.html.erb1
-rw-r--r--guides/source/action_view_overview.md6
-rw-r--r--guides/source/active_record_basics.md4
-rw-r--r--guides/source/active_record_callbacks.md2
-rw-r--r--guides/source/active_record_querying.md2
-rw-r--r--guides/source/asset_pipeline.md6
-rw-r--r--guides/source/contributing_to_ruby_on_rails.md4
-rw-r--r--guides/source/engines.md4
-rw-r--r--guides/source/form_helpers.md26
-rw-r--r--guides/source/getting_started.md64
-rw-r--r--guides/source/index.html.erb2
-rw-r--r--guides/source/layout.html.erb4
-rw-r--r--guides/source/layouts_and_rendering.md4
-rw-r--r--guides/source/migrations.md3
-rw-r--r--guides/source/rails_on_rack.md2
-rw-r--r--guides/source/upgrading_ruby_on_rails.md24
-rw-r--r--install.rb2
-rw-r--r--railties/CHANGELOG.md4
-rw-r--r--railties/RDOC_MAIN.rdoc4
-rw-r--r--railties/lib/rails/api/task.rb8
-rw-r--r--railties/lib/rails/commands.rb96
-rw-r--r--railties/lib/rails/commands/application.rb28
-rw-r--r--railties/lib/rails/commands/commands_tasks.rb170
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb62
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb2
-rw-r--r--railties/test/application/rake/notes_test.rb4
75 files changed, 831 insertions, 460 deletions
diff --git a/Rakefile b/Rakefile
index 3ca801cc66..08473ba216 100644
--- a/Rakefile
+++ b/Rakefile
@@ -47,20 +47,25 @@ task :install => :build do
end
desc "Generate documentation for the Rails framework"
-Rails::API::RepoTask.new('rdoc')
+if ENV['EDGE']
+ Rails::API::EdgeTask.new('rdoc')
+else
+ Rails::API::StableTask.new('rdoc')
+end
desc 'Bump all versions to match version.rb'
task :update_versions do
require File.dirname(__FILE__) + "/version"
File.open("RAILS_VERSION", "w") do |f|
- f.puts Rails.version
+ f.puts Rails::VERSION::STRING
end
constants = {
"activesupport" => "ActiveSupport",
"activemodel" => "ActiveModel",
"actionpack" => "ActionPack",
+ "actionview" => "ActionView",
"actionmailer" => "ActionMailer",
"activerecord" => "ActiveRecord",
"railties" => "Rails"
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md
index e2a6ced1dc..ea1d090bc2 100644
--- a/actionpack/CHANGELOG.md
+++ b/actionpack/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Fix `ActionDispatch::ParamsParser#parse_formatted_parameters` to rewind body input stream on
+ parsing json params.
+
+ Fixes #11345
+
+ *Yuri Bol*, *Paul Nikitochkin*
+
* Ignore spaces around delimiter in Set-Cookie header.
*Yamagishi Kazutoshi*
diff --git a/actionpack/RUNNING_UNIT_TESTS.rdoc b/actionpack/RUNNING_UNIT_TESTS.rdoc
index 1b29abd2d1..08767ae133 100644
--- a/actionpack/RUNNING_UNIT_TESTS.rdoc
+++ b/actionpack/RUNNING_UNIT_TESTS.rdoc
@@ -15,13 +15,3 @@ To run a single test suite
which can be further narrowed down to one test:
rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"
-
-== Dependency on Active Record and database setup
-
-Test cases in the test/active_record/ directory depend on having
-activerecord and sqlite installed. If Active Record is not in
-actionpack/../activerecord directory, or the sqlite rubygem is not installed,
-these tests are skipped.
-
-Other tests are runnable from a fresh copy of actionpack without any configuration.
-
diff --git a/actionpack/Rakefile b/actionpack/Rakefile
index 114d3a7c0c..97ab30ada0 100644
--- a/actionpack/Rakefile
+++ b/actionpack/Rakefile
@@ -19,7 +19,7 @@ end
namespace :test do
task :isolated do
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
- Dir.glob("test/{abstract,controller,dispatch}/**/*_test.rb").all? do |file|
+ Dir.glob("test/{abstract,controller,dispatch,assertions,journey}/**/*_test.rb").all? do |file|
sh(ruby, '-w', '-Ilib:test', file)
end or raise "Failures"
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 4ca1d35489..b4cf8ad2f7 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -226,7 +226,7 @@ module ActionDispatch
def raw_post
unless @env.include? 'RAW_POST_DATA'
raw_post_body = body
- @env['RAW_POST_DATA'] = raw_post_body.read(@env['CONTENT_LENGTH'].to_i)
+ @env['RAW_POST_DATA'] = raw_post_body.read(content_length)
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
end
@env['RAW_POST_DATA']
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index fb70b60ef6..b426183488 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -41,7 +41,7 @@ module ActionDispatch
when Proc
strategy.call(request.raw_post)
when :json
- data = ActiveSupport::JSON.decode(request.body)
+ data = ActiveSupport::JSON.decode(request.raw_post)
data = {:_json => data} unless data.is_a?(Hash)
Request::Utils.deep_munge(data).with_indifferent_access
else
diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb
index b62ed6a8b2..dba9ab688f 100644
--- a/actionpack/test/dispatch/request/json_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb
@@ -70,6 +70,13 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest
end
end
+ test 'raw_post is not empty for JSON request' do
+ with_test_routing do
+ post '/parse', '{"posts": [{"title": "Post Title"}]}', 'CONTENT_TYPE' => 'application/json'
+ assert_equal '{"posts": [{"title": "Post Title"}]}', request.raw_post
+ end
+ end
+
private
def assert_parses(expected, actual, headers = {})
with_test_routing do
diff --git a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
index 9169658c22..9a77454f30 100644
--- a/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/url_encoded_params_parsing_test.rb
@@ -17,10 +17,9 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
end
test "parses unbalanced query string with array" do
- assert_parses(
- {'location' => ["1", "2"], 'age_group' => ["2"]},
- "location[]=1&location[]=2&age_group[]=2"
- )
+ query = "location[]=1&location[]=2&age_group[]=2"
+ expected = { 'location' => ["1", "2"], 'age_group' => ["2"] }
+ assert_parses expected, query
end
test "parses nested hash" do
@@ -30,9 +29,17 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
"note[viewers][viewer][][type]=Group",
"note[viewers][viewer][][id]=2"
].join("&")
-
- expected = { "note" => { "viewers"=>{"viewer"=>[{ "id"=>"1", "type"=>"User"}, {"type"=>"Group", "id"=>"2"} ]} } }
- assert_parses(expected, query)
+ expected = {
+ "note" => {
+ "viewers" => {
+ "viewer" => [
+ { "id" => "1", "type" => "User" },
+ { "type" => "Group", "id" => "2" }
+ ]
+ }
+ }
+ }
+ assert_parses expected, query
end
test "parses more complex nesting" do
@@ -48,7 +55,6 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
"products[second]=Pc",
"=Save"
].join("&")
-
expected = {
"customers" => {
"boston" => {
@@ -70,13 +76,12 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
"second" => "Pc"
}
}
-
assert_parses expected, query
end
test "parses params with array" do
- query = "selected[]=1&selected[]=2&selected[]=3"
- expected = { "selected" => [ "1", "2", "3" ] }
+ query = "selected[]=1&selected[]=2&selected[]=3"
+ expected = { "selected" => ["1", "2", "3"] }
assert_parses expected, query
end
@@ -88,13 +93,13 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
test "parses params with array prefix and hashes" do
query = "a[][b][c]=d"
- expected = {"a" => [{"b" => {"c" => "d"}}]}
+ expected = { "a" => [{ "b" => { "c" => "d" } }] }
assert_parses expected, query
end
test "parses params with complex nesting" do
query = "a[][b][c][][d][]=e"
- expected = {"a" => [{"b" => {"c" => [{"d" => ["e"]}]}}]}
+ expected = { "a" => [{ "b" => { "c" => [{ "d" => ["e"] }] } }] }
assert_parses expected, query
end
@@ -104,7 +109,6 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
"something_else=blah",
"logo=#{File.expand_path(__FILE__)}"
].join("&")
-
expected = {
"customers" => {
"boston" => {
@@ -116,22 +120,20 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
"something_else" => "blah",
"logo" => File.expand_path(__FILE__),
}
-
assert_parses expected, query
end
test "parses params with Safari 2 trailing null character" do
- query = "selected[]=1&selected[]=2&selected[]=3\0"
- expected = { "selected" => [ "1", "2", "3" ] }
+ query = "selected[]=1&selected[]=2&selected[]=3\0"
+ expected = { "selected" => ["1", "2", "3"] }
assert_parses expected, query
end
test "ambiguous params returns a bad request" do
with_routing do |set|
set.draw do
- post ':action', :to => ::UrlEncodedParamsParsingTest::TestController
+ post ':action', to: ::UrlEncodedParamsParsingTest::TestController
end
-
post "/parse", "foo[]=bar&foo[4]=bar"
assert_response :bad_request
end
@@ -141,7 +143,7 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
def with_test_routing
with_routing do |set|
set.draw do
- post ':action', :to => ::UrlEncodedParamsParsingTest::TestController
+ post ':action', to: ::UrlEncodedParamsParsingTest::TestController
end
yield
end
@@ -151,8 +153,8 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
with_test_routing do
post "/parse", actual
assert_response :ok
- assert_equal(expected, TestController.last_request_parameters)
- assert_utf8(TestController.last_request_parameters)
+ assert_equal expected, TestController.last_request_parameters
+ assert_utf8 TestController.last_request_parameters
end
end
@@ -167,11 +169,11 @@ class UrlEncodedParamsParsingTest < ActionDispatch::IntegrationTest
object.each_value do |v|
case v
when Hash
- assert_utf8(v)
+ assert_utf8 v
when Array
- v.each {|el| assert_utf8(el) }
+ v.each { |el| assert_utf8 el }
else
- assert_utf8(v)
+ assert_utf8 v
end
end
end
diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md
index 921ea2be6f..2981e76ec5 100644
--- a/actionview/CHANGELOG.md
+++ b/actionview/CHANGELOG.md
@@ -1,3 +1,43 @@
+* Fix `link_to` with block and url hashes.
+
+ Before:
+
+ link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') }
+ # => "<a action=\"bar\" controller=\"foo\"><span>Example site</span></a>"
+
+ After:
+
+ link_to(action: 'bar', controller: 'foo') { content_tag(:span, 'Example site') }
+ # => "<a href=\"/\"><span>Example site</span></a>"
+
+ *Murahashi Sanemat Kenichi*
+
+* Fix "Stack Level Too Deep" error when redering recursive partials.
+
+ Fixes #11340.
+
+ *Rafael Mendonça França*
+
+* Added an `enforce_utf8` hash option for `form_tag` method.
+
+ Control to output a hidden input tag with name `utf8` without monkey
+ patching.
+
+ Before:
+
+ form_tag
+ # => '<form>..<input name="utf8" type="hidden" value="&#x2713;" />..</form>'
+
+ After:
+
+ form_tag
+ # => '<form>..<input name="utf8" type="hidden" value="&#x2713;" />..</form>'
+
+ form_tag({}, { :enforce_utf8 => false })
+ # => '<form>....</form>'
+
+ *ma2gedev*
+
* Remove the deprecated `include_seconds` argument from `distance_of_time_in_words`,
pass in an `:include_seconds` hash option to use this feature.
diff --git a/actionview/RUNNING_UNIT_TESTS b/actionview/RUNNING_UNIT_TESTS.rdoc
index a0c35e1810..a0c35e1810 100644
--- a/actionview/RUNNING_UNIT_TESTS
+++ b/actionview/RUNNING_UNIT_TESTS.rdoc
diff --git a/actionview/Rakefile b/actionview/Rakefile
index 1cbd35cda4..25365d3c3d 100644
--- a/actionview/Rakefile
+++ b/actionview/Rakefile
@@ -1,5 +1,4 @@
require 'rake/testtask'
-require 'rake/packagetask'
require 'rubygems/package_task'
desc "Default Task"
diff --git a/actionview/lib/action_view/digestor.rb b/actionview/lib/action_view/digestor.rb
index 9324a1ac50..a674e4d7ea 100644
--- a/actionview/lib/action_view/digestor.rb
+++ b/actionview/lib/action_view/digestor.rb
@@ -7,10 +7,14 @@ module ActionView
@@cache = ThreadSafe::Cache.new
def self.digest(name, format, finder, options = {})
- cache_key = [name, format] + Array.wrap(options[:dependencies])
- @@cache[cache_key.join('.')] ||= begin
+ cache_key = ([name, format] + Array.wrap(options[:dependencies])).join('.')
+ @@cache.fetch(cache_key) do
+ @@cache[cache_key] ||= nil if options[:partial] # Prevent re-entry
+
klass = options[:partial] || name.include?("/_") ? PartialDigestor : Digestor
- klass.new(name, format, finder, options).digest
+ digest = klass.new(name, format, finder, options).digest
+
+ @@cache[cache_key] = digest # Store the value
end
end
diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb
index 3fa7696b83..142c27ace0 100644
--- a/actionview/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionview/lib/action_view/helpers/form_tag_helper.rb
@@ -38,6 +38,7 @@ module ActionView
# * A list of parameters to feed to the URL the form will be posted to.
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
# submit behavior. By default this behavior is an ajax submit.
+ # * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name utf8 is not output.
#
# ==== Examples
# form_tag('/posts')
@@ -719,7 +720,8 @@ module ActionView
method_tag(method) + token_tag(authenticity_token)
end
- tags = utf8_enforcer_tag << method_tag
+ enforce_utf8 = html_options.delete("enforce_utf8") { true }
+ tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
end
diff --git a/actionview/lib/action_view/helpers/url_helper.rb b/actionview/lib/action_view/helpers/url_helper.rb
index 19e5941971..daa9a393b3 100644
--- a/actionview/lib/action_view/helpers/url_helper.rb
+++ b/actionview/lib/action_view/helpers/url_helper.rb
@@ -172,7 +172,7 @@ module ActionView
# 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?
+ html_options, options, name = options, name, block if block_given?
options ||= {}
html_options = convert_options_to_data_attributes(options, html_options)
diff --git a/actionview/test/fixtures/digestor/level/_recursion.html.erb b/actionview/test/fixtures/digestor/level/_recursion.html.erb
new file mode 100644
index 0000000000..ee5aaf09c3
--- /dev/null
+++ b/actionview/test/fixtures/digestor/level/_recursion.html.erb
@@ -0,0 +1 @@
+<%= render 'recursion' %>
diff --git a/actionview/test/fixtures/digestor/level/recursion.html.erb b/actionview/test/fixtures/digestor/level/recursion.html.erb
new file mode 100644
index 0000000000..ee5aaf09c3
--- /dev/null
+++ b/actionview/test/fixtures/digestor/level/recursion.html.erb
@@ -0,0 +1 @@
+<%= render 'recursion' %>
diff --git a/actionview/test/template/digestor_test.rb b/actionview/test/template/digestor_test.rb
index 06735c30d3..07f8d36d93 100644
--- a/actionview/test/template/digestor_test.rb
+++ b/actionview/test/template/digestor_test.rb
@@ -95,6 +95,31 @@ class TemplateDigestorTest < ActionView::TestCase
end
end
+ def test_recursion_in_renders
+ assert digest("level/recursion") # assert recursion is possible
+ assert_not_nil digest("level/recursion") # assert digest is stored
+ end
+
+ def test_chaning_the_top_templete_on_recursion
+ assert digest("level/recursion") # assert recursion is possible
+
+ assert_digest_difference("level/recursion") do
+ change_template("level/recursion")
+ end
+
+ assert_not_nil digest("level/recursion") # assert digest is stored
+ end
+
+ def test_chaning_the_partial_templete_on_recursion
+ assert digest("level/recursion") # assert recursion is possible
+
+ assert_digest_difference("level/recursion") do
+ change_template("level/_recursion")
+ end
+
+ assert_not_nil digest("level/recursion") # assert digest is stored
+ end
+
def test_dont_generate_a_digest_for_missing_templates
assert_equal '', digest("nothing/there")
end
diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb
index 70fc6a588b..22bf438a56 100644
--- a/actionview/test/template/form_tag_helper_test.rb
+++ b/actionview/test/template/form_tag_helper_test.rb
@@ -12,9 +12,10 @@ class FormTagHelperTest < ActionView::TestCase
def hidden_fields(options = {})
method = options[:method]
+ enforce_utf8 = options.fetch(:enforce_utf8, true)
txt = %{<div style="margin:0;padding:0;display:inline">}
- txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
+ txt << %{<input name="utf8" type="hidden" value="&#x2713;" />} if enforce_utf8
if method && !%w(get post).include?(method.to_s)
txt << %{<input name="_method" type="hidden" value="#{method}" />}
end
@@ -110,6 +111,20 @@ class FormTagHelperTest < ActionView::TestCase
assert_dom_equal expected, actual
end
+ def test_form_tag_enforce_utf8_true
+ actual = form_tag({}, { :enforce_utf8 => true })
+ expected = whole_form("http://www.example.com", :enforce_utf8 => true)
+ assert_dom_equal expected, actual
+ assert actual.html_safe?
+ end
+
+ def test_form_tag_enforce_utf8_false
+ actual = form_tag({}, { :enforce_utf8 => false })
+ expected = whole_form("http://www.example.com", :enforce_utf8 => false)
+ assert_dom_equal expected, actual
+ assert actual.html_safe?
+ end
+
def test_form_tag_with_block_in_erb
output_buffer = render_erb("<%= form_tag('http://www.example.com') do %>Hello world!<% end %>")
diff --git a/actionview/test/template/url_helper_test.rb b/actionview/test/template/url_helper_test.rb
index 8373d7f992..851ea8796f 100644
--- a/actionview/test/template/url_helper_test.rb
+++ b/actionview/test/template/url_helper_test.rb
@@ -308,6 +308,13 @@ class UrlHelperTest < ActiveSupport::TestCase
link_to('/', class: "special") { content_tag(:span, 'Example site') }
end
+ def test_link_tag_using_block_and_hash
+ assert_dom_equal(
+ %{<a href="/"><span>Example site</span></a>},
+ link_to(url_hash) { content_tag(:span, 'Example site') }
+ )
+ end
+
def test_link_tag_using_block_in_erb
out = render_erb %{<%= link_to('/') do %>Example site<% end %>}
assert_equal '<a href="/">Example site</a>', out
@@ -336,8 +343,6 @@ class UrlHelperTest < ActiveSupport::TestCase
assert_dom_equal %{<a href="/">Listing</a>},
link_to_unless(false, "Listing", url_hash)
- assert_equal "Showing", link_to_unless(true, "Showing", url_hash)
-
assert_equal "<strong>Showing</strong>",
link_to_unless(true, "Showing", url_hash) { |name|
"<strong>#{name}</strong>".html_safe
@@ -357,7 +362,6 @@ class UrlHelperTest < ActiveSupport::TestCase
def test_link_to_if
assert_equal "Showing", link_to_if(false, "Showing", url_hash)
assert_dom_equal %{<a href="/">Listing</a>}, link_to_if(true, "Listing", url_hash)
- assert_equal "Showing", link_to_if(false, "Showing", url_hash)
end
def request_for_url(url, opts = {})
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f59f79112e..1eb2f2d130 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Remove extra select and update queries on save/touch/destroy ActiveRecord model
+ with belongs to reflection with option `touch: true`.
+
+ Fixes: #11288
+
+ *Paul Nikitochkin*
+
* Remove deprecated nil-passing to the following `SchemaCache` methods:
`primary_keys`, `tables`, `columns` and `columns_hash`.
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index 35f13d2438..d3546ce948 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -43,6 +43,8 @@ class Exhibit < ActiveRecord::Base
def self.feel(exhibits) exhibits.each { |e| e.feel } end
end
+def progress_bar(int); print "." if (int%100).zero? ; end
+
puts 'Generating data...'
module ActiveRecord
@@ -75,7 +77,7 @@ notes = ActiveRecord::Faker::LOREM.join ' '
today = Date.today
puts "Inserting #{RECORDS} users and exhibits..."
-RECORDS.times do
+RECORDS.times do |record|
user = User.create(
created_at: today,
name: ActiveRecord::Faker.name,
@@ -88,7 +90,9 @@ RECORDS.times do
user: user,
notes: notes
)
+ progress_bar(record)
end
+puts "Done!\n"
Benchmark.ips(TIME) do |x|
ar_obj = Exhibit.find(1)
@@ -117,10 +121,18 @@ Benchmark.ips(TIME) do |x|
Exhibit.first.look
end
+ x.report 'Model.take' do
+ Exhibit.take
+ end
+
x.report("Model.all limit(100)") do
Exhibit.look Exhibit.limit(100)
end
+ x.report("Model.all take(100)") do
+ Exhibit.look Exhibit.take(100)
+ end
+
x.report "Model.all limit(100) with relationship" do
Exhibit.feel Exhibit.limit(100).includes(:user)
end
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index d4e1a0dda1..81293e464d 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -92,7 +92,7 @@ module ActiveRecord::Associations::Builder
end
def self.touch_record(o, foreign_key, name, touch) # :nodoc:
- old_foreign_id = o.attribute_was(foreign_key)
+ old_foreign_id = o.changed_attributes[foreign_key]
if old_foreign_id
klass = o.association(name).klass
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index c07fd67216..b3f97522c2 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -273,7 +273,7 @@ module ActiveRecord
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
#
# person.attribute_for_inspect(:name)
- # # => "\"David Heinemeier Hansson David Heinemeier Hansson D...\""
+ # # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
#
# person.attribute_for_inspect(:created_at)
# # => "\"2012-10-22 00:15:07\""
@@ -281,7 +281,7 @@ module ActiveRecord
value = read_attribute(attr_name)
if value.is_a?(String) && value.length > 50
- "#{value[0..50]}...".inspect
+ "#{value[0..49]}...".inspect
elsif value.is_a?(Date) || value.is_a?(Time)
%("#{value.to_s(:db)}")
else
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index cfff7202a3..75333765aa 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -307,8 +307,8 @@ module ActiveRecord
# QUOTING ==================================================
- # Returns a bind substitution value given a +column+ and list of current
- # +binds+.
+ # Returns a bind substitution value given a bind +index+ and +column+
+ # NOTE: The column param is currently being used by the sqlserver-adapter
def substitute_at(column, index)
Arel::Nodes::BindParam.new '?'
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 612f376c55..4e86e905ed 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -502,12 +502,19 @@ module ActiveRecord
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
def to_sql
@to_sql ||= begin
+ relation = self
+ connection = klass.connection
+ visitor = connection.visitor
+
if eager_loading?
join_dependency = construct_join_dependency
- relation = construct_relation_for_association_find(join_dependency)
- klass.connection.to_sql(relation.arel, relation.bind_values)
- else
- klass.connection.to_sql(arel, bind_values.dup)
+ relation = construct_relation_for_association_find(join_dependency)
+ end
+
+ ast = relation.arel.ast
+ binds = relation.bind_values.dup
+ visitor.accept(ast) do
+ connection.quote(*binds.shift.reverse)
end
end
end
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index bad5886cde..9186b33bd2 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -1,5 +1,7 @@
module ActiveRecord
module FinderMethods
+ ONE_AS_ONE = '1 AS one'
+
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
# If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
# is an integer, find by id coerces its arguments using +to_i+.
@@ -202,7 +204,7 @@ module ActiveRecord
relation = construct_relation_for_association_find(construct_join_dependency)
return false if ActiveRecord::NullRelation === relation
- relation = relation.except(:select, :order).select("1 AS one").limit(1)
+ relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
case conditions
when Array, Hash
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index cc72ab7b2a..0267cdf6e0 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,4 +1,4 @@
-require "cases/helper"
+require 'cases/helper'
require 'models/developer'
require 'models/project'
require 'models/company'
@@ -14,6 +14,8 @@ require 'models/sponsor'
require 'models/member'
require 'models/essay'
require 'models/toy'
+require 'models/invoice'
+require 'models/line_item'
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
@@ -324,6 +326,45 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Topic.find(topic.id)[:replies_count]
end
+ def test_belongs_to_with_touch_option_on_touch
+ line_item = LineItem.create!
+ Invoice.create!(line_items: [line_item])
+
+ assert_queries(1) { line_item.touch }
+ end
+
+ def test_belongs_to_with_touch_option_on_touch_and_removed_parent
+ line_item = LineItem.create!
+ Invoice.create!(line_items: [line_item])
+
+ line_item.invoice = nil
+
+ assert_queries(2) { line_item.touch }
+ end
+
+ def test_belongs_to_with_touch_option_on_update
+ line_item = LineItem.create!
+ Invoice.create!(line_items: [line_item])
+
+ assert_queries(2) { line_item.update amount: 10 }
+ end
+
+ def test_belongs_to_with_touch_option_on_destroy
+ line_item = LineItem.create!
+ Invoice.create!(line_items: [line_item])
+
+ assert_queries(2) { line_item.destroy }
+ end
+
+ def test_belongs_to_with_touch_option_on_touch_and_reassigned_parent
+ line_item = LineItem.create!
+ Invoice.create!(line_items: [line_item])
+
+ line_item.invoice = Invoice.create!
+
+ assert_queries(3) { line_item.touch }
+ end
+
def test_belongs_to_counter_after_update
topic = Topic.create!(title: "37s")
topic.replies.create!(title: "re: 37s", content: "rails")
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 0f9af8a0c3..c63f48e370 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
@@ -437,13 +437,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert george.treasures(true).empty?
end
- def test_deprecated_push_with_attributes_was_removed
- jamis = developers(:jamis)
- assert_raise(NoMethodError) do
- jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
- end
- end
-
def test_associations_with_conditions
assert_equal 3, projects(:active_record).developers.size
assert_equal 1, projects(:active_record).developers_named_david.size
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 ee88fd490a..119e94b831 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -5,6 +5,7 @@ require 'models/reference'
require 'models/job'
require 'models/reader'
require 'models/comment'
+require 'models/rating'
require 'models/tag'
require 'models/tagging'
require 'models/author'
@@ -616,6 +617,11 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal post.author.author_favorites, post.author_favorites
end
+ def test_merge_join_association_with_has_many_through_association_proxy
+ author = authors(:mary)
+ assert_nothing_raised { author.comments.ratings.to_sql }
+ end
+
def test_has_many_association_through_a_has_many_association_with_nonstandard_primary_keys
assert_equal 2, owners(:blackbeard).toys.count
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index ee0150558d..170c4cd9cd 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -32,7 +32,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
- assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
+ assert_equal '"The First Topic Now Has A Title With\nNewlines And ..."', t.attribute_for_inspect(:title)
end
def test_attribute_present
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 395f28f280..3a1830a739 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'active_support/concurrency/latch'
require 'models/post'
require 'models/author'
require 'models/topic'
@@ -1458,21 +1459,20 @@ class BasicsTest < ActiveRecord::TestCase
orig_handler = klass.connection_handler
new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
after_handler = nil
- is_set = false
+ latch1 = ActiveSupport::Concurrency::Latch.new
+ latch2 = ActiveSupport::Concurrency::Latch.new
t = Thread.new do
klass.connection_handler = new_handler
- is_set = true
- Thread.stop
+ latch1.release
+ latch2.await
after_handler = klass.connection_handler
end
- while(!is_set)
- Thread.pass
- end
+ latch1.await
klass.connection_handler = orig_handler
- t.wakeup
+ latch2.release
t.join
assert_equal after_handler, new_handler
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index d5365b695e..2da51ea015 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -118,6 +118,7 @@ module ActiveRecord
connection = cs.first
@pool.remove connection
assert_respond_to t.join.value, :execute
+ connection.close
end
def test_reap_and_active
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index ec2926632c..6f5f29f0da 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -168,26 +168,6 @@ module ActiveRecord
assert_equal Date, bob.favorite_day.class
end
- # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
- # therefore no timezone change is done afterwards when default timezone is changed
- unless current_adapter?(:OracleAdapter)
- # Test DateTime column and defaults, including timezone.
- # FIXME: moment of truth may be Time on 64-bit platforms.
- if bob.moment_of_truth.is_a?(DateTime)
-
- with_env_tz 'US/Eastern' do
- bob.reload
- assert_equal DateTime.local_offset, bob.moment_of_truth.offset
- assert_not_equal 0, bob.moment_of_truth.offset
- assert_not_equal "Z", bob.moment_of_truth.zone
- # US/Eastern is -5 hours from GMT
- assert_equal Rational(-5, 24), bob.moment_of_truth.offset
- assert_match(/\A-05:00\Z/, bob.moment_of_truth.zone)
- assert_equal DateTime::ITALY, bob.moment_of_truth.start
- end
- end
- end
-
assert_instance_of TrueClass, bob.male?
assert_kind_of BigDecimal, bob.wealth
end
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index a8a9b06ec4..626c6aeaf8 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -16,22 +16,6 @@ class PooledConnectionsTest < ActiveRecord::TestCase
@per_test_teardown.each {|td| td.call }
end
- def checkout_connections
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :checkout_timeout => 0.3}))
- @connections = []
- @timed_out = 0
-
- 4.times do
- Thread.new do
- begin
- @connections << ActiveRecord::Base.connection_pool.checkout
- rescue ActiveRecord::ConnectionTimeoutError
- @timed_out += 1
- end
- end.join
- end
- end
-
# Will deadlock due to lack of Monitor timeouts in 1.9
def checkout_checkin_connections(pool_size, threads)
ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :checkout_timeout => 0.5}))
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 5c7751b445..4bcc97ec44 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -56,7 +56,10 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scoping_with_threads
2.times do
- Thread.new { assert DeveloperOrderedBySalary.all.to_sql.include?('salary DESC') }.join
+ Thread.new {
+ assert DeveloperOrderedBySalary.all.to_sql.include?('salary DESC')
+ DeveloperOrderedBySalary.connection.close
+ }.join
end
end
@@ -360,9 +363,11 @@ class DefaultScopingTest < ActiveRecord::TestCase
threads << Thread.new do
Thread.current[:long_default_scope] = true
assert_equal 1, ThreadsafeDeveloper.all.to_a.count
+ ThreadsafeDeveloper.connection.close
end
threads << Thread.new do
assert_equal 1, ThreadsafeDeveloper.all.to_a.count
+ ThreadsafeDeveloper.connection.close
end
threads.each(&:join)
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 6ba7464203..c0cad52b22 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -526,22 +526,20 @@ if current_adapter?(:PostgreSQLAdapter)
# This will cause transactions to overlap and fail unless they are performed on
# separate database connections.
def test_transaction_per_thread
- assert_nothing_raised do
- threads = (1..3).map do
- Thread.new do
- Topic.transaction do
- topic = Topic.find(1)
- topic.approved = !topic.approved?
- topic.save!
- topic.approved = !topic.approved?
- topic.save!
- end
- Topic.connection.close
+ threads = 3.times.map do
+ Thread.new do
+ Topic.transaction do
+ topic = Topic.find(1)
+ topic.approved = !topic.approved?
+ assert topic.save!
+ topic.approved = !topic.approved?
+ assert topic.save!
end
+ Topic.connection.close
end
-
- threads.each { |t| t.join }
end
+
+ threads.each { |t| t.join }
end
# Test for dirty reads among simultaneous transactions.
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index af80b1ba42..feb828de31 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -13,7 +13,11 @@ class Author < ActiveRecord::Base
has_many :posts_with_extension, :class_name => "Post"
has_one :post_about_thinking, -> { where("posts.title like '%thinking%'") }, :class_name => 'Post'
has_one :post_about_thinking_with_last_comment, -> { where("posts.title like '%thinking%'").includes(:last_comment) }, :class_name => 'Post'
- has_many :comments, :through => :posts
+ has_many :comments, through: :posts do
+ def ratings
+ Rating.joins(:comment).merge(self)
+ end
+ end
has_many :comments_containing_the_letter_e, :through => :posts, :source => :comments
has_many :comments_with_order_and_conditions, -> { order('comments.body').where("comments.body like 'Thank%'") }, :through => :posts, :source => :comments
has_many :comments_with_include, -> { includes(:post) }, :through => :posts, :source => :comments
diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index 8c07a034cc..8b0b7c8d70 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -1,3 +1,14 @@
+* Make `Time.at_with_coercion` retain the second fraction and return local time.
+
+ Fixes #11350
+
+ *Neer Friedman*, *Andrew White*
+
+* Make `HashWithIndifferentAccess#select` always return the hash, even when
+ `Hash#select!` returns `nil`, to allow further chaining.
+
+ *Marc Schütz*
+
* Remove deprecated `String#encoding_aware?` core extensions (`core_ext/string/encoding`).
*Arun Agrawal*
diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb
index c539048a85..308a1b2cd9 100644
--- a/activesupport/lib/active_support/cache.rb
+++ b/activesupport/lib/active_support/cache.rb
@@ -408,7 +408,7 @@ module ActiveSupport
options = merged_options(options)
instrument(:exist?, name) do
entry = read_entry(namespaced_key(name, options), options)
- entry && !entry.expired?
+ (entry && !entry.expired?) || false
end
end
diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb
index fa74fee78a..c739cce223 100644
--- a/activesupport/lib/active_support/core_ext/time/calculations.rb
+++ b/activesupport/lib/active_support/core_ext/time/calculations.rb
@@ -33,10 +33,15 @@ class Time
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
# instances can be used when called with a single argument
def at_with_coercion(*args)
- if args.size == 1 && args.first.acts_like?(:time)
- at_without_coercion(args.first.to_i)
+ return at_without_coercion(*args) if args.size != 1
+
+ # Time.at can be called with a time or numerical value
+ time_or_number = args.first
+
+ if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime)
+ at_without_coercion(time_or_number.to_f).getlocal
else
- at_without_coercion(*args)
+ at_without_coercion(time_or_number)
end
end
alias_method :at_without_coercion, :at
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index 95e03ba95c..3da99872c0 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -228,7 +228,7 @@ module ActiveSupport
def to_options!; self end
def select(*args, &block)
- dup.select!(*args, &block)
+ dup.tap {|hash| hash.select!(*args, &block)}
end
# Convert to a regular hash with string keys.
diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb
index ae6eaa4b60..d2382f0f5b 100644
--- a/activesupport/test/caching_test.rb
+++ b/activesupport/test/caching_test.rb
@@ -327,8 +327,8 @@ module CacheStoreBehavior
def test_exist
@cache.write('foo', 'bar')
- assert @cache.exist?('foo')
- assert !@cache.exist?('bar')
+ assert_equal true, @cache.exist?('foo')
+ assert_equal false, @cache.exist?('bar')
end
def test_nil_exist
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 0857796036..2d0c56bef5 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -487,6 +487,12 @@ class HashExtTest < ActiveSupport::TestCase
assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
end
+ def test_indifferent_select_returns_a_hash_when_unchanged
+ hash = ActiveSupport::HashWithIndifferentAccess.new(@strings).select {|k,v| true}
+
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess, hash
+ end
+
def test_indifferent_select_bang
indifferent_strings = ActiveSupport::HashWithIndifferentAccess.new(@strings)
indifferent_strings.select! {|k,v| v == 1}
diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb
index d43cf41201..8741f033b5 100644
--- a/activesupport/test/core_ext/time_ext_test.rb
+++ b/activesupport/test/core_ext/time_ext_test.rb
@@ -700,6 +700,21 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_at_with_datetime_returns_local_time
+ with_env_tz 'US/Eastern' do
+ dt = DateTime.civil(2000, 1, 1, 0, 0, 0, '+0')
+ assert_equal Time.local(1999, 12, 31, 19, 0, 0), Time.at(dt)
+ assert_equal 'EST', Time.at(dt).zone
+ assert_equal(-18000, Time.at(dt).utc_offset)
+
+ # Daylight savings
+ dt = DateTime.civil(2000, 7, 1, 1, 0, 0, '+1')
+ assert_equal Time.local(2000, 6, 30, 20, 0, 0), Time.at(dt)
+ assert_equal 'EDT', Time.at(dt).zone
+ assert_equal(-14400, Time.at(dt).utc_offset)
+ end
+ end
+
def test_at_with_time_with_zone
assert_equal Time.utc(2000, 1, 1, 0, 0, 0), Time.at(ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC']))
@@ -711,6 +726,45 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase
end
end
+ def test_at_with_time_with_zone_returns_local_time
+ with_env_tz 'US/Eastern' do
+ twz = ActiveSupport::TimeWithZone.new(Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['London'])
+ assert_equal Time.local(1999, 12, 31, 19, 0, 0), Time.at(twz)
+ assert_equal 'EST', Time.at(twz).zone
+ assert_equal(-18000, Time.at(twz).utc_offset)
+
+ # Daylight savings
+ twz = ActiveSupport::TimeWithZone.new(Time.utc(2000, 7, 1, 0, 0, 0), ActiveSupport::TimeZone['London'])
+ assert_equal Time.local(2000, 6, 30, 20, 0, 0), Time.at(twz)
+ assert_equal 'EDT', Time.at(twz).zone
+ assert_equal(-14400, Time.at(twz).utc_offset)
+ end
+ end
+
+ def test_at_with_time_microsecond_precision
+ assert_equal Time.at(Time.utc(2000, 1, 1, 0, 0, 0, 111)).to_f, Time.utc(2000, 1, 1, 0, 0, 0, 111).to_f
+ end
+
+ def test_at_with_utc_time
+ with_env_tz 'US/Eastern' do
+ assert_equal Time.utc(2000), Time.at(Time.utc(2000))
+ assert_equal 'UTC', Time.at(Time.utc(2000)).zone
+ assert_equal(0, Time.at(Time.utc(2000)).utc_offset)
+ end
+ end
+
+ def test_at_with_local_time
+ with_env_tz 'US/Eastern' do
+ assert_equal Time.local(2000), Time.at(Time.local(2000))
+ assert_equal 'EST', Time.at(Time.local(2000)).zone
+ assert_equal(-18000, Time.at(Time.local(2000)).utc_offset)
+
+ assert_equal Time.local(2000, 7, 1), Time.at(Time.local(2000, 7, 1))
+ assert_equal 'EDT', Time.at(Time.local(2000, 7, 1)).zone
+ assert_equal(-14400, Time.at(Time.local(2000, 7, 1)).utc_offset)
+ end
+ end
+
def test_eql?
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) )
assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) )
diff --git a/guides/assets/javascripts/guides.js b/guides/assets/javascripts/guides.js
index 7e494fb6d8..e4d25dfb21 100644
--- a/guides/assets/javascripts/guides.js
+++ b/guides/assets/javascripts/guides.js
@@ -1,57 +1,53 @@
-function guideMenu(){
- if (document.getElementById('guides').style.display == "none") {
- document.getElementById('guides').style.display = "block";
- } else {
- document.getElementById('guides').style.display = "none";
- }
-}
-
-$.fn.selectGuide = function(guide){
+$.fn.selectGuide = function(guide) {
$("select", this).val(guide);
-}
+};
-guidesIndex = {
- bind: function(){
+var 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){
+ $(document).on("click", ".more-info-button", function(e){
e.stopPropagation();
- if($(".more-info-links").is(":visible")){
+ 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;
+ });
+ $("#guidesMenu").on("click", function(e) {
+ $("#guides").toggle();
+ return false;
+ });
+ $(document).on("click", function(e){
+ e.stopPropagation();
+ 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;
+ // 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;
+ // Defeat the older Safari bug:
+ // http://www.quirksmode.org/js/events_properties.html
+ if (element.nodeType === 3) element = element.parentNode;
- var $element = $(element);
+ var $element = $(element);
- var $container = $element.parents(".more-info-container");
+ 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");
- }
- });
+ // 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();
+ }
});
},
navigate: function(e){
var $list = $(e.target);
- url = $list.val();
+ var url = $list.val();
window.location = url;
}
-}
+};
diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css
index ca319c91cf..898f9ff05b 100644
--- a/guides/assets/stylesheets/main.css
+++ b/guides/assets/stylesheets/main.css
@@ -129,6 +129,7 @@ body {
font-family: Helvetica, Arial, Sans-Serif;
text-decoration: none;
vertical-align: middle;
+ cursor: pointer;
}
.red-button:active {
border-top: none;
diff --git a/guides/code/getting_started/Gemfile.lock b/guides/code/getting_started/Gemfile.lock
index 888a6b30e2..2d5c50ef5c 100644
--- a/guides/code/getting_started/Gemfile.lock
+++ b/guides/code/getting_started/Gemfile.lock
@@ -1,135 +1,107 @@
-GIT
- remote: git://github.com/rails/activerecord-deprecated_finders.git
- revision: 2e7b35d7948cefb2bba96438873d7f7bb1961a03
- specs:
- activerecord-deprecated_finders (0.0.2)
-
-GIT
- remote: git://github.com/rails/arel.git
- revision: 38d0a222e275d917a2c1d093b24457bafb600a00
- specs:
- arel (3.0.2.20120819075748)
-
-GIT
- remote: git://github.com/rails/coffee-rails.git
- revision: 052634e6d02d4800d7b021201cc8d5829775b3cd
- specs:
- coffee-rails (4.0.0.beta)
- coffee-script (>= 2.2.0)
- railties (>= 4.0.0.beta, < 5.0)
-
-GIT
- remote: git://github.com/rails/sass-rails.git
- revision: ae8138a89cac397c0df903dd533e2862902ce8f5
- specs:
- sass-rails (4.0.0.beta)
- railties (>= 4.0.0.beta, < 5.0)
- sass (>= 3.1.10)
- sprockets-rails (~> 2.0.0.rc0)
- tilt (~> 1.3)
-
-GIT
- remote: git://github.com/rails/sprockets-rails.git
- revision: 09917104fdb42245fe369612a7b0e3d77e1ba763
- specs:
- sprockets-rails (2.0.0.rc1)
- actionpack (>= 3.0)
- activesupport (>= 3.0)
- sprockets (~> 2.8)
-
-PATH
- remote: /Users/steve/src/rails
+GEM
+ remote: https://rubygems.org/
specs:
- actionmailer (4.0.0.beta)
- actionpack (= 4.0.0.beta)
+ actionmailer (4.0.0)
+ actionpack (= 4.0.0)
mail (~> 2.5.3)
- actionpack (4.0.0.beta)
- activesupport (= 4.0.0.beta)
+ actionpack (4.0.0)
+ activesupport (= 4.0.0)
builder (~> 3.1.0)
erubis (~> 2.7.0)
- rack (~> 1.4.3)
- rack-test (~> 0.6.1)
- activemodel (4.0.0.beta)
- activesupport (= 4.0.0.beta)
+ rack (~> 1.5.2)
+ rack-test (~> 0.6.2)
+ activemodel (4.0.0)
+ activesupport (= 4.0.0)
builder (~> 3.1.0)
- activerecord (4.0.0.beta)
- activemodel (= 4.0.0.beta)
- activerecord-deprecated_finders (= 0.0.2)
- activesupport (= 4.0.0.beta)
- arel (~> 3.0.2)
- activesupport (4.0.0.beta)
- i18n (~> 0.6)
- minitest (~> 4.1)
+ activerecord (4.0.0)
+ activemodel (= 4.0.0)
+ activerecord-deprecated_finders (~> 1.0.2)
+ activesupport (= 4.0.0)
+ arel (~> 4.0.0)
+ activerecord-deprecated_finders (1.0.3)
+ activesupport (4.0.0)
+ i18n (~> 0.6, >= 0.6.4)
+ minitest (~> 4.2)
multi_json (~> 1.3)
thread_safe (~> 0.1)
- tzinfo (~> 0.3.33)
- rails (4.0.0.beta)
- actionmailer (= 4.0.0.beta)
- actionpack (= 4.0.0.beta)
- activerecord (= 4.0.0.beta)
- activesupport (= 4.0.0.beta)
- bundler (>= 1.2.2, < 2.0)
- railties (= 4.0.0.beta)
- sprockets-rails (~> 2.0.0.rc1)
- railties (4.0.0.beta)
- actionpack (= 4.0.0.beta)
- activesupport (= 4.0.0.beta)
- rake (>= 0.8.7)
- rdoc (~> 3.4)
- thor (>= 0.15.4, < 2.0)
-
-GEM
- remote: https://rubygems.org/
- specs:
- atomic (1.0.1)
+ tzinfo (~> 0.3.37)
+ arel (4.0.0)
+ atomic (1.1.10)
builder (3.1.4)
+ coffee-rails (4.0.0)
+ coffee-script (>= 2.2.0)
+ railties (>= 4.0.0.beta, < 5.0)
coffee-script (2.2.0)
coffee-script-source
execjs
- coffee-script-source (1.4.0)
+ coffee-script-source (1.6.3)
erubis (2.7.0)
execjs (1.4.0)
multi_json (~> 1.0)
- hike (1.2.1)
- i18n (0.6.1)
- jbuilder (1.3.0)
+ hike (1.2.3)
+ i18n (0.6.4)
+ jbuilder (1.4.2)
activesupport (>= 3.0.0)
- jquery-rails (2.2.0)
+ multi_json (>= 1.2.0)
+ jquery-rails (3.0.2)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
- json (1.7.6)
- mail (2.5.3)
- i18n (>= 0.4.0)
+ json (1.8.0)
+ mail (2.5.4)
mime-types (~> 1.16)
treetop (~> 1.4.8)
- mime-types (1.19)
- minitest (4.4.0)
- multi_json (1.5.0)
+ mime-types (1.23)
+ minitest (4.7.5)
+ multi_json (1.7.7)
polyglot (0.3.3)
- rack (1.4.4)
+ rack (1.5.2)
rack-test (0.6.2)
rack (>= 1.0)
- rake (10.0.3)
- rdoc (3.12)
+ rails (4.0.0)
+ actionmailer (= 4.0.0)
+ actionpack (= 4.0.0)
+ activerecord (= 4.0.0)
+ activesupport (= 4.0.0)
+ bundler (>= 1.3.0, < 2.0)
+ railties (= 4.0.0)
+ sprockets-rails (~> 2.0.0)
+ railties (4.0.0)
+ actionpack (= 4.0.0)
+ activesupport (= 4.0.0)
+ rake (>= 0.8.7)
+ thor (>= 0.18.1, < 2.0)
+ rake (10.1.0)
+ rdoc (3.12.2)
json (~> 1.4)
- sass (3.2.5)
- sprockets (2.8.2)
+ sass (3.2.9)
+ sass-rails (4.0.0)
+ railties (>= 4.0.0.beta, < 5.0)
+ sass (>= 3.1.10)
+ sprockets-rails (~> 2.0.0)
+ sdoc (0.3.20)
+ json (>= 1.1.3)
+ rdoc (~> 3.10)
+ sprockets (2.10.0)
hike (~> 1.2)
multi_json (~> 1.0)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
+ sprockets-rails (2.0.0)
+ actionpack (>= 3.0)
+ activesupport (>= 3.0)
+ sprockets (~> 2.8)
sqlite3 (1.3.7)
- thor (0.16.0)
+ thor (0.18.1)
thread_safe (0.1.0)
atomic
- tilt (1.3.3)
- treetop (1.4.12)
+ tilt (1.4.1)
+ treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
- turbolinks (1.0.0)
+ turbolinks (1.2.0)
coffee-rails
- tzinfo (0.3.35)
- uglifier (1.3.0)
+ tzinfo (0.3.37)
+ uglifier (2.1.1)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
@@ -137,14 +109,12 @@ PLATFORMS
ruby
DEPENDENCIES
- activerecord-deprecated_finders!
- arel!
- coffee-rails!
- jbuilder (~> 1.0.1)
+ coffee-rails
+ jbuilder (~> 1.2)
jquery-rails
- rails!
- sass-rails!
- sprockets-rails!
+ rails (= 4.0.0)
+ sass-rails
+ sdoc
sqlite3
turbolinks
uglifier (>= 1.0.3)
diff --git a/guides/code/getting_started/app/controllers/comments_controller.rb b/guides/code/getting_started/app/controllers/comments_controller.rb
index 0e3d2a6dde..b2d9bcdf7f 100644
--- a/guides/code/getting_started/app/controllers/comments_controller.rb
+++ b/guides/code/getting_started/app/controllers/comments_controller.rb
@@ -4,7 +4,7 @@ class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
- @comment = @post.comments.create(params[:comment].permit(:commenter, :body))
+ @comment = @post.comments.create(comment_params)
redirect_to post_path(@post)
end
@@ -14,4 +14,10 @@ class CommentsController < ApplicationController
@comment.destroy
redirect_to post_path(@post)
end
+
+ private
+
+ def comment_params
+ params.require(:comment).permit(:commenter, :body)
+ end
end
diff --git a/guides/code/getting_started/app/controllers/posts_controller.rb b/guides/code/getting_started/app/controllers/posts_controller.rb
index 6aa1409170..02689ad67b 100644
--- a/guides/code/getting_started/app/controllers/posts_controller.rb
+++ b/guides/code/getting_started/app/controllers/posts_controller.rb
@@ -17,7 +17,7 @@ class PostsController < ApplicationController
def update
@post = Post.find(params[:id])
- if @post.update(params[:post].permit(:title, :text))
+ if @post.update(post_params)
redirect_to action: :show, id: @post.id
else
render 'edit'
@@ -29,7 +29,7 @@ class PostsController < ApplicationController
end
def create
- @post = Post.new(params[:post].permit(:title, :text))
+ @post = Post.new(post_params)
if @post.save
redirect_to action: :show, id: @post.id
@@ -44,4 +44,10 @@ class PostsController < ApplicationController
redirect_to action: :index
end
+
+ private
+
+ def post_params
+ params.require(:post).permit(:title, :text)
+ end
end
diff --git a/guides/code/getting_started/app/views/welcome/index.html.erb b/guides/code/getting_started/app/views/welcome/index.html.erb
index 738e12d7dc..56be8dd3cc 100644
--- a/guides/code/getting_started/app/views/welcome/index.html.erb
+++ b/guides/code/getting_started/app/views/welcome/index.html.erb
@@ -1,3 +1,4 @@
<h1>Hello, Rails!</h1>
<%= link_to "My Blog", controller: "posts" %>
+<%= link_to "New Post", new_post_path %>
diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md
index bdb51d881d..75f2989f5b 100644
--- a/guides/source/action_view_overview.md
+++ b/guides/source/action_view_overview.md
@@ -941,9 +941,9 @@ Creates a form and a scope around a specific model object that is used as a base
```html+erb
<%= form_for @post do |f| %>
<%= f.label :title, 'Title' %>:
- <%= f.text_field :title %><br />
+ <%= f.text_field :title %><br>
<%= f.label :body, 'Body' %>:
- <%= f.text_area :body %><br />
+ <%= f.text_area :body %><br>
<% end %>
```
@@ -1492,7 +1492,7 @@ number_to_human_size(1234567) # => 1.2 MB
Formats a number as a percentage string.
```ruby
-number_to_percentage(100, :precision => 0) # => 100%
+number_to_percentage(100, precision: 0) # => 100%
```
#### number_to_phone
diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md
index d9fb20f3bf..556c2544ff 100644
--- a/guides/source/active_record_basics.md
+++ b/guides/source/active_record_basics.md
@@ -105,7 +105,7 @@ depending on the purpose of these columns.
Migrations](migrations.html) to create your tables, this column will be
automatically created.
-There are also some optional column names that will create additional features
+There are also some optional column names that will add additional features
to Active Record instances:
* `created_at` - Automatically gets set to the current date and time when the
@@ -343,7 +343,7 @@ Migrations
Rails provides a domain-specific language for managing a database schema called
migrations. Migrations are stored in files which are executed against any
-database that Active Record support using `rake`. Here's a migration that
+database that Active Record supports using `rake`. Here's a migration that
creates a table:
```ruby
diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md
index 01401cc340..2e70e64b39 100644
--- a/guides/source/active_record_callbacks.md
+++ b/guides/source/active_record_callbacks.md
@@ -343,7 +343,7 @@ By using the `after_commit` callback we can account for this case.
```ruby
class PictureFile < ActiveRecord::Base
- after_commit :delete_picture_file_from_disk, :on => [:destroy]
+ after_commit :delete_picture_file_from_disk, on: [:destroy]
def delete_picture_file_from_disk
if File.exist?(filepath)
diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md
index 031a203f08..18a5342a2f 100644
--- a/guides/source/active_record_querying.md
+++ b/guides/source/active_record_querying.md
@@ -711,7 +711,7 @@ Post.order('id DESC').limit(20).unscope(:order, :limit) = Post.all
You can additionally unscope specific where clauses. For example:
```ruby
-Post.where(:id => 10).limit(1).unscope(where: :id, :limit).order('id DESC') = Post.order('id DESC')
+Post.where(id: 10).limit(1).unscope(:where, :limit).order('id DESC') = Post.order('id DESC')
```
### `only`
diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md
index b68c24acfd..a6e3d3b0ac 100644
--- a/guides/source/asset_pipeline.md
+++ b/guides/source/asset_pipeline.md
@@ -69,12 +69,12 @@ Rails' old strategy was to append a date-based query string to every asset linke
The query string strategy has several disadvantages:
-1. **Not all caches will reliably cache content where the filename only differs by query parameters**<br />
+1. **Not all caches will reliably cache content where the filename only differs by query parameters**<br>
[Steve Souders recommends](http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/), "...avoiding a querystring for cacheable resources". He found that in this case 5-20% of requests will not be cached. Query strings in particular do not work at all with some CDNs for cache invalidation.
-2. **The file name can change between nodes in multi-server environments.**<br />
+2. **The file name can change between nodes in multi-server environments.**<br>
The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request.
-3. **Too much cache invalidation**<br />
+3. **Too much cache invalidation**<br>
When static assets are deployed with each new release of code, the mtime(time of last modification) of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.
Fingerprinting fixes these problems by avoiding query strings, and by ensuring that filenames are consistent based on their content.
diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md
index b9f42fa51b..24e16ecd40 100644
--- a/guides/source/contributing_to_ruby_on_rails.md
+++ b/guides/source/contributing_to_ruby_on_rails.md
@@ -234,8 +234,8 @@ workflow with the [rails-dev-box](https://github.com/rails/rails-dev-box).
As a compromise, test what your code obviously affects, and if the change is
not in railties run the whole test suite of the affected component. If all is
-green that's enough to propose your contribution. We have [Travis CI](https
-://travis-ci.org/) as a safety net for catching unexpected breakages
+green that's enough to propose your contribution. We have [Travis CI](https://travis-ci.org/rails/rails)
+as a safety net for catching unexpected breakages
elsewhere.
TIP: Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Rails will generally not be accepted.
diff --git a/guides/source/engines.md b/guides/source/engines.md
index bc66ed256e..d714f84731 100644
--- a/guides/source/engines.md
+++ b/guides/source/engines.md
@@ -346,7 +346,7 @@ Next, the partial that this line will render needs to exist. Create a new direct
<h3>New comment</h3>
<%= form_for [@post, @post.comments.build] do |f| %>
<p>
- <%= f.label :text %><br />
+ <%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<%= f.submit %>
@@ -515,7 +515,7 @@ First, the `author_name` text field needs to be added to the `app/views/blorgh/p
```html+erb
<div class="field">
- <%= f.label :author_name %><br />
+ <%= f.label :author_name %><br>
<%= f.text_field :author_name %>
</div>
```
diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md
index 7f37a298b1..11e8db9e88 100644
--- a/guides/source/form_helpers.md
+++ b/guides/source/form_helpers.md
@@ -553,7 +553,7 @@ outputs (with actual option values omitted for brevity)
which results in a `params` hash like
```ruby
-{:person => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}}
+{'person' => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}}
```
When this is passed to `Person.new` (or `update`), Active Record spots that these parameters should all be used to construct the `birth_date` attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as `Date.civil`.
@@ -881,19 +881,19 @@ end
```ruby
{
- :person => {
- :name => 'John Doe',
- :addresses_attributes => {
- '0' => {
- :kind => 'Home',
- :street => '221b Baker Street',
- },
- '1' => {
- :kind => 'Office',
- :street => '31 Spooner Street'
- }
- }
+ 'person' => {
+ 'name' => 'John Doe',
+ 'addresses_attributes' => {
+ '0' => {
+ 'kind' => 'Home',
+ 'street' => '221b Baker Street'
+ },
+ '1' => {
+ 'kind' => 'Office',
+ 'street' => '31 Spooner Street'
+ }
}
+ }
}
```
diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md
index c785fd1f8c..9b2fa315a1 100644
--- a/guides/source/getting_started.md
+++ b/guides/source/getting_started.md
@@ -138,7 +138,7 @@ application. Most of the work in this tutorial will happen in the `app/` folder,
|config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html)|
|config.ru|Rack configuration for Rack based servers used to start the application.|
|db/|Contains your current database schema, as well as the database migrations.|
-|Gemfile<br />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com) |
+|Gemfile<br>Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com) |
|lib/|Extended modules for your application.|
|log/|Application log files.|
|public/|The only folder seen to the world as-is. Contains the static files and compiled assets.|
@@ -264,11 +264,14 @@ Blog::Application.routes.draw do
end
```
-If you run `rake routes`, you'll see that all the routes for the
-standard RESTful actions.
+If you run `rake routes`, you'll see that it has defined routes for all the
+standard RESTful actions. The meaning of the prefix column (and other columns)
+will be seen later, but for now notice that Rails has inferred the
+singular form `post` and makes meaningful use of the distinction.
```bash
$ rake routes
+ Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
@@ -394,9 +397,27 @@ Edit the `form_for` line inside `app/views/posts/new.html.erb` to look like this
<%= form_for :post, url: posts_path do |f| %>
```
-In this example, the `posts_path` helper is passed to the `:url` option. What Rails will do with this is that it will point the form to the `create` action of the current controller, the `PostsController`, and will send a `POST` request to that route.
-
-By using the `post` method rather than the `get` method, Rails will define a route that will only respond to POST methods. The POST method is the typical method used by forms all over the web.
+In this example, the `posts_path` helper is passed to the `:url` option.
+To see what Rails will do with this, we look back at the output of
+`rake routes`:
+```bash
+$ rake routes
+ Prefix Verb URI Pattern Controller#Action
+ posts GET /posts(.:format) posts#index
+ POST /posts(.:format) posts#create
+ new_post GET /posts/new(.:format) posts#new
+edit_post GET /posts/:id/edit(.:format) posts#edit
+ post GET /posts/:id(.:format) posts#show
+ PATCH /posts/:id(.:format) posts#update
+ PUT /posts/:id(.:format) posts#update
+ DELETE /posts/:id(.:format) posts#destroy
+ root / welcome#index
+```
+The `posts_path` helper tells Rails to point the form
+to the URI Pattern associated with the `posts` prefix; and
+the form will (by default) send a `POST` request
+to that route. This is associated with the
+`create` action of the current controller, the `PostsController`.
With the form and its associated route defined, you will be able to fill in the form and then click the submit button to begin the process of creating a new post, so go ahead and do that. When you submit the form, you should see a familiar error:
@@ -723,7 +744,7 @@ TIP: In development mode (which is what you're working in by default), Rails
reloads your application with every browser request, so there's no need to stop
and restart the web server when a change is made.
-### Allowing the update of fields
+### Adding Some Validation
The model file, `app/models/post.rb` is about as simple as it can get:
@@ -738,8 +759,6 @@ your Rails models for free, including basic database CRUD (Create, Read, Update,
Destroy) operations, data validation, as well as sophisticated search support
and the ability to relate multiple models to one another.
-### Adding Some Validation
-
Rails includes methods to help you validate the data that you send to models.
Open the `app/models/post.rb` file and edit it:
@@ -1018,9 +1037,14 @@ content:
```
Everything except for the `form_for` declaration remained the same.
-How `form_for` can figure out the right `action` and `method` attributes when building the form
-will be explained in [just a moment](/form_helpers.html#binding-a-form-to-an-object).
-For now, let's update the `app/views/posts/new.html.erb` view to use this new partial, rewriting it
+The reason we can use this shorter, simpler `form_for` declaration
+to stand in for either of the other forms is that `@post` is a *resource*
+corresponding to a full set of RESTful routes, and Rails is able to infer
+which URI and method to use.
+For more information about this use of `form_for`, see
+[Resource-oriented style](//api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for-label-Resource-oriented+style).
+
+Now, let's update the `app/views/posts/new.html.erb` view to use this new partial, rewriting it
completely:
```html+erb
@@ -1291,11 +1315,11 @@ So first, we'll wire up the Post show template
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
<p>
- <%= f.label :commenter %><br />
+ <%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br />
+ <%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
@@ -1371,11 +1395,11 @@ template. This is where we want the comment to show, so let's add that to the
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
<p>
- <%= f.label :commenter %><br />
+ <%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br />
+ <%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
@@ -1437,11 +1461,11 @@ following:
<h2>Add a comment:</h2>
<%= form_for([@post, @post.comments.build]) do |f| %>
<p>
- <%= f.label :commenter %><br />
+ <%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br />
+ <%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
@@ -1467,11 +1491,11 @@ create a file `app/views/comments/_form.html.erb` containing:
```html+erb
<%= form_for([@post, @post.comments.build]) do |f| %>
<p>
- <%= f.label :commenter %><br />
+ <%= f.label :commenter %><br>
<%= f.text_field :commenter %>
</p>
<p>
- <%= f.label :body %><br />
+ <%= f.label :body %><br>
<%= f.text_area :body %>
</p>
<p>
diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb
index a8e4525c67..57c224c165 100644
--- a/guides/source/index.html.erb
+++ b/guides/source/index.html.erb
@@ -19,7 +19,7 @@ Ruby on Rails Guides
<h3><%= section['name'] %></h3>
<dl>
<% section['documents'].each do |document| %>
- <%= guide(document['name'], document['url'], :work_in_progress => document['work_in_progress']) do %>
+ <%= guide(document['name'], document['url'], work_in_progress: document['work_in_progress']) do %>
<p><%= document['description'] %></p>
<% end %>
<% end %>
diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb
index c737a0e9dc..71d3c5638b 100644
--- a/guides/source/layout.html.erb
+++ b/guides/source/layout.html.erb
@@ -48,7 +48,7 @@
<ul class="nav">
<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>
+ <a href="index.html" 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| %>
@@ -143,7 +143,7 @@
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushSql.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushPlain.js"></script>
<script type="text/javascript">
- SyntaxHighlighter.all()
+ SyntaxHighlighter.all();
$(guidesIndex.bind);
</script>
</body>
diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md
index 5908801bc9..5b6e5387ff 100644
--- a/guides/source/layouts_and_rendering.md
+++ b/guides/source/layouts_and_rendering.md
@@ -88,7 +88,7 @@ If we want to display the properties of all the books in our view, we can do so
<% end %>
</table>
-<br />
+<br>
<%= link_to "New book", new_book_path %>
```
@@ -1026,7 +1026,7 @@ You can also pass local variables into partials, making them even more powerful
```html+erb
<%= form_for(zone) do |f| %>
<p>
- <b>Zone name</b><br />
+ <b>Zone name</b><br>
<%= f.text_field :name %>
</p>
<p>
diff --git a/guides/source/migrations.md b/guides/source/migrations.md
index 035f9499de..e6d1e71f5e 100644
--- a/guides/source/migrations.md
+++ b/guides/source/migrations.md
@@ -376,7 +376,7 @@ create_join_table :products, :categories, column_options: {null: true}
will create the `product_id` and `category_id` with the `:null` option as
`true`.
-You can pass the option `:table_name` with you want to customize the table
+You can pass the option `:table_name` when you want to customize the table
name. For example,
```ruby
@@ -841,7 +841,6 @@ class AddFlagToProduct < ActiveRecord::Migration
reversible do |dir|
dir.up { Product.update_all flag: false }
end
- Product.update_all flag: false
end
end
```
diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md
index d144fba762..b1a7865d10 100644
--- a/guides/source/rails_on_rack.md
+++ b/guides/source/rails_on_rack.md
@@ -326,7 +326,7 @@ The following shows how to replace use `Rack::Builder` instead of the Rails supp
config.middleware.clear
```
-<br />
+<br>
<strong>Add a `config.ru` file to `Rails.root`</strong>
```ruby
diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md
index 0f388d15c4..5992e94cd8 100644
--- a/guides/source/upgrading_ruby_on_rails.md
+++ b/guides/source/upgrading_ruby_on_rails.md
@@ -131,7 +131,15 @@ The following changes are meant for upgrading your application to Rails 4.0.
### Gemfile
-Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that line from your Gemfile when upgrading.
+Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that
+line from your Gemfile when upgrading. You should also update your application
+file (in `config/application.rb`):
+
+```ruby
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(:default, Rails.env)
+```
### vendor/plugins
@@ -149,6 +157,9 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep
* Rails 4.0 has removed `attr_accessible` and `attr_protected` feature in favor of Strong Parameters. You can use the [Protected Attributes gem](https://github.com/rails/protected_attributes) to a smoothly upgrade path.
+* If you are not using Protected Attributes, you can remove any options related to
+this gem such as `whitelist_attributes` or `mass_assignment_sanitizer` options.
+
* Rails 4.0 requires that scopes use a callable object such as a Proc or lambda:
```ruby
@@ -208,6 +219,11 @@ Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for d
* Rails 4.0 deprecates the `dom_id` and `dom_class` methods in controllers (they are fine in views). You will need to include the `ActionView::RecordIdentifier` module in controllers requiring this feature.
+* Rails 4.0 deprecates the `:confirm` option for the `link_to` helper. You should
+instead rely on a data attribute (e.g. `data: { confirm: 'Are you sure?' }`).
+This deprecation also concerns the helpers based on this one (such as `link_to_if`
+or `link_to_unless`).
+
* 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 raises an `ArgumentError` if clashing named routes are defined. This can be triggered by explicitly defined named routes or by the `resources` method. Here are two examples that clash with routes named `example_path`:
@@ -295,6 +311,12 @@ Active Record Observer and Action Controller Sweeper have been extracted to the
### sprockets-rails
* `assets:precompile:primary` has been removed. Use `assets:precompile` instead.
+* The `config.assets.compress` option should be changed to
+`config.assets.js_compressor` like so for instance:
+
+```ruby
+config.assets.js_compressor = :uglifier
+```
### sass-rails
diff --git a/install.rb b/install.rb
index 77c3e6048a..bff8fee934 100644
--- a/install.rb
+++ b/install.rb
@@ -5,7 +5,7 @@ if version.nil?
exit(64)
end
-%w( activesupport activemodel activerecord actionpack actionmailer railties ).each do |framework|
+%w( activesupport activemodel activerecord actionpack actionview actionmailer railties ).each do |framework|
puts "Installing #{framework}..."
`cd #{framework} && gem build #{framework}.gemspec && gem install #{framework}-#{version}.gem --no-ri --no-rdoc && rm #{framework}-#{version}.gem`
end
diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md
index b80aa944e0..cc7fecb28a 100644
--- a/railties/CHANGELOG.md
+++ b/railties/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Fix `rake notes` to look into `*.sass` files
+
+ *Yuri Artemev*
+
* Removed deprecated `Rails.application.railties.engines`.
*Arun Agrawal*
diff --git a/railties/RDOC_MAIN.rdoc b/railties/RDOC_MAIN.rdoc
index cadf0fb43e..aa67005b24 100644
--- a/railties/RDOC_MAIN.rdoc
+++ b/railties/RDOC_MAIN.rdoc
@@ -18,7 +18,7 @@ you to present the data from database rows as objects and embellish these data o
with business logic methods. Although most \Rails models are backed by a database, models
can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as
provided by the ActiveModel module. You can read more about Active Record in its
-{README}[link:/activerecord/README.rdoc].
+{README}[link:files/activerecord/README_rdoc.html].
The Controller layer is responsible for handling incoming HTTP requests and providing a
suitable response. Usually this means returning \HTML, but \Rails controllers can also
@@ -29,7 +29,7 @@ In \Rails, the Controller and View layers are handled together by Action Pack.
These two layers are bundled in a single package due to their heavy interdependence.
This is unlike the relationship between Active Record and Action Pack, which are
independent. Each of these packages can be used independently outside of \Rails. You
-can read more about Action Pack in its {README}[link:/actionpack/README.rdoc].
+can read more about Action Pack in its {README}[link:files/actionpack/README_rdoc.html].
== Getting Started
diff --git a/railties/lib/rails/api/task.rb b/railties/lib/rails/api/task.rb
index c829873da4..edd2283182 100644
--- a/railties/lib/rails/api/task.rb
+++ b/railties/lib/rails/api/task.rb
@@ -135,12 +135,20 @@ module Rails
def api_dir
'doc/rdoc'
end
+ end
+ class EdgeTask < RepoTask
def rails_version
"master@#{`git rev-parse HEAD`[0, 7]}"
end
end
+ class StableTask < RepoTask
+ def rails_version
+ File.read('RAILS_VERSION').strip
+ end
+ end
+
class AppTask < Task
def component_root_dir(gem_name)
$:.grep(%r{#{gem_name}[\w.-]*/lib\z}).first[0..-5]
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 3e8163490f..f32bf772a5 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -9,101 +9,9 @@ aliases = {
"r" => "runner"
}
-help_message = <<-EOT
-Usage: rails COMMAND [ARGS]
-
-The most common rails commands are:
- generate Generate new code (short-cut alias: "g")
- console Start the Rails console (short-cut alias: "c")
- server Start the Rails server (short-cut alias: "s")
- dbconsole Start a console for the database specified in config/database.yml
- (short-cut alias: "db")
- new Create a new Rails application. "rails new my_app" creates a
- new application called MyApp in "./my_app"
-
-In addition to those, there are:
- application Generate the Rails application code
- destroy Undo code generated with "generate" (short-cut alias: "d")
- plugin new Generates skeleton for developing a Rails plugin
- runner Run a piece of code in the application environment (short-cut alias: "r")
-
-All commands can be run with -h (or --help) for more information.
-EOT
-
-
command = ARGV.shift
command = aliases[command] || command
-case command
-when 'plugin'
- require "rails/commands/plugin"
-when 'generate', 'destroy'
- require 'rails/generators'
-
- require APP_PATH
- Rails.application.require_environment!
-
- Rails.application.load_generators
-
- require "rails/commands/#{command}"
-
-when 'console'
- require 'rails/commands/console'
- options = Rails::Console.parse_arguments(ARGV)
-
- # RAILS_ENV needs to be set before config/application is required
- ENV['RAILS_ENV'] = options[:environment] if options[:environment]
-
- # shift ARGV so IRB doesn't freak
- ARGV.shift if ARGV.first && ARGV.first[0] != '-'
-
- require APP_PATH
- Rails.application.require_environment!
- Rails::Console.start(Rails.application, options)
-
-when 'server'
- # Change to the application's path if there is no config.ru file in current directory.
- # This allows us to run `rails server` from other directories, but still get
- # the main config.ru and properly set the tmp directory.
- Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))
-
- require 'rails/commands/server'
- Rails::Server.new.tap do |server|
- # We need to require application after the server sets environment,
- # otherwise the --environment option given to the server won't propagate.
- require APP_PATH
- Dir.chdir(Rails.application.root)
- server.start
- end
-
-when 'dbconsole'
- require 'rails/commands/dbconsole'
- Rails::DBConsole.start
-
-when 'application', 'runner'
- require "rails/commands/#{command}"
-
-when 'new'
- if %w(-h --help).include?(ARGV.first)
- require 'rails/commands/application'
- else
- puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
- puts "Type 'rails' for help."
- exit(1)
- end
-
-when '--version', '-v'
- ARGV.unshift '--version'
- require 'rails/commands/application'
-
-when '-h', '--help'
- puts help_message
+require 'rails/commands/commands_tasks'
-else
- puts "Error: Command '#{command}' not recognized"
- if %x{rake #{command} --dry-run 2>&1 } && $?.success?
- puts "Did you mean: `$ rake #{command}` ?\n\n"
- end
- puts help_message
- exit(1)
-end
+Rails::CommandsTasks.new(ARGV).run_command!(command)
diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb
index 2ff29418c6..678697f09b 100644
--- a/railties/lib/rails/commands/application.rb
+++ b/railties/lib/rails/commands/application.rb
@@ -1,30 +1,3 @@
-require 'rails/version'
-
-if ['--version', '-v'].include?(ARGV.first)
- puts "Rails #{Rails::VERSION::STRING}"
- exit(0)
-end
-
-if ARGV.first != "new"
- ARGV[0] = "--help"
-else
- ARGV.shift
- unless ARGV.delete("--no-rc")
- customrc = ARGV.index{ |x| x.include?("--rc=") }
- railsrc = if customrc
- File.expand_path(ARGV.delete_at(customrc).gsub(/--rc=/, ""))
- else
- File.join(File.expand_path("~"), '.railsrc')
- end
- if File.exist?(railsrc)
- extra_args_string = File.read(railsrc)
- extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten
- puts "Using #{extra_args.join(" ")} from #{railsrc}"
- ARGV.insert(1, *extra_args)
- end
- end
-end
-
require 'rails/generators'
require 'rails/generators/rails/app/app_generator'
@@ -40,4 +13,5 @@ module Rails
end
end
+Rails::Generators::AppPreparer.new(ARGV).prepare!
Rails::Generators::AppGenerator.start
diff --git a/railties/lib/rails/commands/commands_tasks.rb b/railties/lib/rails/commands/commands_tasks.rb
new file mode 100644
index 0000000000..11524c4ef5
--- /dev/null
+++ b/railties/lib/rails/commands/commands_tasks.rb
@@ -0,0 +1,170 @@
+module Rails
+ # This is a class which takes in a rails command and initiates the appropriate
+ # initiation sequence.
+ #
+ # Warning: This class mutates ARGV because some commands require manipulating
+ # it before they are run.
+ class CommandsTasks # :nodoc:
+ attr_reader :argv
+
+ HELP_MESSAGE = <<-EOT
+Usage: rails COMMAND [ARGS]
+
+The most common rails commands are:
+ generate Generate new code (short-cut alias: "g")
+ console Start the Rails console (short-cut alias: "c")
+ server Start the Rails server (short-cut alias: "s")
+ dbconsole Start a console for the database specified in config/database.yml
+ (short-cut alias: "db")
+ new Create a new Rails application. "rails new my_app" creates a
+ new application called MyApp in "./my_app"
+
+In addition to those, there are:
+ application Generate the Rails application code
+ destroy Undo code generated with "generate" (short-cut alias: "d")
+ plugin new Generates skeleton for developing a Rails plugin
+ runner Run a piece of code in the application environment (short-cut alias: "r")
+
+All commands can be run with -h (or --help) for more information.
+EOT
+
+ COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)
+
+ def initialize(argv)
+ @argv = argv
+ end
+
+ def run_command!(command)
+ command = parse_command(command)
+ if COMMAND_WHITELIST.include?(command)
+ send(command)
+ else
+ write_error_message(command)
+ end
+ end
+
+ def plugin
+ require_command!("plugin")
+ end
+
+ def generate
+ generate_or_destroy(:generate)
+ end
+
+ def console
+ require_command!("console")
+ options = Rails::Console.parse_arguments(argv)
+
+ # RAILS_ENV needs to be set before config/application is required
+ ENV['RAILS_ENV'] = options[:environment] if options[:environment]
+
+ # shift ARGV so IRB doesn't freak
+ shift_argv!
+
+ require_application_and_environment!
+ Rails::Console.start(Rails.application, options)
+ end
+
+ def server
+ set_application_directory!
+ require_command!("server")
+
+ Rails::Server.new.tap do |server|
+ # We need to require application after the server sets environment,
+ # otherwise the --environment option given to the server won't propagate.
+ require APP_PATH
+ Dir.chdir(Rails.application.root)
+ server.start
+ end
+ end
+
+ def dbconsole
+ require_command!("dbconsole")
+ Rails::DBConsole.start
+ end
+
+ def application
+ require_command!("application")
+ end
+
+ def runner
+ require_command!("runner")
+ end
+
+ def new
+ if %w(-h --help).include?(argv.first)
+ require_command!("application")
+ else
+ exit_with_initialization_warning!
+ end
+ end
+
+ def version
+ argv.unshift '--version'
+ require_command!("application")
+ end
+
+ def help
+ write_help_message
+ end
+
+ private
+
+ def exit_with_initialization_warning!
+ puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
+ puts "Type 'rails' for help."
+ exit(1)
+ end
+
+ def shift_argv!
+ argv.shift if argv.first && argv.first[0] != '-'
+ end
+
+ def require_command!(command)
+ require "rails/commands/#{command}"
+ end
+
+ def generate_or_destroy(command)
+ require 'rails/generators'
+ require_application_and_environment!
+ Rails.application.load_generators
+ require "rails/commands/#{command}"
+ end
+
+ # Change to the application's path if there is no config.ru file in current directory.
+ # This allows us to run `rails server` from other directories, but still get
+ # the main config.ru and properly set the tmp directory.
+ def set_application_directory!
+ Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))
+ end
+
+ def require_application_and_environment!
+ require APP_PATH
+ Rails.application.require_environment!
+ end
+
+ def write_help_message
+ puts HELP_MESSAGE
+ end
+
+ def write_error_message(command)
+ puts "Error: Command '#{command}' not recognized"
+ if %x{rake #{command} --dry-run 2>&1 } && $?.success?
+ puts "Did you mean: `$ rake #{command}` ?\n\n"
+ end
+ write_help_message
+ exit(1)
+ end
+
+ def parse_command(command)
+ case command
+ when '--version', '-v'
+ 'version'
+ when '--help', '-h'
+ 'help'
+ else
+ command
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index d48dcf9ef3..9fb8ea6955 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -300,5 +300,67 @@ module Rails
defined?(::AppBuilder) ? ::AppBuilder : Rails::AppBuilder
end
end
+
+ # This class handles preparation of the arguments before the AppGenerator is
+ # called. The class provides version or help information if they were
+ # requested, and also constructs the railsrc file (used for extra configuration
+ # options).
+ #
+ # This class should be called before the AppGenerator is required and started
+ # since it configures and mutates ARGV correctly.
+ class AppPreparer # :nodoc
+ attr_reader :argv
+
+ def initialize(argv = ARGV)
+ @argv = argv
+ end
+
+ def prepare!
+ handle_version_request!(argv.first)
+ unless handle_invalid_command!(argv.first)
+ argv.shift
+ handle_rails_rc!
+ end
+ end
+
+ private
+
+ def handle_version_request!(argument)
+ if ['--version', '-v'].include?(argv.first)
+ require 'rails/version'
+ puts "Rails #{Rails::VERSION::STRING}"
+ exit(0)
+ end
+ end
+
+ def handle_invalid_command!(argument)
+ if argument != "new"
+ argv[0] = "--help"
+ end
+ end
+
+ def handle_rails_rc!
+ unless argv.delete("--no-rc")
+ insert_railsrc(railsrc)
+ end
+ end
+
+ def railsrc
+ if (customrc = argv.index{ |x| x.include?("--rc=") })
+ File.expand_path(argv.delete_at(customrc).gsub(/--rc=/, ""))
+ else
+ File.join(File.expand_path("~"), '.railsrc')
+ end
+ end
+
+ def insert_railsrc(railsrc)
+ if File.exist?(railsrc)
+ extra_args_string = File.read(railsrc)
+ extra_args = extra_args_string.split(/\n+/).map {|l| l.split}.flatten
+ puts "Using #{extra_args.join(" ")} from #{railsrc}"
+ argv.insert(1, *extra_args)
+ end
+ end
+ end
end
end
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 2cbb0a435c..290634290f 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -82,7 +82,7 @@ class SourceAnnotationExtractor
case item
when /\.(builder|rb|coffee|rake)$/
/#\s*(#{tag}):?\s*(.*)$/
- when /\.(css|scss|js)$/
+ when /\.(css|scss|sass|js)$/
/\/\/\s*(#{tag}):?\s*(.*)$/
when /\.erb$/
/<%\s*#\s*(#{tag}):?\s*(.*?)\s*%>/
diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb
index 3508f4225a..9a92c5f6ff 100644
--- a/railties/test/application/rake/notes_test.rb
+++ b/railties/test/application/rake/notes_test.rb
@@ -24,6 +24,7 @@ module ApplicationTests
app_file "app/assets/javascripts/application.js", "// TODO: note in js"
app_file "app/assets/stylesheets/application.css", "// TODO: note in css"
app_file "app/assets/stylesheets/application.css.scss", "// TODO: note in scss"
+ app_file "app/assets/stylesheets/application.css.sass", "// TODO: note in sass"
app_file "app/controllers/application_controller.rb", 1000.times.map { "" }.join("\n") << "# TODO: note in ruby"
app_file "lib/tasks/task.rake", "# TODO: note in rake"
@@ -46,9 +47,10 @@ module ApplicationTests
assert_match(/note in js/, output)
assert_match(/note in css/, output)
assert_match(/note in scss/, output)
+ assert_match(/note in sass/, output)
assert_match(/note in rake/, output)
- assert_equal 9, lines.size
+ assert_equal 10, lines.size
lines.each do |line|
assert_equal 4, line[0].size