diff options
72 files changed, 437 insertions, 331 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index d9c1e28878..c1101d849b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -16,6 +16,7 @@ Style/AndOr: # method call. Style/BracesAroundHashParameters: Enabled: true + EnforcedStyle: context_dependent # Align `when` with `case`. Layout/CaseIndentation: @@ -10,7 +10,6 @@ gem "arel", github: "rails/arel" # We need a newish Rake since Active Job sets its test tasks' descriptions. gem "rake", ">= 11.1" -gem "thor", github: "erikhuda/thor" # This needs to be with require false to ensure correct loading order, as it has to # be loaded after loading the test library. diff --git a/Gemfile.lock b/Gemfile.lock index 0738e1c35d..2096465a8b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,12 +7,6 @@ GIT pg (>= 0.17, < 0.20) GIT - remote: https://github.com/erikhuda/thor.git - revision: df5ba2b653a28087b3617d6c082b00866b0c0d6c - specs: - thor (0.19.4) - -GIT remote: https://github.com/matthewd/rb-inotify.git revision: 856730aad4b285969e8dd621e44808a7c5af4242 branch: close-handling @@ -31,7 +25,7 @@ GIT GIT remote: https://github.com/rails/arel.git - revision: f4227bf20a16a1d688afc24cf0038e2f98905dd4 + revision: 42510bf71472e2e35d9becb546edd05562672344 specs: arel (9.0.0.alpha) @@ -117,23 +111,21 @@ GEM public_suffix (~> 2.0, >= 2.0.2) amq-protocol (2.2.0) ast (2.3.0) - aws-sdk (2.10.19) - aws-sdk-resources (= 2.10.19) - aws-sdk-core (2.10.19) + aws-sdk (2.10.27) + aws-sdk-resources (= 2.10.27) + aws-sdk-core (2.10.27) aws-sigv4 (~> 1.0) jmespath (~> 1.0) - aws-sdk-resources (2.10.19) - aws-sdk-core (= 2.10.19) + aws-sdk-resources (2.10.27) + aws-sdk-core (= 2.10.27) aws-sigv4 (1.0.1) - azure-core (0.1.9) + azure-core (0.1.10) faraday (~> 0.9) faraday_middleware (~> 0.10) - nokogiri (~> 1.7) - azure-storage (0.11.4.preview) + azure-storage (0.12.3.preview) azure-core (~> 0.1) faraday (~> 0.9) faraday_middleware (~> 0.10) - nokogiri (~> 1.6) backburner (1.4.1) beaneater (~> 1.0) concurrent-ruby (~> 1.0.1) @@ -156,7 +148,7 @@ GEM thor (~> 0.19.1) useragent (~> 0.16.7) blade-qunit_adapter (2.0.1) - blade-sauce_labs_plugin (0.7.1) + blade-sauce_labs_plugin (0.7.2) childprocess faraday selenium-webdriver @@ -166,9 +158,9 @@ GEM bunny (2.6.6) amq-protocol (>= 2.1.0) byebug (9.0.6) - capybara (2.14.4) + capybara (2.15.1) addressable - mime-types (>= 1.16) + mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) @@ -213,13 +205,13 @@ GEM et-orbi (1.0.5) tzinfo event_emitter (0.2.6) - eventmachine (1.2.3) - eventmachine (1.2.3-x64-mingw32) - eventmachine (1.2.3-x86-mingw32) + eventmachine (1.2.5) + eventmachine (1.2.5-x64-mingw32) + eventmachine (1.2.5-x86-mingw32) execjs (2.7.0) - faraday (0.12.2) + faraday (0.13.0) multipart-post (>= 1.2, < 3) - faraday_middleware (0.12.0) + faraday_middleware (0.12.2) faraday (>= 0.7.4, < 1.0) faye (1.2.4) cookiejar (>= 0.3.0) @@ -249,7 +241,7 @@ GEM googleauth (~> 0.5.1) google-cloud-env (1.0.1) faraday (~> 0.11) - google-cloud-storage (1.3.0) + google-cloud-storage (1.4.0) digest-crc (~> 0.4) google-api-client (~> 0.13.0) google-cloud-core (~> 1.0) @@ -295,8 +287,9 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_magick (4.8.0) + mini_mime (0.1.4) mini_portile2 (2.2.0) - minitest (5.10.2) + minitest (5.10.3) minitest-bisect (1.4.0) minitest-server (~> 1.0) path_expander (~> 1.0) @@ -312,9 +305,9 @@ GEM multipart-post (2.0.0) mustache (1.0.5) mustermann (1.0.0) - mysql2 (0.4.8) - mysql2 (0.4.8-x64-mingw32) - mysql2 (0.4.8-x86-mingw32) + mysql2 (0.4.9) + mysql2 (0.4.9-x64-mingw32) + mysql2 (0.4.9-x86-mingw32) nio4r (2.1.0) nokogiri (1.8.0) mini_portile2 (~> 2.2.0) @@ -323,10 +316,10 @@ GEM nokogiri (1.8.0-x86-mingw32) mini_portile2 (~> 2.2.0) os (0.9.6) - parallel (1.11.2) + parallel (1.12.0) parser (2.4.0.0) ast (~> 2.2) - path_expander (1.0.1) + path_expander (1.0.2) pg (0.19.0) pg (0.19.0-x64-mingw32) pg (0.19.0-x86-mingw32) @@ -340,7 +333,7 @@ GEM qu (= 0.2.0) redis-namespace simple_uuid - que (0.13.1) + que (0.14.0) racc (1.4.14) rack (2.0.3) rack-cache (1.7.0) @@ -398,10 +391,10 @@ GEM rb-inotify (~> 0.9, >= 0.9.7) sdoc (1.0.0.rc2) rdoc (~> 5.0) - selenium-webdriver (3.4.4) + selenium-webdriver (3.5.1) childprocess (~> 0.5) rubyzip (~> 1.0) - sequel (4.48.0) + sequel (4.49.0) serverengine (1.5.11) sigdump (~> 0.2.2) sidekiq (5.0.4) @@ -444,6 +437,7 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) + thor (0.19.4) thread (0.1.7) thread_safe (0.3.6) tilt (2.0.8) @@ -535,7 +529,6 @@ DEPENDENCIES sqlite3 (~> 1.3.6) stackprof sucker_punch - thor! turbolinks (~> 5) tzinfo-data uglifier (>= 1.3.0) diff --git a/actioncable/test/channel/stream_test.rb b/actioncable/test/channel/stream_test.rb index 79c25d936f..eca06fe365 100644 --- a/actioncable/test/channel/stream_test.rb +++ b/actioncable/test/channel/stream_test.rb @@ -154,7 +154,7 @@ module ActionCable::StreamTests subscribe_to connection, identifiers: { id: 1 } connection.websocket.expects(:transmit) - @server.broadcast "test_room_1", { foo: "bar" }, coder: DummyEncoder + @server.broadcast "test_room_1", { foo: "bar" }, { coder: DummyEncoder } wait_for_async wait_for_executor connection.server.worker_pool.executor end diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb index cc02c9a53a..769be39004 100644 --- a/actionpack/lib/action_controller/railtie.rb +++ b/actionpack/lib/action_controller/railtie.rb @@ -24,7 +24,7 @@ module ActionController initializer "action_controller.parameters_config" do |app| options = app.config.action_controller - ActiveSupport.on_load(:action_controller) do + ActiveSupport.on_load(:action_controller, run_once: true) do ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false } if app.config.action_controller[:always_permitted_parameters] ActionController::Parameters.always_permitted_parameters = diff --git a/actionpack/test/controller/live_stream_test.rb b/actionpack/test/controller/live_stream_test.rb index 1ccbee8a0f..8cfb43a6bc 100644 --- a/actionpack/test/controller/live_stream_test.rb +++ b/actionpack/test/controller/live_stream_test.rb @@ -30,7 +30,7 @@ module ActionController def sse_with_retry sse = SSE.new(response.stream, retry: 1000) sse.write("{\"name\":\"John\"}") - sse.write({ name: "Ryan" }, retry: 1500) + sse.write({ name: "Ryan" }, { retry: 1500 }) ensure sse.close end @@ -38,7 +38,7 @@ module ActionController def sse_with_id sse = SSE.new(response.stream) sse.write("{\"name\":\"John\"}", id: 1) - sse.write({ name: "Ryan" }, id: 2) + sse.write({ name: "Ryan" }, { id: 2 }) ensure sse.close end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 7546b36bc3..e447b66486 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -35,7 +35,7 @@ class RedirectController < ActionController::Base end def redirect_with_status_hash - redirect_to({ action: "hello_world" }, status: 301) + redirect_to({ action: "hello_world" }, { status: 301 }) end def redirect_with_protocol diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 871fba7e73..3d98237003 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -943,8 +943,8 @@ class ResourcesTest < ActionController::TestCase assert_resource_allowed_routes("products", {}, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) assert_resource_allowed_routes("products", { format: "xml" }, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) - assert_recognizes({ controller: "products", action: "sale" }, path: "products/sale", method: :get) - assert_recognizes({ controller: "products", action: "sale", format: "xml" }, path: "products/sale.xml", method: :get) + assert_recognizes({ controller: "products", action: "sale" }, { path: "products/sale", method: :get }) + assert_recognizes({ controller: "products", action: "sale", format: "xml" }, { path: "products/sale.xml", method: :get }) end end @@ -959,8 +959,8 @@ class ResourcesTest < ActionController::TestCase assert_resource_allowed_routes("products", {}, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) assert_resource_allowed_routes("products", { format: "xml" }, { id: "1" }, [], [:index, :new, :create, :show, :edit, :update, :destroy]) - assert_recognizes({ controller: "products", action: "preview", id: "1" }, path: "products/1/preview", method: :get) - assert_recognizes({ controller: "products", action: "preview", id: "1", format: "xml" }, path: "products/1/preview.xml", method: :get) + assert_recognizes({ controller: "products", action: "preview", id: "1" }, { path: "products/1/preview", method: :get }) + assert_recognizes({ controller: "products", action: "preview", id: "1", format: "xml" }, { path: "products/1/preview.xml", method: :get }) end end @@ -977,8 +977,8 @@ class ResourcesTest < ActionController::TestCase assert_singleton_resource_allowed_routes("accounts", {}, [], [:new, :create, :show, :edit, :update, :destroy]) assert_singleton_resource_allowed_routes("accounts", { format: "xml" }, [], [:new, :create, :show, :edit, :update, :destroy]) - assert_recognizes({ controller: "accounts", action: "signup" }, path: "account/signup", method: :get) - assert_recognizes({ controller: "accounts", action: "signup", format: "xml" }, path: "account/signup.xml", method: :get) + assert_recognizes({ controller: "accounts", action: "signup" }, { path: "account/signup", method: :get }) + assert_recognizes({ controller: "accounts", action: "signup", format: "xml" }, { path: "account/signup.xml", method: :get }) end end @@ -995,8 +995,8 @@ class ResourcesTest < ActionController::TestCase assert_resource_allowed_routes("images", { product_id: "1" }, { id: "2" }, :show, [:index, :new, :create, :edit, :update, :destroy], "products/1/images") assert_resource_allowed_routes("images", { product_id: "1", format: "xml" }, { id: "2" }, :show, [:index, :new, :create, :edit, :update, :destroy], "products/1/images") - assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2" }, path: "products/1/images/2/thumbnail", method: :get) - assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2", format: "jpg" }, path: "products/1/images/2/thumbnail.jpg", method: :get) + assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2" }, { path: "products/1/images/2/thumbnail", method: :get }) + assert_recognizes({ controller: "images", action: "thumbnail", product_id: "1", id: "2", format: "jpg" }, { path: "products/1/images/2/thumbnail.jpg", method: :get }) end end @@ -1069,7 +1069,7 @@ class ResourcesTest < ActionController::TestCase match "/products", to: "products#show", via: :all end - assert_routing({ method: "all", path: "/products" }, controller: "products", action: "show") + assert_routing({ method: "all", path: "/products" }, { controller: "products", action: "show" }) end end @@ -1080,7 +1080,7 @@ class ResourcesTest < ActionController::TestCase end assert_raises(Minitest::Assertion) do - assert_routing({ method: "all", path: "/products" }, controller: "products", action: "show") + assert_routing({ method: "all", path: "/products" }, { controller: "products", action: "show" }) end end end diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index 92b1c75443..46369ebbb0 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -392,9 +392,9 @@ XML def test_assert_generates assert_generates "controller/action/5", controller: "controller", action: "action", id: "5" - assert_generates "controller/action/7", { id: "7" }, controller: "controller", action: "action" - assert_generates "controller/action/5", { controller: "controller", action: "action", id: "5", name: "bob" }, {}, name: "bob" - assert_generates "controller/action/7", { id: "7", name: "bob" }, { controller: "controller", action: "action" }, name: "bob" + assert_generates "controller/action/7", { id: "7" }, { controller: "controller", action: "action" } + assert_generates "controller/action/5", { controller: "controller", action: "action", id: "5", name: "bob" }, {}, { name: "bob" } + assert_generates "controller/action/7", { id: "7", name: "bob" }, { controller: "controller", action: "action" }, { name: "bob" } assert_generates "controller/action/7", { id: "7" }, { controller: "controller", action: "action", name: "bob" }, {} end @@ -405,7 +405,7 @@ XML def test_assert_routing_with_method with_routing do |set| set.draw { resources(:content) } - assert_routing({ method: "post", path: "content" }, controller: "content", action: "create") + assert_routing({ method: "post", path: "content" }, { controller: "content", action: "create" }) end end diff --git a/actionpack/test/dispatch/routing_assertions_test.rb b/actionpack/test/dispatch/routing_assertions_test.rb index a196fec7e9..e492a56653 100644 --- a/actionpack/test/dispatch/routing_assertions_test.rb +++ b/actionpack/test/dispatch/routing_assertions_test.rb @@ -33,11 +33,11 @@ class RoutingAssertionsTest < ActionController::TestCase end def test_assert_generates_with_defaults - assert_generates("/articles/1/edit", { controller: "articles", action: "edit" }, id: "1") + assert_generates("/articles/1/edit", { controller: "articles", action: "edit" }, { id: "1" }) end def test_assert_generates_with_extras - assert_generates("/articles", { controller: "articles", action: "index", page: "1" }, {}, page: "1") + assert_generates("/articles", { controller: "articles", action: "index", page: "1" }, {}, { page: "1" }) end def test_assert_recognizes @@ -50,8 +50,8 @@ class RoutingAssertionsTest < ActionController::TestCase end def test_assert_recognizes_with_method - assert_recognizes({ controller: "articles", action: "create" }, path: "/articles", method: :post) - assert_recognizes({ controller: "articles", action: "update", id: "1" }, path: "/articles/1", method: :put) + assert_recognizes({ controller: "articles", action: "create" }, { path: "/articles", method: :post }) + assert_recognizes({ controller: "articles", action: "update", id: "1" }, { path: "/articles/1", method: :put }) end def test_assert_recognizes_with_hash_constraint @@ -96,11 +96,11 @@ class RoutingAssertionsTest < ActionController::TestCase end def test_assert_routing_with_defaults - assert_routing("/articles/1/edit", { controller: "articles", action: "edit", id: "1" }, id: "1") + assert_routing("/articles/1/edit", { controller: "articles", action: "edit", id: "1" }, { id: "1" }) end def test_assert_routing_with_extras - assert_routing("/articles", { controller: "articles", action: "index", page: "1" }, {}, page: "1") + assert_routing("/articles", { controller: "articles", action: "index", page: "1" }, {}, { page: "1" }) end def test_assert_routing_with_hash_constraint diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb index 27f4f42aab..29cc74471d 100644 --- a/actionpack/test/journey/router_test.rb +++ b/actionpack/test/journey/router_test.rb @@ -186,14 +186,14 @@ module ActionDispatch def test_required_part_in_recall get "/messages/:a/:b", to: "foo#bar" - path, _ = @formatter.generate(nil, { controller: "foo", action: "bar", a: "a" }, b: "b") + path, _ = @formatter.generate(nil, { controller: "foo", action: "bar", a: "a" }, { b: "b" }) assert_equal "/messages/a/b", path end def test_splat_in_recall get "/*path", to: "foo#bar" - path, _ = @formatter.generate(nil, { controller: "foo", action: "bar" }, path: "b") + path, _ = @formatter.generate(nil, { controller: "foo", action: "bar" }, { path: "b" }) assert_equal "/b", path end @@ -201,7 +201,7 @@ module ActionDispatch get "/messages/:action(/:id(.:format))", to: "foo#bar" get "/messages/:id(.:format)", to: "bar#baz" - path, _ = @formatter.generate(nil, { controller: "foo", id: 10 }, action: "index") + path, _ = @formatter.generate(nil, { controller: "foo", id: 10 }, { action: "index" }) assert_equal "/messages/index/10", path end @@ -314,7 +314,7 @@ module ActionDispatch path, params = @formatter.generate( nil, { controller: "tasks", id: 10 }, - action: "index") + { action: "index" }) assert_equal "/tasks/index/10", path assert_equal({}, params) end @@ -325,7 +325,7 @@ module ActionDispatch path, params = @formatter.generate( "tasks", { controller: "tasks" }, - controller: "tasks", action: "index") + { controller: "tasks", action: "index" }) assert_equal "/tasks", path assert_equal({}, params) end diff --git a/actionview/test/actionpack/abstract/abstract_controller_test.rb b/actionview/test/actionpack/abstract/abstract_controller_test.rb index 2bf2ddcc70..468a6376c7 100644 --- a/actionview/test/actionpack/abstract/abstract_controller_test.rb +++ b/actionview/test/actionpack/abstract/abstract_controller_test.rb @@ -191,10 +191,10 @@ module AbstractController private def self.layout(formats) - find_template(name.underscore, { formats: formats }, _prefixes: ["layouts"]) + find_template(name.underscore, { formats: formats }, { _prefixes: ["layouts"] }) rescue ActionView::MissingTemplate begin - find_template("application", { formats: formats }, _prefixes: ["layouts"]) + find_template("application", { formats: formats }, { _prefixes: ["layouts"] }) rescue ActionView::MissingTemplate end end diff --git a/actionview/test/template/date_helper_test.rb b/actionview/test/template/date_helper_test.rb index 35a08ea672..5a5550438b 100644 --- a/actionview/test/template/date_helper_test.rb +++ b/actionview/test/template/date_helper_test.rb @@ -255,8 +255,8 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16), {}, class: "selector") - assert_dom_equal expected, select_day(16, {}, class: "selector") + assert_dom_equal expected, select_day(Time.mktime(2003, 8, 16), {}, { class: "selector" }) + assert_dom_equal expected, select_day(16, {}, { class: "selector" }) end def test_select_day_with_default_prompt @@ -425,7 +425,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8" selected="selected">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12">December</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), {}, class: "selector", accesskey: "M") + assert_dom_equal expected, select_month(Time.mktime(2003, 8, 16), {}, { class: "selector", accesskey: "M" }) end def test_select_month_with_default_prompt @@ -520,7 +520,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="2003" selected="selected">2003</option>\n<option value="2004">2004</option>\n<option value="2005">2005</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, class: "selector", accesskey: "M") + assert_dom_equal expected, select_year(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, { class: "selector", accesskey: "M" }) end def test_select_year_with_default_prompt @@ -615,7 +615,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08" selected="selected">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector", accesskey: "M") + assert_dom_equal expected, select_hour(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector", accesskey: "M" }) end def test_select_hour_with_default_prompt @@ -719,7 +719,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector", accesskey: "M") + assert_dom_equal expected, select_minute(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector", accesskey: "M" }) end def test_select_minute_with_default_prompt @@ -799,7 +799,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18" selected="selected">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector", accesskey: "M") + assert_dom_equal expected, select_second(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector", accesskey: "M" }) end def test_select_second_with_default_prompt @@ -1066,7 +1066,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, class: "selector") + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, { class: "selector" }) end def test_select_date_with_separator @@ -1170,7 +1170,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]", with_css_classes: true }, class: "datetime optional") + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, prefix: "date[first]", with_css_classes: true }, { class: "datetime optional" }) end def test_select_date_with_custom_with_css_classes_and_html_class_option @@ -1186,7 +1186,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { year: "my-year", month: "my-month", day: "my-day" } }, class: "date optional") + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { year: "my-year", month: "my-month", day: "my-day" } }, { class: "date optional" }) end def test_select_date_with_partial_with_css_classes_and_html_class_option @@ -1202,7 +1202,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { month: "my-month custom-grid" } }, class: "date optional") + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005, with_css_classes: { month: "my-month custom-grid" } }, { class: "date optional" }) end def test_select_date_with_html_class_option @@ -1218,7 +1218,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16" selected="selected">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, class: "date optional custom-grid") + assert_dom_equal expected, select_date(Time.mktime(2003, 8, 16), { start_year: 2003, end_year: 2005 }, { class: "date optional custom-grid" }) end def test_select_datetime @@ -1358,7 +1358,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, class: "selector") + assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { start_year: 2003, end_year: 2005, prefix: "date[first]" }, { class: "selector" }) end def test_select_datetime_with_all_separators @@ -1390,7 +1390,7 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { datetime_separator: "—", date_separator: "/", time_separator: ":", start_year: 2003, end_year: 2005, prefix: "date[first]" }, class: "selector") + assert_dom_equal expected, select_datetime(Time.mktime(2003, 8, 16, 8, 4, 18), { datetime_separator: "—", date_separator: "/", time_separator: ":", start_year: 2003, end_year: 2005, prefix: "date[first]" }, { class: "selector" }) end def test_select_datetime_should_work_with_date @@ -1670,8 +1670,8 @@ class DateHelperTest < ActionView::TestCase expected << %(<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04" selected="selected">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n) expected << "</select>\n" - assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector") - assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), { include_seconds: false }, class: "selector") + assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector" }) + assert_dom_equal expected, select_time(Time.mktime(2003, 8, 16, 8, 4, 18), { include_seconds: false }, { class: "selector" }) end def test_select_time_should_work_with_date @@ -1928,7 +1928,7 @@ class DateHelperTest < ActionView::TestCase expected << %{<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option value="2004" selected="selected">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n} expected << "</select>\n" - assert_dom_equal expected, date_select("post", "written_on", { order: [ :month, :year ] }, disabled: true) + assert_dom_equal expected, date_select("post", "written_on", { order: [ :month, :year ] }, { disabled: true }) end def test_date_select_within_fields_for @@ -2170,7 +2170,7 @@ class DateHelperTest < ActionView::TestCase expected << "</select>\n" - assert_dom_equal expected, date_select("post", "written_on", {}, class: "selector") + assert_dom_equal expected, date_select("post", "written_on", {}, { class: "selector" }) end def test_date_select_with_html_options_within_fields_for @@ -2178,7 +2178,7 @@ class DateHelperTest < ActionView::TestCase @post.written_on = Date.new(2004, 6, 15) output_buffer = fields_for :post, @post do |f| - concat f.date_select(:written_on, {}, class: "selector") + concat f.date_select(:written_on, {}, { class: "selector" }) end expected = %{<select id="post_written_on_1i" name="post[written_on(1i)]" class="selector">\n}.dup @@ -2453,7 +2453,7 @@ class DateHelperTest < ActionView::TestCase 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) } expected << "</select>\n" - assert_dom_equal expected, time_select("post", "written_on", {}, class: "selector") + assert_dom_equal expected, time_select("post", "written_on", {}, { class: "selector" }) end def test_time_select_with_html_options_within_fields_for @@ -2461,7 +2461,7 @@ class DateHelperTest < ActionView::TestCase @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) output_buffer = fields_for :post, @post do |f| - concat f.time_select(:written_on, {}, class: "selector") + concat f.time_select(:written_on, {}, { class: "selector" }) end expected = %{<input type="hidden" id="post_written_on_1i" name="post[written_on(1i)]" value="2004" />\n}.dup @@ -2606,7 +2606,7 @@ class DateHelperTest < ActionView::TestCase 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) } expected << "</select>\n" - assert_dom_equal expected, time_select("post", "written_on", {}, disabled: true) + assert_dom_equal expected, time_select("post", "written_on", {}, { disabled: true }) end def test_datetime_select @@ -2740,7 +2740,7 @@ class DateHelperTest < ActionView::TestCase @post.updated_at = Time.local(2004, 6, 15, 16, 35) output_buffer = fields_for :post, @post do |f| - concat f.datetime_select(:updated_at, {}, class: "selector") + concat f.datetime_select(:updated_at, {}, { class: "selector" }) end expected = %{<select id="post_updated_at_1i" name="post[updated_at(1i)]" class="selector">\n<option value="1999">1999</option>\n<option value="2000">2000</option>\n<option value="2001">2001</option>\n<option value="2002">2002</option>\n<option value="2003">2003</option>\n<option selected="selected" value="2004">2004</option>\n<option value="2005">2005</option>\n<option value="2006">2006</option>\n<option value="2007">2007</option>\n<option value="2008">2008</option>\n<option value="2009">2009</option>\n</select>\n}.dup @@ -3222,7 +3222,7 @@ class DateHelperTest < ActionView::TestCase 0.upto(59) { |i| expected << %(<option value="#{sprintf("%02d", i)}"#{' selected="selected"' if i == 16}>#{sprintf("%02d", i)}</option>\n) } expected << "</select>\n" - assert_dom_equal expected, datetime_select("post", "updated_at", { discard_year: true, discard_month: true }, disabled: true) + assert_dom_equal expected, datetime_select("post", "updated_at", { discard_year: true, discard_month: true }, { disabled: true }) end def test_datetime_select_discard_hour @@ -3442,7 +3442,7 @@ class DateHelperTest < ActionView::TestCase expected << %{<option value="00">00</option>\n<option value="01">01</option>\n<option value="02">02</option>\n<option value="03">03</option>\n<option value="04">04</option>\n<option value="05">05</option>\n<option value="06">06</option>\n<option value="07">07</option>\n<option value="08">08</option>\n<option value="09">09</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31">31</option>\n<option value="32">32</option>\n<option value="33">33</option>\n<option value="34">34</option>\n<option value="35" selected="selected">35</option>\n<option value="36">36</option>\n<option value="37">37</option>\n<option value="38">38</option>\n<option value="39">39</option>\n<option value="40">40</option>\n<option value="41">41</option>\n<option value="42">42</option>\n<option value="43">43</option>\n<option value="44">44</option>\n<option value="45">45</option>\n<option value="46">46</option>\n<option value="47">47</option>\n<option value="48">48</option>\n<option value="49">49</option>\n<option value="50">50</option>\n<option value="51">51</option>\n<option value="52">52</option>\n<option value="53">53</option>\n<option value="54">54</option>\n<option value="55">55</option>\n<option value="56">56</option>\n<option value="57">57</option>\n<option value="58">58</option>\n<option value="59">59</option>\n} expected << "</select>\n" - assert_dom_equal expected, datetime_select("post", "updated_at", {}, class: "selector") + assert_dom_equal expected, datetime_select("post", "updated_at", {}, { class: "selector" }) end def test_date_select_should_not_change_passed_options_hash @@ -3602,7 +3602,7 @@ class DateHelperTest < ActionView::TestCase assert select_minute(8, use_hidden: true).html_safe? assert select_month(8, prompt: "Choose month").html_safe? - assert select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, class: "selector").html_safe? + assert select_time(Time.mktime(2003, 8, 16, 8, 4, 18), {}, { class: "selector" }).html_safe? assert select_date(Time.mktime(2003, 8, 16), date_separator: " / ", start_year: 2003, end_year: 2005, prefix: "date[first]").html_safe? end diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb index bba529a98a..6db55a1447 100644 --- a/actionview/test/template/form_collections_helper_test.rb +++ b/actionview/test/template/form_collections_helper_test.rb @@ -95,7 +95,7 @@ class FormCollectionsHelperTest < ActionView::TestCase test "collection radio accepts html options as input" do collection = [[1, true], [0, false]] - with_collection_radio_buttons :user, :active, collection, :last, :first, {}, class: "special-radio" + with_collection_radio_buttons :user, :active, collection, :last, :first, {}, { class: "special-radio" } assert_select "input[type=radio][value=true].special-radio#user_active_true" assert_select "input[type=radio][value=false].special-radio#user_active_false" @@ -215,7 +215,7 @@ class FormCollectionsHelperTest < ActionView::TestCase test "collection radio buttons generates a hidden field using the given :name in :html_options" do collection = [Category.new(1, "Category 1"), Category.new(2, "Category 2")] - with_collection_radio_buttons :user, :category_ids, collection, :id, :name, {}, name: "user[other_category_ids]" + with_collection_radio_buttons :user, :category_ids, collection, :id, :name, {}, { name: "user[other_category_ids]" } assert_select "input[type=hidden][name='user[other_category_ids]'][value='']", count: 1 end @@ -259,7 +259,7 @@ class FormCollectionsHelperTest < ActionView::TestCase test "collection check boxes generates a hidden field using the given :name in :html_options" do collection = [Category.new(1, "Category 1"), Category.new(2, "Category 2")] - with_collection_check_boxes :user, :category_ids, collection, :id, :name, {}, name: "user[other_category_ids][]" + with_collection_check_boxes :user, :category_ids, collection, :id, :name, {}, { name: "user[other_category_ids][]" } assert_select "input[type=hidden][name='user[other_category_ids][]'][value='']", count: 1 end @@ -446,7 +446,7 @@ class FormCollectionsHelperTest < ActionView::TestCase test "collection check boxes accepts html options" do collection = [[1, "Category 1"], [2, "Category 2"]] - with_collection_check_boxes :user, :category_ids, collection, :first, :last, {}, class: "check" + with_collection_check_boxes :user, :category_ids, collection, :first, :last, {}, { class: "check" } assert_select 'input.check[type=checkbox][value="1"]' assert_select 'input.check[type=checkbox][value="2"]' diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index 246f52588d..ac64096908 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -1386,7 +1386,7 @@ class FormHelperTest < ActionView::TestCase ) assert_dom_equal( '<select name="post[secret]"></select>', - select("post", "secret", [], {}, "id" => nil) + select("post", "secret", [], {}, { "id" => nil }) ) assert_dom_equal( text_field("post", "title", "id" => nil), diff --git a/actionview/test/template/form_options_helper_test.rb b/actionview/test/template/form_options_helper_test.rb index 56a8a125e5..a66db2f3dc 100644 --- a/actionview/test/template/form_options_helper_test.rb +++ b/actionview/test/template/form_options_helper_test.rb @@ -491,7 +491,7 @@ class FormOptionsHelperTest < ActionView::TestCase def test_select_without_multiple assert_dom_equal( "<select id=\"post_category\" name=\"post[category]\"></select>", - select(:post, :category, "", {}, multiple: false) + select(:post, :category, "", {}, { multiple: false }) ) end @@ -639,7 +639,7 @@ class FormOptionsHelperTest < ActionView::TestCase end def test_select_with_multiple_to_add_hidden_input - output_buffer = select(:post, :category, "", {}, multiple: true) + output_buffer = select(:post, :category, "", {}, { multiple: true }) assert_dom_equal( "<input type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>", output_buffer @@ -647,7 +647,7 @@ class FormOptionsHelperTest < ActionView::TestCase end def test_select_with_multiple_and_without_hidden_input - output_buffer = select(:post, :category, "", { include_hidden: false }, multiple: true) + output_buffer = select(:post, :category, "", { include_hidden: false }, { multiple: true }) assert_dom_equal( "<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>", output_buffer @@ -655,7 +655,7 @@ class FormOptionsHelperTest < ActionView::TestCase end def test_select_with_multiple_and_with_explicit_name_ending_with_brackets - output_buffer = select(:post, :category, [], { include_hidden: false }, multiple: true, name: "post[category][]") + output_buffer = select(:post, :category, [], { include_hidden: false }, { multiple: true, name: "post[category][]" }) assert_dom_equal( "<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>", output_buffer @@ -663,7 +663,7 @@ class FormOptionsHelperTest < ActionView::TestCase end def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input - output_buffer = select(:post, :category, "", {}, multiple: true, disabled: true) + output_buffer = select(:post, :category, "", {}, { multiple: true, disabled: true }) assert_dom_equal( "<input disabled=\"disabled\"type=\"hidden\" name=\"post[category][]\" value=\"\"/><select multiple=\"multiple\" disabled=\"disabled\" id=\"post_category\" name=\"post[category][]\"></select>", output_buffer @@ -682,7 +682,7 @@ class FormOptionsHelperTest < ActionView::TestCase def test_select_with_include_blank_false_and_required @post = Post.new @post.category = "<mus>" - e = assert_raises(ArgumentError) { select("post", "category", %w( abe <mus> hest), { include_blank: false }, required: "required") } + e = assert_raises(ArgumentError) { select("post", "category", %w( abe <mus> hest), { include_blank: false }, { required: "required" }) } assert_match(/include_blank cannot be false for a required field./, e.message) end @@ -762,7 +762,7 @@ class FormOptionsHelperTest < ActionView::TestCase @post.category = "" assert_dom_equal( "<select class=\"disabled\" disabled=\"disabled\" name=\"post[category]\" id=\"post_category\"><option value=\"\">Please select</option>\n<option value=\"\"></option>\n</select>", - select("post", "category", [], { prompt: true, include_blank: true }, class: "disabled", disabled: true) + select("post", "category", [], { prompt: true, include_blank: true }, { class: "disabled", disabled: true }) ) end @@ -778,42 +778,42 @@ class FormOptionsHelperTest < ActionView::TestCase def test_required_select assert_dom_equal( %(<select id="post_category" name="post[category]" required="required"><option value=""></option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>), - select("post", "category", %w(abe mus hest), {}, required: true) + select("post", "category", %w(abe mus hest), {}, { required: true }) ) end def test_required_select_with_include_blank_prompt assert_dom_equal( %(<select id="post_category" name="post[category]" required="required"><option value="">Select one</option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>), - select("post", "category", %w(abe mus hest), { include_blank: "Select one" }, required: true) + select("post", "category", %w(abe mus hest), { include_blank: "Select one" }, { required: true }) ) end def test_required_select_with_prompt assert_dom_equal( %(<select id="post_category" name="post[category]" required="required"><option value="">Select one</option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>), - select("post", "category", %w(abe mus hest), { prompt: "Select one" }, required: true) + select("post", "category", %w(abe mus hest), { prompt: "Select one" }, { required: true }) ) end def test_required_select_display_size_equals_to_one assert_dom_equal( %(<select id="post_category" name="post[category]" required="required" size="1"><option value=""></option>\n<option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>), - select("post", "category", %w(abe mus hest), {}, required: true, size: 1) + select("post", "category", %w(abe mus hest), {}, { required: true, size: 1 }) ) end def test_required_select_with_display_size_bigger_than_one assert_dom_equal( %(<select id="post_category" name="post[category]" required="required" size="2"><option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>), - select("post", "category", %w(abe mus hest), {}, required: true, size: 2) + select("post", "category", %w(abe mus hest), {}, { required: true, size: 2 }) ) end def test_required_select_with_multiple_option assert_dom_equal( %(<input name="post[category][]" type="hidden" value=""/><select id="post_category" multiple="multiple" name="post[category][]" required="required"><option value="abe">abe</option>\n<option value="mus">mus</option>\n<option value="hest">hest</option></select>), - select("post", "category", %w(abe mus hest), {}, required: true, multiple: true) + select("post", "category", %w(abe mus hest), {}, { required: true, multiple: true }) ) end @@ -852,7 +852,7 @@ class FormOptionsHelperTest < ActionView::TestCase assert_dom_equal( expected, - select("album[]", "genre", %w[rap rock country], {}, index: nil) + select("album[]", "genre", %w[rap rock country], {}, { index: nil }) ) end @@ -982,7 +982,7 @@ class FormOptionsHelperTest < ActionView::TestCase assert_dom_equal( "<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option value=\"\"></option>\n<option value=\"<Abe>\"><Abe></option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>", - collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, "style" => "width: 200px") + collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, { "style" => "width: 200px" }) ) end @@ -992,7 +992,7 @@ class FormOptionsHelperTest < ActionView::TestCase assert_dom_equal( "<select id=\"post_author_name\" name=\"post[author_name]\" style=\"width: 200px\"><option value=\"\">No Selection</option>\n<option value=\"<Abe>\"><Abe></option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>", - collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: "No Selection" }, "style" => "width: 200px") + collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: "No Selection" }, { "style" => "width: 200px" }) ) end @@ -1003,10 +1003,10 @@ class FormOptionsHelperTest < ActionView::TestCase expected = "<input type=\"hidden\" name=\"post[author_name][]\" value=\"\"/><select id=\"post_author_name\" name=\"post[author_name][]\" multiple=\"multiple\"><option value=\"\"></option>\n<option value=\"<Abe>\"><Abe></option>\n<option value=\"Babe\" selected=\"selected\">Babe</option>\n<option value=\"Cabe\">Cabe</option></select>" # Should suffix default name with []. - assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, multiple: true) + assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true }, { multiple: true }) # Shouldn't suffix custom name with []. - assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true, name: "post[author_name][]" }, multiple: true) + assert_dom_equal expected, collection_select("post", "author_name", dummy_posts, "author_name", "author_name", { include_blank: true, name: "post[author_name][]" }, { multiple: true }) end def test_collection_select_with_blank_and_selected @@ -1149,7 +1149,7 @@ class FormOptionsHelperTest < ActionView::TestCase def test_time_zone_select_with_style @firm = Firm.new("D") html = time_zone_select("firm", "time_zone", nil, {}, - "style" => "color: red") + { "style" => "color: red" }) assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" \ "<option value=\"A\">A</option>\n" \ "<option value=\"B\">B</option>\n" \ @@ -1159,13 +1159,13 @@ class FormOptionsHelperTest < ActionView::TestCase "</select>", html assert_dom_equal html, time_zone_select("firm", "time_zone", nil, {}, - style: "color: red") + { style: "color: red" }) end def test_time_zone_select_with_blank_and_style @firm = Firm.new("D") html = time_zone_select("firm", "time_zone", nil, - { include_blank: true }, "style" => "color: red") + { include_blank: true }, { "style" => "color: red" }) assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" \ "<option value=\"\"></option>\n" \ "<option value=\"A\">A</option>\n" \ @@ -1176,13 +1176,13 @@ class FormOptionsHelperTest < ActionView::TestCase "</select>", html assert_dom_equal html, time_zone_select("firm", "time_zone", nil, - { include_blank: true }, style: "color: red") + { include_blank: true }, { style: "color: red" }) end def test_time_zone_select_with_blank_as_string_and_style @firm = Firm.new("D") html = time_zone_select("firm", "time_zone", nil, - { include_blank: "No Zone" }, "style" => "color: red") + { include_blank: "No Zone" }, { "style" => "color: red" }) assert_dom_equal "<select id=\"firm_time_zone\" name=\"firm[time_zone]\" style=\"color: red\">" \ "<option value=\"\">No Zone</option>\n" \ "<option value=\"A\">A</option>\n" \ @@ -1193,7 +1193,7 @@ class FormOptionsHelperTest < ActionView::TestCase "</select>", html assert_dom_equal html, time_zone_select("firm", "time_zone", nil, - { include_blank: "No Zone" }, style: "color: red") + { include_blank: "No Zone" }, { style: "color: red" }) end def test_time_zone_select_with_priority_zones diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index b985b9789b..48c567008d 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -100,53 +100,53 @@ class FormTagHelperTest < ActionView::TestCase end def test_form_tag_multipart - actual = form_tag({}, "multipart" => true) + actual = form_tag({}, { "multipart" => true }) expected = whole_form("http://www.example.com", enctype: true) assert_dom_equal expected, actual end def test_form_tag_with_method_patch - actual = form_tag({}, method: :patch) + actual = form_tag({}, { method: :patch }) expected = whole_form("http://www.example.com", method: :patch) assert_dom_equal expected, actual end def test_form_tag_with_method_put - actual = form_tag({}, method: :put) + actual = form_tag({}, { method: :put }) expected = whole_form("http://www.example.com", method: :put) assert_dom_equal expected, actual end def test_form_tag_with_method_delete - actual = form_tag({}, method: :delete) + actual = form_tag({}, { method: :delete }) expected = whole_form("http://www.example.com", method: :delete) assert_dom_equal expected, actual end def test_form_tag_with_remote - actual = form_tag({}, remote: true) + actual = form_tag({}, { remote: true }) expected = whole_form("http://www.example.com", remote: true) assert_dom_equal expected, actual end def test_form_tag_with_remote_false - actual = form_tag({}, remote: false) + actual = form_tag({}, { remote: false }) expected = whole_form assert_dom_equal expected, actual end def test_form_tag_enforce_utf8_true - actual = 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) + 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? diff --git a/actionview/test/template/text_helper_test.rb b/actionview/test/template/text_helper_test.rb index adb923d93b..f247de066f 100644 --- a/actionview/test/template/text_helper_test.rb +++ b/actionview/test/template/text_helper_test.rb @@ -50,19 +50,19 @@ class TextHelperTest < ActionView::TestCase def test_simple_format_should_sanitize_input_when_sanitize_option_is_true assert_equal "<p><b> test with unsafe string </b>code!</p>", - simple_format("<b> test with unsafe string </b><script>code!</script>", {}, sanitize: true) + simple_format("<b> test with unsafe string </b><script>code!</script>", {}, { sanitize: true }) end def test_simple_format_should_not_sanitize_input_when_sanitize_option_is_false - assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, sanitize: false) + assert_equal "<p><b> test with unsafe string </b><script>code!</script></p>", simple_format("<b> test with unsafe string </b><script>code!</script>", {}, { sanitize: false }) end def test_simple_format_with_custom_wrapper - assert_equal "<div></div>", simple_format(nil, {}, wrapper_tag: "div") + assert_equal "<div></div>", simple_format(nil, {}, { wrapper_tag: "div" }) end def test_simple_format_with_custom_wrapper_and_multi_line_breaks - assert_equal "<div>We want to put a wrapper...</div>\n\n<div>...right there.</div>", simple_format("We want to put a wrapper...\n\n...right there.", {}, wrapper_tag: "div") + assert_equal "<div>We want to put a wrapper...</div>\n\n<div>...right there.</div>", simple_format("We want to put a wrapper...\n\n...right there.", {}, { wrapper_tag: "div" }) end def test_simple_format_should_not_change_the_text_passed diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 9f95b30122..b39d28ca31 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,8 +1,8 @@ * Ensure `sum` honors `distinct` on `has_many :through` associations - Fixes #16791 + Fixes #16791. - *Aaron Wortham + *Aaron Wortham* * Add `binary` fixture helper method. diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 5ba03c555a..4915a37f06 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -116,18 +116,8 @@ module ActiveRecord @reflection_scope ||= reflection.scope_for(klass) end - def klass_scope - current_scope = klass.current_scope - - if current_scope && current_scope.empty_scope? - klass.unscoped - else - klass.default_scoped - end - end - def build_scope - scope = klass_scope + scope = klass.scope_for_association if reflection.type scope.where!(reflection.type => model.base_class.sti_name) diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index 76237c4a0c..cba565448f 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -15,7 +15,7 @@ module ActiveRecord def target_scope scope = super reflection.chain.drop(1).each do |reflection| - relation = reflection.klass.all + relation = reflection.klass.scope_for_association scope.merge!( relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load) ) diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 48d33e6744..c622df132e 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -62,12 +62,6 @@ module ActiveRecord clear_mutation_trackers end - def write_attribute_without_type_cast(attr_name, *) # :nodoc: - result = super - clear_attribute_change(attr_name) - result - end - def clear_attribute_changes(attr_names) # :nodoc: super attr_names.each do |attr_name| @@ -183,6 +177,11 @@ module ActiveRecord end private + def write_attribute_without_type_cast(attr_name, _) + result = super + clear_attribute_change(attr_name) + result + end def mutation_tracker unless defined?(@mutation_tracker) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index b2c62cd686..b847933b2e 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -221,13 +221,8 @@ module ActiveRecord end def klass_join_scope(table, predicate_builder) # :nodoc: - current_scope = klass.current_scope - - if current_scope && current_scope.empty_scope? - build_scope(table, predicate_builder) - else - klass.default_scoped(build_scope(table, predicate_builder)) - end + relation = build_scope(table, predicate_builder) + klass.scope_for_association(relation) end def constraints diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 43cce19c1f..6fa096c1fe 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -31,6 +31,16 @@ module ActiveRecord end end + def scope_for_association(scope = relation) # :nodoc: + current_scope = self.current_scope + + if current_scope && current_scope.empty_scope? + scope + else + default_scoped(scope) + end + end + def default_scoped(scope = relation) # :nodoc: build_default_scope(scope) || scope end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index b2f5e39e09..603dee8c0d 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -190,7 +190,7 @@ module ActiveRecord # # === Caveats # - # If you're on MySQL, then do not use Data Definition Language(DDL) operations in nested + # If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested # transactions blocks that are emulated with savepoints. That is, do not execute statements # like 'CREATE TABLE' inside such blocks. This is because MySQL automatically # releases all savepoints upon executing a DDL operation. When +transaction+ diff --git a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb index d18330f5b2..35d5664400 100644 --- a/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb +++ b/activerecord/lib/rails/generators/active_record/application_record/application_record_generator.rb @@ -15,11 +15,12 @@ module ActiveRecord private def application_record_file_name - @application_record_file_name ||= if namespaced? - "app/models/#{namespaced_path}/application_record.rb" - else - "app/models/application_record.rb" - end + @application_record_file_name ||= + if namespaced? + "app/models/#{namespaced_path}/application_record.rb" + else + "app/models/application_record.rb" + end end end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 968c0eb944..f8ea51225a 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1261,6 +1261,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert_equal 0, users[2].family_members.to_a.size end + def test_through_scope_is_affected_by_unscoping + author = authors(:david) + + expected = author.comments.to_a + FirstPost.unscoped do + assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id) + end + end + + def test_through_scope_isnt_affected_by_scoping + author = authors(:david) + + expected = author.comments_on_first_posts.to_a + FirstPost.where(id: 2).scoping do + author.comments_on_first_posts.reset + assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id) + end + end + def test_incorrectly_ordered_through_associations assert_raises(ActiveRecord::HasManyThroughOrderError) do DeveloperWithIncorrectlyOrderedHasManyThrough.create( diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index 006372a757..8be77ed88f 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -16,7 +16,7 @@ module ActiveRecord test "building with custom types" do builder = AttributeSet::Builder.new(foo: Type::Float.new) - attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, bar: Type::Integer.new) + attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, { bar: Type::Integer.new }) assert_equal 3.3, attributes[:foo].value assert_equal 4, attributes[:bar].value diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb index abbc011b34..68a1190d68 100644 --- a/activerecord/test/cases/enum_test.rb +++ b/activerecord/test/cases/enum_test.rb @@ -255,12 +255,14 @@ class EnumTest < ActiveRecord::TestCase assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible? end - test "_before_type_cast returns the enum label (required for form fields)" do - if @book.status_came_from_user? - assert_equal "published", @book.status_before_type_cast - else - assert_equal "published", @book.status - end + test "_before_type_cast" do + assert_equal 2, @book.status_before_type_cast + assert_equal "published", @book.status + + @book.status = "published" + + assert_equal "published", @book.status_before_type_cast + assert_equal "published", @book.status end test "reserved enum names" do diff --git a/activestorage/README.md b/activestorage/README.md index d4ffe0c484..17d98978bb 100644 --- a/activestorage/README.md +++ b/activestorage/README.md @@ -1,17 +1,16 @@ # Active Storage -Active Storage makes it simple to upload and reference files in cloud services, like Amazon S3, Google Cloud Storage or Microsoft Azure Storage and attach those files to Active Records. It also provides a disk service for testing or local deployments, but the focus is on cloud storage. +Active Storage makes it simple to upload and reference files in cloud services like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage, and attach those files to Active Records. Supports having one main service and mirrors in other services for redundancy. It also provides a disk service for testing or local deployments, but the focus is on cloud storage. Files can be uploaded from the server to the cloud or directly from the client to the cloud. -Image files can further more be transformed using on-demand variants for quality, aspect ratio, size, or any other -MiniMagick supported transformation. +Image files can furthermore be transformed using on-demand variants for quality, aspect ratio, size, or any other [MiniMagick](https://github.com/minimagick/minimagick) supported transformation. ## Compared to other storage solutions -A key difference to how Active Storage works compared to other attachment solutions in Rails is through the use of built-in [Blob](https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob.rb) and [Attachment](https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/attachment.rb) models (backed by Active Record). This means existing application models do not need to be modified with additional columns to associate with files. Active Storage uses polymorphic associations via the join model of `Attachment`, which then connects to the actual `Blob`. +A key difference to how Active Storage works compared to other attachment solutions in Rails is through the use of built-in [Blob](https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob.rb) and [Attachment](https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/attachment.rb) models (backed by Active Record). This means existing application models do not need to be modified with additional columns to associate with files. Active Storage uses polymorphic associations via the `Attachment` join model, which then connects to the actual `Blob`. -These `Blob` models are intended to be immutable in spirit. One file, one blob. You can associate the same blob with multiple application models as well. And if you want to do transformations of a given `Blob`, the idea is that you'll simply create a new one, rather than attempt to mutate the existing (though of course you can delete that later if you don't need it). +`Blob` models store attachment metadata (filename, content-type, etc.), and their identifier key in the storage service. Blob models do not store the actual binary data. They are intended to be immutable in spirit. One file, one blob. You can associate the same blob with multiple application models as well. And if you want to do transformations of a given `Blob`, the idea is that you'll simply create a new one, rather than attempt to mutate the existing one (though of course you can delete the previous version later if you don't need it). ## Examples @@ -19,16 +18,32 @@ One attachment: ```ruby class User < ApplicationRecord + # Associates an attachment and a blob. When the user is destroyed they are + # purged by default (models destroyed, and resource files deleted). has_one_attached :avatar end -user.avatar.attach io: File.open("~/face.jpg"), filename: "avatar.jpg", content_type: "image/jpg" +# Attach an avatar to the user. +user.avatar.attach(io: File.open("~/face.jpg"), filename: "avatar.jpg", content_type: "image/jpg") + +# Does the user have an avatar? user.avatar.attached? # => true +# Synchronously destroy the avatar and actual resource files. user.avatar.purge + +# Destroy the associated models and actual resource files async, via Active Job. +user.avatar.purge_later + +# Does the user have an avatar? user.avatar.attached? # => false -url_for(user.avatar) # Generate a permanent URL for the blob, which upon access will redirect to a temporary service URL. +# Generate a permanent URL for the blob that points to the application. +# Upon access, a redirect to the actual service endpoint is returned. +# This indirection decouples the public URL from the actual one, and +# allows for example mirroring attachments in different services for +# high-availability. The redirection has an HTTP expiration of 5 min. +url_for(user.avatar) class AvatarsController < ApplicationController def update diff --git a/activestorage/app/jobs/active_storage/purge_job.rb b/activestorage/app/jobs/active_storage/purge_job.rb index 404ccefd05..369c07929d 100644 --- a/activestorage/app/jobs/active_storage/purge_job.rb +++ b/activestorage/app/jobs/active_storage/purge_job.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -# Provides delayed purging of attachments or blobs using their +#purge_later+ method. +# Provides delayed purging of attachments or blobs using their +purge_later+ method. class ActiveStorage::PurgeJob < ActiveJob::Base # FIXME: Limit this to a custom ActiveStorage error retry_on StandardError diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb index adfa6d4aa5..ad43845e4e 100644 --- a/activestorage/app/models/active_storage/attachment.rb +++ b/activestorage/app/models/active_storage/attachment.rb @@ -4,7 +4,7 @@ require "active_support/core_ext/module/delegation" # Attachments associate records with blobs. Usually that's a one record-many blobs relationship, # but it is possible to associate many different records with the same blob. If you're doing that, -# you'll want to declare with `has_one/many_attached :thingy, dependent: false`, so that destroying +# you'll want to declare with <tt>has_one/many_attached :thingy, dependent: false</tt>, so that destroying # any one record won't destroy the blob as well. (Then you'll need to do your own garbage collecting, though). class ActiveStorage::Attachment < ActiveRecord::Base self.table_name = "active_storage_attachments" @@ -22,8 +22,8 @@ class ActiveStorage::Attachment < ActiveRecord::Base end # Purging an attachment means purging the blob, which means talking to the service, which means - # talking over the internet. Whenever you're doing that, it's a good idea to put that work in a job, - # so it doesn't hold up other operations. That's what +#purge_later+ provides. + # talking over the Internet. Whenever you're doing that, it's a good idea to put that work in a job, + # so it doesn't hold up other operations. That's what +purge_later+ provides. def purge_later ActiveStorage::PurgeJob.perform_later(self) end diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index dc91a9265f..9f2ed1e5ac 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -3,8 +3,8 @@ # A blob is a record that contains the metadata about a file and a key for where that file resides on the service. # Blobs can be created in two ways: # -# 1) Subsequent to the file being uploaded server-side to the service via #create_after_upload! -# 2) Ahead of the file being directly uploaded client-side to the service via #create_before_direct_upload! +# 1) Subsequent to the file being uploaded server-side to the service via <tt>create_after_upload!</tt>. +# 2) Ahead of the file being directly uploaded client-side to the service via <tt>create_before_direct_upload!</tt>. # # The first option doesn't require any client-side JavaScript integration, and can be used by any other back-end # service that deals with files. The second option is faster, since you're not using your own server as a staging @@ -12,7 +12,7 @@ # # Blobs are intended to be immutable in as-so-far as their reference to a specific file goes. You're allowed to # update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file. -# If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old. +# If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one. class ActiveStorage::Blob < ActiveRecord::Base self.table_name = "active_storage_blobs" @@ -22,11 +22,11 @@ class ActiveStorage::Blob < ActiveRecord::Base class_attribute :service class << self - # You can used the signed id of a blob to refer to it on the client side without fear of tampering. - # This is particularly helpful for direct uploads where the client side needs to refer to the blob + # You can used the signed ID of a blob to refer to it on the client side without fear of tampering. + # This is particularly helpful for direct uploads where the client-side needs to refer to the blob # that was created ahead of the upload itself on form submission. # - # The signed id is also used to create stable URLs for the blob through the BlobsController. + # The signed ID is also used to create stable URLs for the blob through the BlobsController. def find_signed(id) find ActiveStorage.verifier.verify(id, purpose: :blob_id) end @@ -43,8 +43,8 @@ class ActiveStorage::Blob < ActiveRecord::Base end # Returns a saved blob instance after the +io+ has been uploaded to the service. Note, the blob is first built, - # then the +io+ is uploaded, then the blob is saved. This is doing to avoid opening a transaction and talking to - # the service during that (which is a bad idea and leads to deadlocks). + # then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take + # time), while having an open database transaction. def create_after_upload!(io:, filename:, content_type: nil, metadata: nil) build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata).tap(&:save!) end @@ -61,7 +61,7 @@ class ActiveStorage::Blob < ActiveRecord::Base # Returns a signed ID for this blob that's suitable for reference on the client-side without fear of tampering. - # It uses the framework-wide verifier on `ActiveStorage.verifier`, but with a dedicated purpose. + # It uses the framework-wide verifier on <tt>ActiveStorage.verifier</tt>, but with a dedicated purpose. def signed_id ActiveStorage.verifier.generate(id, purpose: :blob_id) end @@ -74,8 +74,9 @@ class ActiveStorage::Blob < ActiveRecord::Base self[:key] ||= self.class.generate_unique_secure_token end - # Returns a ActiveStorage::Filename instance of the filename that can be queried for basename, extension, and - # a sanitized version of the filename that's safe to use in URLs. + # Returns an ActiveStorage::Filename instance of the filename that can be + # queried for basename, extension, and a sanitized version of the filename + # that's safe to use in URLs. def filename ActiveStorage::Filename.new(self[:filename]) end @@ -100,8 +101,9 @@ class ActiveStorage::Blob < ActiveRecord::Base content_type.start_with?("text") end - # Returns a ActiveStorage::Variant instance with the set of +transformations+ passed in. This is only relevant - # for image files, and it allows any image to be transformed for size, colors, and the like. Example: + # Returns an ActiveStorage::Variant instance with the set of +transformations+ + # passed in. This is only relevant for image files, and it allows any image to + # be transformed for size, colors, and the like. Example: # # avatar.variant(resize: "100x100").processed.service_url # @@ -178,7 +180,7 @@ class ActiveStorage::Blob < ActiveRecord::Base destroy end - # Enqueues a ActiveStorage::PurgeJob job that'll call +#purge+. This is the recommended way to purge blobs when the call + # Enqueues an ActiveStorage::PurgeJob job that'll call +purge+. This is the recommended way to purge blobs when the call # needs to be made from a transaction, a callback, or any other real-time scenario. def purge_later ActiveStorage::PurgeJob.perform_later(self) diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb index 7b4ca18c8c..40648a27f7 100644 --- a/activestorage/app/models/active_storage/variant.rb +++ b/activestorage/app/models/active_storage/variant.rb @@ -4,8 +4,8 @@ # These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the # original. # -# Variants rely on `MiniMagick` for the actual transformations of the file, so you must add `gem "mini_magick"` -# to your Gemfile if you wish to use variants. +# Variants rely on {MiniMagick}(https://github.com/minimagick/minimagick) for the actual transformations +# of the file, so you must add <tt>gem "mini_magick"</tt> to your Gemfile if you wish to use variants. # # Note that to create a variant it's necessary to download the entire blob file from the service and load it # into memory. The larger the image, the more memory is used. Because of this process, you also want to be @@ -21,7 +21,7 @@ # This will create a URL for that specific blob with that specific variant, which the ActiveStorage::VariantsController # can then produce on-demand. # -# When you do want to actually produce the variant needed, call +#processed+. This will check that the variant +# When you do want to actually produce the variant needed, call +processed+. This will check that the variant # has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform # the transformations, upload the variant to the service, and return itself again. Example: # @@ -58,8 +58,8 @@ class ActiveStorage::Variant # Hiding the +service_url+ behind a redirect also gives you the power to change services without updating all URLs. And # it allows permanent URLs that redirect to the +service_url+ to be cached in the view. # - # Use `url_for(variant)` (or the implied form, like `link_to variant` or `redirect_to variant`) to get the stable URL - # for a variant that points to the ActiveStorage::VariantsController, which in turn will use this +#service_call+ method + # Use <tt>url_for(variant)</tt> (or the implied form, like +link_to variant+ or +redirect_to variant+) to get the stable URL + # for a variant that points to the ActiveStorage::VariantsController, which in turn will use this +service_call+ method # for its redirection. def service_url(expires_in: 5.minutes, disposition: :inline) service.url key, expires_in: expires_in, disposition: disposition, filename: blob.filename, content_type: blob.content_type diff --git a/activestorage/app/models/active_storage/variation.rb b/activestorage/app/models/active_storage/variation.rb index 396ce85ef1..f657d90db4 100644 --- a/activestorage/app/models/active_storage/variation.rb +++ b/activestorage/app/models/active_storage/variation.rb @@ -15,13 +15,13 @@ class ActiveStorage::Variation attr_reader :transformations class << self - # Returns a variation instance with the transformations that were encoded by +#encode+. + # Returns a variation instance with the transformations that were encoded by +encode+. def decode(key) new ActiveStorage.verifier.verify(key, purpose: :variation) end # Returns a signed key for the +transformations+, which can be used to refer to a specific - # variation in a URL or combined key (like `ActiveStorage::Variant#key`). + # variation in a URL or combined key (like <tt>ActiveStorage::Variant#key</tt>). def encode(transformations) ActiveStorage.verifier.generate(transformations, purpose: :variation) end @@ -31,7 +31,7 @@ class ActiveStorage::Variation @transformations = transformations end - # Accepts an open MiniMagick image instance, like what's return by `MiniMagick::Image.read(io)`, + # Accepts an open MiniMagick image instance, like what's returned by <tt>MiniMagick::Image.read(io)</tt>, # and performs the +transformations+ against it. The transformed image instance is then returned. def transform(image) transformations.each do |(method, argument)| diff --git a/activestorage/lib/active_storage/attached.rb b/activestorage/lib/active_storage/attached.rb index e90b75afd0..9c4b6d5d1d 100644 --- a/activestorage/lib/active_storage/attached.rb +++ b/activestorage/lib/active_storage/attached.rb @@ -5,8 +5,8 @@ require "action_dispatch/http/upload" require "active_support/core_ext/module/delegation" module ActiveStorage -# Abstract baseclass for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many -# classes that both provide proxy access to the blob association for a record. + # Abstract base class for the concrete ActiveStorage::Attached::One and ActiveStorage::Attached::Many + # classes that both provide proxy access to the blob association for a record. class Attached attr_reader :name, :record diff --git a/activestorage/lib/active_storage/attached/macros.rb b/activestorage/lib/active_storage/attached/macros.rb index 027112195f..f3879ee2e3 100644 --- a/activestorage/lib/active_storage/attached/macros.rb +++ b/activestorage/lib/active_storage/attached/macros.rb @@ -19,7 +19,7 @@ module ActiveStorage # most circumstances. # # The system has been designed to having you go through the ActiveStorage::Attached::One - # proxy that provides the dynamic proxy to the associations and factory methods, like +#attach+. + # proxy that provides the dynamic proxy to the associations and factory methods, like +attach+. # # If the +:dependent+ option isn't set, the attachment will be purged # (i.e. destroyed) whenever the record is destroyed. diff --git a/activestorage/lib/active_storage/gem_version.rb b/activestorage/lib/active_storage/gem_version.rb index bb6241ae47..e1d7b3493a 100644 --- a/activestorage/lib/active_storage/gem_version.rb +++ b/activestorage/lib/active_storage/gem_version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ActiveStorage - # Returns the version of the currently loaded Active Storage as a <tt>Gem::Version</tt> + # Returns the version of the currently loaded Active Storage as a <tt>Gem::Version</tt>. def self.gem_version Gem::Version.new VERSION::STRING end diff --git a/activestorage/lib/active_storage/log_subscriber.rb b/activestorage/lib/active_storage/log_subscriber.rb index 80cc5d5c80..0d00a75c0e 100644 --- a/activestorage/lib/active_storage/log_subscriber.rb +++ b/activestorage/lib/active_storage/log_subscriber.rb @@ -19,7 +19,7 @@ module ActiveStorage end def service_exist(event) - debug event, color("Checked if file exist at key: #{key_in(event)} (#{event.payload[:exist] ? "yes" : "no"})", BLUE) + debug event, color("Checked if file exists at key: #{key_in(event)} (#{event.payload[:exist] ? "yes" : "no"})", BLUE) end def service_url(event) diff --git a/activestorage/lib/active_storage/service/azure_storage_service.rb b/activestorage/lib/active_storage/service/azure_storage_service.rb index d066deaa20..d0ee2273c5 100644 --- a/activestorage/lib/active_storage/service/azure_storage_service.rb +++ b/activestorage/lib/active_storage/service/azure_storage_service.rb @@ -5,7 +5,7 @@ require "azure/storage" require "azure/storage/core/auth/shared_access_signature" module ActiveStorage - # Wraps the Microsoft Azure Storage Blob Service as a Active Storage service. + # Wraps the Microsoft Azure Storage Blob Service as an Active Storage service. # See ActiveStorage::Service for the generic API documentation that applies to all services. class Service::AzureStorageService < Service attr_reader :client, :path, :blobs, :container, :signer diff --git a/activestorage/lib/active_storage/service/disk_service.rb b/activestorage/lib/active_storage/service/disk_service.rb index cf4019966f..1d121dcb6c 100644 --- a/activestorage/lib/active_storage/service/disk_service.rb +++ b/activestorage/lib/active_storage/service/disk_service.rb @@ -6,7 +6,7 @@ require "digest/md5" require "active_support/core_ext/numeric/bytes" module ActiveStorage - # Wraps a local disk path as a Active Storage service. See ActiveStorage::Service for the generic API + # Wraps a local disk path as an Active Storage service. See ActiveStorage::Service for the generic API # documentation that applies to all services. class Service::DiskService < Service attr_reader :root @@ -84,8 +84,8 @@ module ActiveStorage content_length: content_length, checksum: checksum }, - expires_in: expires_in, - purpose: :blob_token + { expires_in: expires_in, + purpose: :blob_token } ) generated_url = diff --git a/activestorage/lib/active_storage/service/gcs_service.rb b/activestorage/lib/active_storage/service/gcs_service.rb index 32dfb4851b..e656f2c69f 100644 --- a/activestorage/lib/active_storage/service/gcs_service.rb +++ b/activestorage/lib/active_storage/service/gcs_service.rb @@ -4,7 +4,7 @@ require "google/cloud/storage" require "active_support/core_ext/object/to_query" module ActiveStorage - # Wraps the Google Cloud Storage as a Active Storage service. See ActiveStorage::Service for the generic API + # Wraps the Google Cloud Storage as an Active Storage service. See ActiveStorage::Service for the generic API # documentation that applies to all services. class Service::GCSService < Service attr_reader :client, :bucket diff --git a/activestorage/lib/active_storage/service/s3_service.rb b/activestorage/lib/active_storage/service/s3_service.rb index 98c9dd2972..bef2ecbea9 100644 --- a/activestorage/lib/active_storage/service/s3_service.rb +++ b/activestorage/lib/active_storage/service/s3_service.rb @@ -4,7 +4,7 @@ require "aws-sdk" require "active_support/core_ext/numeric/bytes" module ActiveStorage - # Wraps the Amazon Simple Storage Service (S3) as a Active Storage service. + # Wraps the Amazon Simple Storage Service (S3) as an Active Storage service. # See ActiveStorage::Service for the generic API documentation that applies to all services. class Service::S3Service < Service attr_reader :client, :bucket, :upload_options diff --git a/activestorage/lib/tasks/activestorage.rake b/activestorage/lib/tasks/activestorage.rake index 746bc5b28a..ef923e5926 100644 --- a/activestorage/lib/tasks/activestorage.rake +++ b/activestorage/lib/tasks/activestorage.rake @@ -1,6 +1,6 @@ # frozen_string_literal: true -namespace :activestorage do +namespace :active_storage do desc "Copy over the migration needed to the application" task install: :environment do Rake::Task["active_storage:install:migrations"].invoke diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index c23b319046..c124416595 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -27,11 +27,17 @@ module ActiveSupport base.class_eval do @load_hooks = Hash.new { |h, k| h[k] = [] } @loaded = Hash.new { |h, k| h[k] = [] } + @run_once = Hash.new { |h, k| h[k] = [] } end end # Declares a block that will be executed when a Rails component is fully # loaded. + # + # Options: + # + # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+. + # * <tt>:run_once</tt> - Given +block+ will run only once. def on_load(name, options = {}, &block) @loaded[name].each do |base| execute_hook(base, options, block) @@ -40,20 +46,32 @@ module ActiveSupport @load_hooks[name] << [block, options] end - def execute_hook(base, options, block) - if options[:yield] - block.call(base) - else - base.instance_eval(&block) - end - end - def run_load_hooks(name, base = Object) @loaded[name] << base @load_hooks[name].each do |hook, options| execute_hook(base, options, hook) end end + + private + + def with_execution_control(name, block, once) + unless @run_once[name].include?(block) + @run_once[name] << block if once + + yield + end + end + + def execute_hook(base, options, block) + with_execution_control(name, block, options[:run_once]) do + if options[:yield] + block.call(base) + else + base.instance_eval(&block) + end + end + end end extend LazyLoadHooks diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 80d03ec9d6..eafa2e1712 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -100,11 +100,11 @@ class TestJSONEncoding < ActiveSupport::TestCase end def test_hash_should_allow_key_filtering_with_only - assert_equal %({"a":1}), ActiveSupport::JSON.encode({ "a" => 1, :b => 2, :c => 3 }, only: "a") + assert_equal %({"a":1}), ActiveSupport::JSON.encode({ "a" => 1, :b => 2, :c => 3 }, { only: "a" }) end def test_hash_should_allow_key_filtering_with_except - assert_equal %({"b":2}), ActiveSupport::JSON.encode({ "foo" => "bar", :b => 2, :c => 3 }, except: ["foo", :c]) + assert_equal %({"b":2}), ActiveSupport::JSON.encode({ "foo" => "bar", :b => 2, :c => 3 }, { except: ["foo", :c] }) end def test_time_to_json_includes_local_offset diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb index 9c9264e8fc..c161005100 100644 --- a/activesupport/test/lazy_load_hooks_test.rb +++ b/activesupport/test/lazy_load_hooks_test.rb @@ -20,6 +20,18 @@ class LazyLoadHooksTest < ActiveSupport::TestCase assert_equal 7, i end + def test_basic_hook_with_two_registrations_only_once + i = 0 + ActiveSupport.on_load(:basic_hook_with_two_once, run_once: true) do + i += incr + end + assert_equal 0, i + ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(2)) + assert_equal 2, i + ActiveSupport.run_load_hooks(:basic_hook_with_two_once, FakeContext.new(5)) + assert_equal 2, i + end + def test_hook_registered_after_run i = 0 ActiveSupport.run_load_hooks(:registered_after) @@ -37,6 +49,15 @@ class LazyLoadHooksTest < ActiveSupport::TestCase assert_equal 7, i end + def test_hook_registered_after_run_with_two_registrations_only_once + i = 0 + ActiveSupport.run_load_hooks(:registered_after_with_two_once, FakeContext.new(2)) + ActiveSupport.run_load_hooks(:registered_after_with_two_once, FakeContext.new(5)) + assert_equal 0, i + ActiveSupport.on_load(:registered_after_with_two_once, run_once: true) { i += incr } + assert_equal 2, i + end + def test_hook_registered_interleaved_run_with_two_registrations i = 0 ActiveSupport.run_load_hooks(:registered_interleaved_with_two, FakeContext.new(2)) @@ -47,6 +68,22 @@ class LazyLoadHooksTest < ActiveSupport::TestCase assert_equal 7, i end + def test_hook_registered_interleaved_run_with_two_registrations_once + i = 0 + ActiveSupport + .run_load_hooks(:registered_interleaved_with_two_once, FakeContext.new(2)) + assert_equal 0, i + + ActiveSupport.on_load(:registered_interleaved_with_two_once, run_once: true) do + i += incr + end + assert_equal 2, i + + ActiveSupport + .run_load_hooks(:registered_interleaved_with_two_once, FakeContext.new(5)) + assert_equal 2, i + end + def test_hook_receives_a_context i = 0 ActiveSupport.on_load(:contextual) { i += incr } diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index fc4f773e3c..53417f012e 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -429,7 +429,7 @@ end WARNING. The `after_commit` and `after_rollback` callbacks are called for all models created, updated, or destroyed within a transaction block. However, if an exception is raised within one of these callbacks, the exception will bubble up and any remaining `after_commit` or `after_rollback` methods will _not_ be executed. As such, if your callback code could raise an exception, you'll need to rescue it and handle it within the callback in order to allow other callbacks to run. -WARNING. Using `after_create_commit` and `after_update_commit` both in the same model will override the callback which was registered first amongst them. +WARNING. Using both `after_create_commit` and `after_update_commit` in the same model will only allow the last callback defined to take effect, and will override all others. ```ruby class User < ApplicationRecord diff --git a/guides/source/engines.md b/guides/source/engines.md index 2276f348a1..c7331b6ca4 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -346,6 +346,9 @@ invoke test_unit create test/controllers/blorgh/articles_controller_test.rb invoke helper create app/helpers/blorgh/articles_helper.rb +invoke test_unit +create test/application_system_test_case.rb +create test/system/articles_test.rb invoke assets invoke js create app/assets/javascripts/blorgh/articles.js diff --git a/guides/source/generators.md b/guides/source/generators.md index 389224d908..37af7a1310 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -197,6 +197,9 @@ $ bin/rails generate scaffold User name:string invoke jbuilder create app/views/users/index.json.jbuilder create app/views/users/show.json.jbuilder + invoke test_unit + create test/application_system_test_case.rb + create test/system/users_test.rb invoke assets invoke coffee create app/assets/javascripts/users.coffee @@ -415,6 +418,9 @@ $ bin/rails generate scaffold Comment body:text invoke jbuilder create app/views/comments/index.json.jbuilder create app/views/comments/show.json.jbuilder + invoke test_unit + create test/application_system_test_case.rb + create test/system/comments_test.rb invoke assets invoke coffee create app/assets/javascripts/comments.coffee diff --git a/guides/source/plugins.md b/guides/source/plugins.md index 8c2d56ceb8..164207a9f9 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -74,7 +74,7 @@ In this example you will add a method to String named `to_squawk`. To begin, cre ```ruby # yaffle/test/core_ext_test.rb -require 'test_helper' +require "test_helper" class CoreExtTest < ActiveSupport::TestCase def test_to_squawk_prepends_the_word_squawk @@ -104,14 +104,16 @@ Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s. Great - now you are ready to start development. -In `lib/yaffle.rb`, add `require 'yaffle/core_ext'`: +In `lib/yaffle.rb`, add `require "yaffle/core_ext"`: ```ruby # yaffle/lib/yaffle.rb -require 'yaffle/core_ext' +require "yaffle/railtie" +require "yaffle/core_ext" module Yaffle + # Your code goes here... end ``` @@ -120,7 +122,7 @@ Finally, create the `core_ext.rb` file and add the `to_squawk` method: ```ruby # yaffle/lib/yaffle/core_ext.rb -String.class_eval do +class String def to_squawk "squawk! #{self}".strip end @@ -152,7 +154,7 @@ To begin, set up your files so that you have: ```ruby # yaffle/test/acts_as_yaffle_test.rb -require 'test_helper' +require "test_helper" class ActsAsYaffleTest < ActiveSupport::TestCase end @@ -161,10 +163,12 @@ end ```ruby # yaffle/lib/yaffle.rb -require 'yaffle/core_ext' -require 'yaffle/acts_as_yaffle' +require "yaffle/railtie" +require "yaffle/core_ext" +require "yaffle/acts_as_yaffle" module Yaffle + # Your code goes here... end ``` @@ -173,7 +177,6 @@ end module Yaffle module ActsAsYaffle - # your code will go here end end ``` @@ -189,7 +192,7 @@ To start out, write a failing test that shows the behavior you'd like: ```ruby # yaffle/test/acts_as_yaffle_test.rb -require 'test_helper' +require "test_helper" class ActsAsYaffleTest < ActiveSupport::TestCase def test_a_hickwalls_yaffle_text_field_should_be_last_squawk @@ -276,12 +279,8 @@ module Yaffle module ActsAsYaffle extend ActiveSupport::Concern - included do - end - - module ClassMethods + class_methods do def acts_as_yaffle(options = {}) - # your code will go here end end end @@ -335,10 +334,7 @@ module Yaffle module ActsAsYaffle extend ActiveSupport::Concern - included do - end - - module ClassMethods + class_methods do def acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s end @@ -370,7 +366,7 @@ To start out, write a failing test that shows the behavior you'd like: ```ruby # yaffle/test/acts_as_yaffle_test.rb -require 'test_helper' +require "test_helper" class ActsAsYaffleTest < ActiveSupport::TestCase def test_a_hickwalls_yaffle_text_field_should_be_last_squawk @@ -406,19 +402,14 @@ module Yaffle extend ActiveSupport::Concern included do + def squawk(string) + write_attribute(self.class.yaffle_text_field, string.to_squawk) + end end - module ClassMethods + class_methods do def acts_as_yaffle(options = {}) cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s - - include Yaffle::ActsAsYaffle::LocalInstanceMethods - end - end - - module LocalInstanceMethods - def squawk(string) - write_attribute(self.class.yaffle_text_field, string.to_squawk) end end end @@ -449,7 +440,7 @@ Generators ---------- Generators can be included in your gem simply by creating them in a lib/generators directory of your plugin. More information about -the creation of generators can be found in the [Generators Guide](generators.html) +the creation of generators can be found in the [Generators Guide](generators.html). Publishing Your Gem ------------------- @@ -458,13 +449,13 @@ Gem plugins currently in development can easily be shared from any Git repositor commit the code to a Git repository (like GitHub) and add a line to the Gemfile of the application in question: ```ruby -gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git' +gem "yaffle", git: "https://github.com/rails/yaffle.git" ``` After running `bundle install`, your gem functionality will be available to the application. -When the gem is ready to be shared as a formal release, it can be published to [RubyGems](http://www.rubygems.org). -For more information about publishing gems to RubyGems, see: [Creating and Publishing Your First Ruby Gem](http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html). +When the gem is ready to be shared as a formal release, it can be published to [RubyGems](https://rubygems.org). +For more information about publishing gems to RubyGems, see: [Publishing your gem](http://guides.rubygems.org/publishing). RDoc Documentation ------------------ @@ -478,7 +469,7 @@ The first step is to update the README file with detailed information about how * How to add the functionality to the app (several examples of common use cases) * Warnings, gotchas or tips that might help users and save them time -Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add '#:nodoc:' comments to those parts of the code that are not included in the public API. +Once your README is solid, go through and add rdoc comments to all of the methods that developers will use. It's also customary to add `#:nodoc:` comments to those parts of the code that are not included in the public API. Once your comments are good to go, navigate to your plugin directory and run: diff --git a/guides/source/security.md b/guides/source/security.md index 297680b176..56969c7aea 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1060,7 +1060,7 @@ Additional Resources The security landscape shifts and it is important to keep up to date, because missing a new vulnerability can be catastrophic. You can find additional resources about (Rails) security here: -* Subscribe to the Rails security [mailing list.](http://groups.google.com/group/rubyonrails-security) +* Subscribe to the Rails security [mailing list](http://groups.google.com/group/rubyonrails-security). * [Brakeman - Rails Security Scanner](http://brakemanscanner.org/) - To perform static security analysis for Rails applications. -* [Keep up to date on the other application layers.](http://secunia.com/) (they have a weekly newsletter, too) -* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet.](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet) +* [Keep up to date on the other application layers](http://secunia.com/) (they have a weekly newsletter, too). +* A [good security blog](https://www.owasp.org) including the [Cross-Site scripting Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet). diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 6e927d7244..49a0584d51 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -278,7 +278,7 @@ You can now just call the dependency once with a wildcard. ### `ActionView::Helpers::RecordTagHelper` moved to external gem (record_tag_helper) -`content_tag_for` and `div_for` has been removed in favor of just using `content_tag`. To continue using it, add the `record_tag_helper` gem to your Gemfile: +`content_tag_for` and `div_for` have been removed in favor of just using `content_tag`. To continue using the older methods, add the `record_tag_helper` gem to your Gemfile: ```ruby gem 'record_tag_helper', '~> 1.0' diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 17d9ff8116..7b720d6e18 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add `--skip-yarn` option to the plugin generator. + + *bogdanvlviv* + * Optimize routes indentation. *Yoshiyuki Hirano* @@ -10,7 +14,7 @@ *Yoshiyuki Hirano* -* Add git_source to `Gemfile` for plugin generator. +* Add `git_source` to `Gemfile` for plugin generator. *Yoshiyuki Hirano* @@ -80,7 +84,7 @@ * Load environment file in `dbconsole` command. - Fixes #29717 + Fixes #29717. *Yuji Yaginuma* diff --git a/railties/lib/rails/engine/updater.rb b/railties/lib/rails/engine/updater.rb index 2e0aef8aa7..21a0fc5562 100644 --- a/railties/lib/rails/engine/updater.rb +++ b/railties/lib/rails/engine/updater.rb @@ -9,7 +9,7 @@ module Rails class << self def generator @generator ||= Rails::Generators::PluginGenerator.new ["plugin"], - { engine: true }, destination_root: ENGINE_ROOT + { engine: true }, { destination_root: ENGINE_ROOT } end def run(action) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index eda12b6da2..c773e07eba 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -145,7 +145,7 @@ module Rails create_file("vendor/#{filename}", optimize_indentation(data), verbose: false) end - # Create a new file in the lib/ directory. Code can be specified + # Create a new file in the <tt>lib/</tt> directory. Code can be specified # in a block or a data string can be given. # # lib("crypto.rb") do diff --git a/railties/lib/rails/generators/rails/app/templates/gitignore b/railties/lib/rails/generators/rails/app/templates/gitignore index d8eeb1772d..83a7b211aa 100644 --- a/railties/lib/rails/generators/rails/app/templates/gitignore +++ b/railties/lib/rails/generators/rails/app/templates/gitignore @@ -27,7 +27,6 @@ <% unless options.skip_yarn? -%> /node_modules /yarn-error.log - <% end -%> <% unless options.api? -%> diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb index a0de708913..eb941adf95 100644 --- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb +++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb @@ -89,7 +89,7 @@ task default: :test PASSTHROUGH_OPTIONS = [ :skip_active_record, :skip_action_mailer, :skip_javascript, :skip_action_cable, :skip_sprockets, :database, - :javascript, :api, :quiet, :pretend, :skip + :javascript, :skip_yarn, :api, :quiet, :pretend, :skip ] def generate_test_dummy(force = false) diff --git a/railties/lib/rails/generators/rails/plugin/templates/gitignore b/railties/lib/rails/generators/rails/plugin/templates/gitignore index 757172e6a6..8c7cad74ed 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/gitignore +++ b/railties/lib/rails/generators/rails/plugin/templates/gitignore @@ -5,5 +5,9 @@ pkg/ <%= dummy_path %>/db/*.sqlite3 <%= dummy_path %>/db/*.sqlite3-journal <%= dummy_path %>/log/*.log +<% unless options[:skip_yarn] -%> +<%= dummy_path %>/node_modules/ +<%= dummy_path %>/yarn-error.log +<% end -%> <%= dummy_path %>/tmp/ <% end -%> diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 0f578fe270..8931aabcda 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -11,7 +11,7 @@ namespace :app do template = File.expand_path(template) if template !~ %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} require_relative "../generators" require_relative "../generators/rails/app/app_generator" - generator = Rails::Generators::AppGenerator.new [Rails.root], {}, destination_root: Rails.root + generator = Rails::Generators::AppGenerator.new [Rails.root], {}, { destination_root: Rails.root } generator.apply template, verbose: false end diff --git a/railties/test/application/assets_test.rb b/railties/test/application/assets_test.rb index e0afc67174..99fe6749d9 100644 --- a/railties/test/application/assets_test.rb +++ b/railties/test/application/assets_test.rb @@ -477,9 +477,9 @@ module ApplicationTests class ::PostsController < ActionController::Base; end - get "/posts", {}, "HTTPS" => "off" + get "/posts", {}, { "HTTPS" => "off" } assert_match('src="http://example.com/assets/application.self.js', last_response.body) - get "/posts", {}, "HTTPS" => "on" + get "/posts", {}, { "HTTPS" => "on" } assert_match('src="https://example.com/assets/application.self.js', last_response.body) end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 72942a527c..ebe019f6a9 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -1191,6 +1191,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters @@ -1202,6 +1203,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal %w(controller action), ActionController::Parameters.always_permitted_parameters end @@ -1214,6 +1216,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal %w( controller action format ), ActionController::Parameters.always_permitted_parameters end @@ -1238,6 +1241,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters @@ -1249,6 +1253,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters end @@ -1257,6 +1262,7 @@ module ApplicationTests app "test" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal :log, ActionController::Parameters.action_on_unpermitted_parameters end @@ -1265,6 +1271,7 @@ module ApplicationTests app "production" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal false, ActionController::Parameters.action_on_unpermitted_parameters end @@ -1284,6 +1291,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal true, ActionController::Parameters.permit_all_parameters end @@ -1295,6 +1303,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal [], ActionController::Parameters.always_permitted_parameters end @@ -1306,6 +1315,7 @@ module ApplicationTests app "development" force_lazy_load_hooks { ActionController::Base } + force_lazy_load_hooks { ActionController::API } assert_equal :raise, ActionController::Parameters.action_on_unpermitted_parameters end @@ -1323,10 +1333,10 @@ module ApplicationTests end end - get "/", {}, "HTTP_ACCEPT" => "application/xml" + get "/", {}, { "HTTP_ACCEPT" => "application/xml" } assert_equal "HTML", last_response.body - get "/", { format: :xml }, "HTTP_ACCEPT" => "application/xml" + get "/", { format: :xml }, { "HTTP_ACCEPT" => "application/xml" } assert_equal "XML", last_response.body end diff --git a/railties/test/application/mailer_previews_test.rb b/railties/test/application/mailer_previews_test.rb index 6b872151b8..4e77cece1b 100644 --- a/railties/test/application/mailer_previews_test.rb +++ b/railties/test/application/mailer_previews_test.rb @@ -32,7 +32,7 @@ module ApplicationTests test "/rails/mailers is accessible with correct configuration" do add_to_config "config.action_mailer.show_previews = true" app("production") - get "/rails/mailers", {}, "REMOTE_ADDR" => "4.2.42.42" + get "/rails/mailers", {}, { "REMOTE_ADDR" => "4.2.42.42" } assert_equal 200, last_response.status end @@ -482,7 +482,7 @@ module ApplicationTests app("development") - get "/rails/mailers", {}, "SCRIPT_NAME" => "/my_app" + get "/rails/mailers", {}, { "SCRIPT_NAME" => "/my_app" } assert_match '<h3><a href="/my_app/rails/mailers/notifier">Notifier</a></h3>', last_response.body assert_match '<li><a href="/my_app/rails/mailers/notifier/foo">foo</a></li>', last_response.body end diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb index 05f4317ad3..9822ec563d 100644 --- a/railties/test/application/middleware/cache_test.rb +++ b/railties/test/application/middleware/cache_test.rb @@ -56,7 +56,7 @@ module ApplicationTests simple_controller expected = "Wed, 30 May 1984 19:43:31 GMT" - get "/expires/keeps_if_modified_since", {}, "HTTP_IF_MODIFIED_SINCE" => expected + get "/expires/keeps_if_modified_since", {}, { "HTTP_IF_MODIFIED_SINCE" => expected } assert_equal 200, last_response.status assert_equal expected, last_response.body, "cache should have kept If-Modified-Since" @@ -121,7 +121,7 @@ module ApplicationTests etag = last_response.headers["ETag"] - get "/expires/expires_etag", {}, "HTTP_IF_NONE_MATCH" => etag + get "/expires/expires_etag", {}, { "HTTP_IF_NONE_MATCH" => etag } assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"] assert_equal 304, last_response.status assert_equal "", last_response.body @@ -139,7 +139,7 @@ module ApplicationTests body = last_response.body etag = last_response.headers["ETag"] - get "/expires/expires_etag", { private: true }, "HTTP_IF_NONE_MATCH" => etag + get "/expires/expires_etag", { private: true }, { "HTTP_IF_NONE_MATCH" => etag } assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end @@ -155,7 +155,7 @@ module ApplicationTests last = last_response.headers["Last-Modified"] - get "/expires/expires_last_modified", {}, "HTTP_IF_MODIFIED_SINCE" => last + get "/expires/expires_last_modified", {}, { "HTTP_IF_MODIFIED_SINCE" => last } assert_equal "stale, valid, store", last_response.headers["X-Rack-Cache"] assert_equal 304, last_response.status assert_equal "", last_response.body @@ -173,7 +173,7 @@ module ApplicationTests body = last_response.body last = last_response.headers["Last-Modified"] - get "/expires/expires_last_modified", { private: true }, "HTTP_IF_MODIFIED_SINCE" => last + get "/expires/expires_last_modified", { private: true }, { "HTTP_IF_MODIFIED_SINCE" => last } assert_equal "miss", last_response.headers["X-Rack-Cache"] assert_not_equal body, last_response.body end diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index b998e8a0bd..0a5a524692 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -276,7 +276,7 @@ module ApplicationTests assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"] assert_equal etag, last_response.headers["Etag"] - get "/", {}, "HTTP_IF_NONE_MATCH" => etag + get "/", {}, { "HTTP_IF_NONE_MATCH" => etag } assert_equal 304, last_response.status assert_equal "", last_response.body assert_nil last_response.headers["Content-Type"] diff --git a/railties/test/generators/api_app_generator_test.rb b/railties/test/generators/api_app_generator_test.rb index ba7f7bb5f3..d141b1d4b4 100644 --- a/railties/test/generators/api_app_generator_test.rb +++ b/railties/test/generators/api_app_generator_test.rb @@ -36,7 +36,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase run_generator assert_file ".gitignore" do |content| - assert_no_match(/\/public\/asserts/, content) + assert_no_match(/\/public\/assets/, content) end assert_file "Gemfile" do |content| @@ -67,7 +67,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase run_generator generator = Rails::Generators::AppGenerator.new ["rails"], - { api: true, update: true }, destination_root: destination_root, shell: @shell + { api: true, update: true }, { destination_root: destination_root, shell: @shell } quietly { generator.send(:update_config_files) } assert_no_file "config/initializers/cookies_serializer.rb" @@ -78,7 +78,7 @@ class ApiAppGeneratorTest < Rails::Generators::TestCase run_generator generator = Rails::Generators::AppGenerator.new ["rails"], - { api: true, update: true }, destination_root: destination_root, shell: @shell + { api: true, update: true }, { destination_root: destination_root, shell: @shell } quietly { generator.send(:update_bin_files) } assert_no_file "bin/yarn" diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 1183dcd34d..b50b9364d0 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -248,7 +248,7 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_no_file "#{app_root}/config/initializers/new_framework_defaults_5_2.rb" stub_rails_application(app_root) do - generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, destination_root: app_root, shell: @shell + generator = Rails::Generators::AppGenerator.new ["rails"], { update: true }, { destination_root: app_root, shell: @shell } generator.send(:app_const) quietly { generator.send(:update_config_files) } @@ -584,32 +584,6 @@ class AppGeneratorTest < Rails::Generators::TestCase end end - def test_generator_for_yarn - run_generator([destination_root]) - assert_file "package.json", /dependencies/ - assert_file "config/initializers/assets.rb", /node_modules/ - - assert_file ".gitignore" do |content| - assert_match(/node_modules/, content) - assert_match(/yarn-error\.log/, content) - end - end - - def test_generator_for_yarn_skipped - run_generator([destination_root, "--skip-yarn"]) - assert_no_file "package.json" - assert_no_file "bin/yarn" - - assert_file "config/initializers/assets.rb" do |content| - assert_no_match(/node_modules/, content) - end - - assert_file ".gitignore" do |content| - assert_no_match(/node_modules/, content) - assert_no_match(/yarn-error\.log/, content) - end - end - def test_inclusion_of_jbuilder run_generator assert_gem "jbuilder" diff --git a/railties/test/generators/create_migration_test.rb b/railties/test/generators/create_migration_test.rb index ce7bd3f5a0..3cb7fd6baa 100644 --- a/railties/test/generators/create_migration_test.rb +++ b/railties/test/generators/create_migration_test.rb @@ -53,7 +53,7 @@ class CreateMigrationTest < Rails::Generators::TestCase end def test_invoke_pretended - create_migration(default_destination_path, {}, pretend: true) + create_migration(default_destination_path, {}, { pretend: true }) assert_no_file @migration.destination end @@ -94,7 +94,7 @@ class CreateMigrationTest < Rails::Generators::TestCase def test_invoke_forced_pretended_when_exists_not_identical migration_exists! - create_migration(default_destination_path, { force: true }, pretend: true) do + create_migration(default_destination_path, { force: true }, { pretend: true }) do "different content" end @@ -106,7 +106,7 @@ class CreateMigrationTest < Rails::Generators::TestCase def test_invoke_skipped_when_exists_not_identical migration_exists! - create_migration(default_destination_path, {}, skip: true) { "different content" } + create_migration(default_destination_path, {}, { skip: true }) { "different content" } assert_match(/skip db\/migrate\/2_create_articles\.rb\n/, invoke!) assert_no_file @migration.destination @@ -122,7 +122,7 @@ class CreateMigrationTest < Rails::Generators::TestCase def test_revoke_pretended migration_exists! - create_migration(default_destination_path, {}, pretend: true) + create_migration(default_destination_path, {}, { pretend: true }) assert_match(/remove db\/migrate\/1_create_articles\.rb\n/, revoke!) assert_file @existing_migration.destination diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb index bd76af86e1..e94b1ac8fe 100644 --- a/railties/test/generators/plugin_generator_test.rb +++ b/railties/test/generators/plugin_generator_test.rb @@ -26,6 +26,10 @@ class PluginGeneratorTest < Rails::Generators::TestCase destination File.join(Rails.root, "tmp/bukkits") arguments [destination_root] + def application_path + "#{destination_root}/test/dummy" + end + # brings setup, teardown, and some tests include SharedGeneratorTests diff --git a/railties/test/generators/shared_generator_tests.rb b/railties/test/generators/shared_generator_tests.rb index c73b91e3f8..6c0775b50e 100644 --- a/railties/test/generators/shared_generator_tests.rb +++ b/railties/test/generators/shared_generator_tests.rb @@ -22,6 +22,10 @@ module SharedGeneratorTests Rails.application = TestApp::Application.instance end + def application_path + destination_root + end + def test_skeleton_is_created run_generator @@ -123,4 +127,30 @@ module SharedGeneratorTests assert_no_file("app/models/concerns/.keep") end + + def test_generator_for_yarn + run_generator + assert_file "#{application_path}/package.json", /dependencies/ + assert_file "#{application_path}/config/initializers/assets.rb", /node_modules/ + + assert_file ".gitignore" do |content| + assert_match(/node_modules/, content) + assert_match(/yarn-error\.log/, content) + end + end + + def test_generator_for_yarn_skipped + run_generator([destination_root, "--skip-yarn"]) + assert_no_file "#{application_path}/package.json" + assert_no_file "#{application_path}/bin/yarn" + + assert_file "#{application_path}/config/initializers/assets.rb" do |content| + assert_no_match(/node_modules/, content) + end + + assert_file ".gitignore" do |content| + assert_no_match(/node_modules/, content) + assert_no_match(/yarn-error\.log/, content) + end + end end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 350d39ad09..e6964b4b18 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -1297,10 +1297,10 @@ YAML boot_rails - get("/bukkits/bukkit", {}, "SCRIPT_NAME" => "/foo") + get("/bukkits/bukkit", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bar", last_response.body - get("/bar", {}, "SCRIPT_NAME" => "/foo") + get("/bar", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bukkits/bukkit", last_response.body end @@ -1346,10 +1346,10 @@ YAML boot_rails - get("/bukkits/bukkit", {}, "SCRIPT_NAME" => "/foo") + get("/bukkits/bukkit", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bar", last_response.body - get("/bar", {}, "SCRIPT_NAME" => "/foo") + get("/bar", {}, { "SCRIPT_NAME" => "/foo" }) assert_equal "/foo/bukkits/bukkit", last_response.body end diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb index 6b40a085ae..48f0fbc80f 100644 --- a/railties/test/railties/mounted_engine_test.rb +++ b/railties/test/railties/mounted_engine_test.rb @@ -208,7 +208,7 @@ module ApplicationTests assert_equal "/ada/blog/posts/1", last_response.body # test generating engine's route from engine with default_url_options - get "/john/blog/posts", {}, "SCRIPT_NAME" => "/foo" + get "/john/blog/posts", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/john/blog/posts/1", last_response.body # test generating engine's route from application @@ -222,10 +222,10 @@ module ApplicationTests assert_equal "/john/blog/posts", last_response.body # test generating engine's route from application with default_url_options - get "/engine_route", {}, "SCRIPT_NAME" => "/foo" + get "/engine_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/anonymous/blog/posts", last_response.body - get "/url_for_engine_route", {}, "SCRIPT_NAME" => "/foo" + get "/url_for_engine_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/john/blog/posts", last_response.body # test generating application's route from engine @@ -243,14 +243,14 @@ module ApplicationTests assert_equal "/anonymous/blog/posts/1", last_response.body # test generating engine's route from other engine with default_url_options - get "/metrics/generate_blog_route", {}, "SCRIPT_NAME" => "/foo" + get "/metrics/generate_blog_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/anonymous/blog/posts/1", last_response.body - get "/metrics/generate_blog_route_in_view", {}, "SCRIPT_NAME" => "/foo" + get "/metrics/generate_blog_route_in_view", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/anonymous/blog/posts/1", last_response.body # test generating application's route from engine with default_url_options - get "/someone/blog/generate_application_route", {}, "SCRIPT_NAME" => "/foo" + get "/someone/blog/generate_application_route", {}, { "SCRIPT_NAME" => "/foo" } assert_equal "/foo/", last_response.body # test polymorphic routes |