diff options
Diffstat (limited to 'actionview')
-rw-r--r-- | actionview/Rakefile | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/asset_tag_helper.rb | 25 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/form_helper.rb | 4 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/form_tag_helper.rb | 9 | ||||
-rw-r--r-- | actionview/lib/action_view/helpers/tags/base.rb | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/renderer/partial_renderer.rb | 2 | ||||
-rw-r--r-- | actionview/lib/action_view/test_case.rb | 14 | ||||
-rw-r--r-- | actionview/package.json | 2 | ||||
-rw-r--r-- | actionview/test/template/asset_tag_helper_test.rb | 3 | ||||
-rw-r--r-- | actionview/test/template/form_collections_helper_test.rb | 14 | ||||
-rw-r--r-- | actionview/test/template/form_helper_test.rb | 31 | ||||
-rw-r--r-- | actionview/test/template/form_tag_helper_test.rb | 37 | ||||
-rw-r--r-- | actionview/test/template/test_case_test.rb | 8 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-confirm.js | 2 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-remote.js | 2 |
15 files changed, 143 insertions, 14 deletions
diff --git a/actionview/Rakefile b/actionview/Rakefile index 0d974cb087..20dfa4e114 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -37,7 +37,7 @@ namespace :test do start_time = Time.now loop do - break if system("lsof -i :4567 >/dev/null") + break if system("lsof -i :4567", 1 => File::NULL) if Time.now - start_time > 5 puts "Timed out after 5 seconds" diff --git a/actionview/lib/action_view/helpers/asset_tag_helper.rb b/actionview/lib/action_view/helpers/asset_tag_helper.rb index 66e31978c6..82b0fcbf2e 100644 --- a/actionview/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionview/lib/action_view/helpers/asset_tag_helper.rb @@ -200,7 +200,7 @@ module ActionView end # Returns an HTML image tag for the +source+. The +source+ can be a full - # path or a file. + # path, a file or an Active Storage attachment. # # ==== Options # @@ -217,6 +217,8 @@ module ActionView # # ==== Examples # + # Assets (images that are part of your app): + # # image_tag("icon") # # => <img alt="Icon" src="/assets/icon" /> # image_tag("icon.png") @@ -235,12 +237,21 @@ module ActionView # # => <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x"> # image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw") # # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw"> + # + # Active Storage (images that are uploaded by the users of your app): + # + # image_tag(user.avatar) + # # => <img src="/rails/active_storage/blobs/.../tiger.jpg" alt="Tiger" /> + # image_tag(user.avatar.variant(resize: "100x100")) + # # => <img src="/rails/active_storage/variants/.../tiger.jpg" alt="Tiger" /> + # image_tag(user.avatar.variant(resize: "100x100"), size: '100') + # # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" alt="Tiger" /> def image_tag(source, options = {}) options = options.symbolize_keys check_for_image_tag_errors(options) skip_pipeline = options.delete(:skip_pipeline) - src = options[:src] = path_to_image(source, skip_pipeline: skip_pipeline) + src = options[:src] = resolve_image_source(source, skip_pipeline) unless src.start_with?("cid:") || src.start_with?("data:") || src.blank? options[:alt] = options.fetch(:alt) { image_alt(src) } @@ -364,6 +375,16 @@ module ActionView end end + def resolve_image_source(source, skip_pipeline) + if source.is_a?(Symbol) || source.is_a?(String) + path_to_image(source, skip_pipeline: skip_pipeline) + else + polymorphic_url(source) + end + rescue NoMethodError => e + raise ArgumentError, "Can't resolve image into URL: #{e}" + end + def extract_dimensions(size) size = size.to_s if /\A\d+x\d+\z/.match?(size) diff --git a/actionview/lib/action_view/helpers/form_helper.rb b/actionview/lib/action_view/helpers/form_helper.rb index 4eac086a87..082ea85aba 100644 --- a/actionview/lib/action_view/helpers/form_helper.rb +++ b/actionview/lib/action_view/helpers/form_helper.rb @@ -1193,7 +1193,7 @@ module ActionView # file_field(:attachment, :file, class: 'file_input') # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" /> def file_field(object_name, method, options = {}) - Tags::FileField.new(object_name, method, self, options).render + Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render end # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+) @@ -1569,7 +1569,7 @@ module ActionView # In the above block, a +FormBuilder+ object is yielded as the # +person_form+ variable. This allows you to generate the +text_field+ # and +check_box+ fields by specifying their eponymous methods, which - # modify the underlying template and associates the +@person+ model object + # modify the underlying template and associates the <tt>@person</tt> model object # with the form. # # The +FormBuilder+ object can be thought of as serving as a proxy for the diff --git a/actionview/lib/action_view/helpers/form_tag_helper.rb b/actionview/lib/action_view/helpers/form_tag_helper.rb index 91046acbf8..2519ff2837 100644 --- a/actionview/lib/action_view/helpers/form_tag_helper.rb +++ b/actionview/lib/action_view/helpers/form_tag_helper.rb @@ -274,7 +274,7 @@ module ActionView # file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html' # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" /> def file_field_tag(name, options = {}) - text_field_tag(name, nil, options.merge(type: :file)) + text_field_tag(name, nil, convert_direct_upload_option_to_url(options.merge(type: :file))) end # Creates a password field, a masked text field that will hide the users input behind a mask character. @@ -904,6 +904,13 @@ module ActionView tag_options.delete("data-disable-with") end + + def convert_direct_upload_option_to_url(options) + if options.delete(:direct_upload) && respond_to?(:rails_direct_uploads_url) + options["data-direct-upload-url"] = rails_direct_uploads_url + end + options + end end end end diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb index bbb8c4d224..922d4c5390 100644 --- a/actionview/lib/action_view/helpers/tags/base.rb +++ b/actionview/lib/action_view/helpers/tags/base.rb @@ -138,7 +138,7 @@ module ActionView end def sanitized_value(value) - value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase + value.to_s.gsub(/\s/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s end def select_content_tag(option_tags, options, html_options) diff --git a/actionview/lib/action_view/renderer/partial_renderer.rb b/actionview/lib/action_view/renderer/partial_renderer.rb index f548fc24ed..f2edcb750c 100644 --- a/actionview/lib/action_view/renderer/partial_renderer.rb +++ b/actionview/lib/action_view/renderer/partial_renderer.rb @@ -355,7 +355,7 @@ module ActionView # finds the options and details and extracts them. The method also contains # logic that handles the type of object passed in as the partial. # - # If +options[:partial]+ is a string, then the +@path+ instance variable is + # If +options[:partial]+ is a string, then the <tt>@path</tt> instance variable is # set to that string. Otherwise, the +options[:partial]+ object must # respond to +to_partial_path+ in order to setup the path. def setup(context, options, block) diff --git a/actionview/lib/action_view/test_case.rb b/actionview/lib/action_view/test_case.rb index 8542978e34..6913c31a20 100644 --- a/actionview/lib/action_view/test_case.rb +++ b/actionview/lib/action_view/test_case.rb @@ -103,6 +103,7 @@ module ActionView def setup_with_controller @controller = ActionView::TestCase::TestController.new @request = @controller.request + @view_flow = ActionView::OutputFlow.new # empty string ensures buffer has UTF-8 encoding as # new without arguments returns ASCII-8BIT encoded buffer like String#new @output_buffer = ActiveSupport::SafeBuffer.new "" @@ -246,6 +247,7 @@ module ActionView :@test_passed, :@view, :@view_context_class, + :@view_flow, :@_subscribers, :@html_document ] @@ -279,6 +281,18 @@ module ActionView super end end + + def respond_to_missing?(name, include_private = false) + begin + routes = @controller.respond_to?(:_routes) && @controller._routes + rescue + # Dont call routes, if there is an error on _routes call + end + + routes && + (routes.named_routes.route_defined?(name) || + routes.mounted_helpers.method_defined?(name)) + end end include Behavior diff --git a/actionview/package.json b/actionview/package.json index 85f4ddacbe..4cbf0207e5 100644 --- a/actionview/package.json +++ b/actionview/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "bundle exec blade build", "test": "echo \"See the README: https://github.com/rails/rails/blob/master/actionview/app/assets/javascripts#how-to-run-tests\" && exit 1", - "lint": "coffeelint app/assets/javascripts && eslint test/public/test" + "lint": "coffeelint app/assets/javascripts && eslint test/ujs/public/test" }, "repository": { "type": "git", diff --git a/actionview/test/template/asset_tag_helper_test.rb b/actionview/test/template/asset_tag_helper_test.rb index 4d312d3b6d..5b8e50cc5e 100644 --- a/actionview/test/template/asset_tag_helper_test.rb +++ b/actionview/test/template/asset_tag_helper_test.rb @@ -565,9 +565,6 @@ class AssetTagHelperTest < ActionView::TestCase def blank?; true; end def to_s; "no-image-yet.png"; end end - def test_image_tag_with_blank_placeholder - assert_equal '<img alt="" src="/images/no-image-yet.png" />', image_tag(PlaceholderImage.new, alt: "") - end def test_image_path_with_blank_placeholder assert_equal "/images/no-image-yet.png", image_path(PlaceholderImage.new) end diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb index aa72621c7d..bba529a98a 100644 --- a/actionview/test/template/form_collections_helper_test.rb +++ b/actionview/test/template/form_collections_helper_test.rb @@ -39,6 +39,13 @@ class FormCollectionsHelperTest < ActionView::TestCase assert_select "label[for=user_active_no]", "No" end + test "collection radio generates labels for non-English values correctly" do + with_collection_radio_buttons :user, :title, ["Господин", "Госпожа"], :to_s, :to_s + + assert_select "input[type=radio]#user_title_господин" + assert_select "label[for=user_title_господин]", "Господин" + end + test "collection radio should sanitize collection values for labels correctly" do with_collection_radio_buttons :user, :name, ["$0.99", "$1.99"], :to_s, :to_s assert_select "label[for=user_name_099]", "$0.99" @@ -299,6 +306,13 @@ class FormCollectionsHelperTest < ActionView::TestCase assert_select "label[for=user_name_199]", "$1.99" end + test "collection check boxes generates labels for non-English values correctly" do + with_collection_check_boxes :user, :title, ["Господин", "Госпожа"], :to_s, :to_s + + assert_select "input[type=checkbox]#user_title_господин" + assert_select "label[for=user_title_господин]", "Господин" + end + test "collection check boxes accepts html options as the last element of array" do collection = [[1, "Category 1", { class: "foo" }], [2, "Category 2", { class: "bar" }]] with_collection_check_boxes :user, :active, collection, :first, :second diff --git a/actionview/test/template/form_helper_test.rb b/actionview/test/template/form_helper_test.rb index 8d689aebeb..246f52588d 100644 --- a/actionview/test/template/form_helper_test.rb +++ b/actionview/test/template/form_helper_test.rb @@ -8,6 +8,16 @@ class FormHelperTest < ActionView::TestCase tests ActionView::Helpers::FormHelper + class WithActiveStorageRoutesControllers < ActionController::Base + test_routes do + post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads + end + + def url_options + { host: "testtwo.host" } + end + end + def form_for(*) @output_buffer = super end @@ -542,6 +552,27 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, file_field("import", "file", multiple: true, name: "custom") end + def test_file_field_with_direct_upload_when_rails_direct_uploads_url_is_not_defined + expected = '<input type="file" name="import[file]" id="import_file" />' + assert_dom_equal expected, file_field("import", "file", direct_upload: true) + end + + def test_file_field_with_direct_upload_when_rails_direct_uploads_url_is_defined + @controller = WithActiveStorageRoutesControllers.new + + expected = '<input data-direct-upload-url="http://testtwo.host/rails/active_storage/direct_uploads" type="file" name="import[file]" id="import_file" />' + assert_dom_equal expected, file_field("import", "file", direct_upload: true) + end + + def test_file_field_with_direct_upload_dont_mutate_arguments + original_options = { class: "pix", direct_upload: true } + + expected = '<input class="pix" type="file" name="import[file]" id="import_file" />' + assert_dom_equal expected, file_field("import", "file", original_options) + + assert_equal({ class: "pix", direct_upload: true }, original_options) + end + def test_hidden_field assert_dom_equal( '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />', diff --git a/actionview/test/template/form_tag_helper_test.rb b/actionview/test/template/form_tag_helper_test.rb index 62b67b0435..b985b9789b 100644 --- a/actionview/test/template/form_tag_helper_test.rb +++ b/actionview/test/template/form_tag_helper_test.rb @@ -7,6 +7,16 @@ class FormTagHelperTest < ActionView::TestCase tests ActionView::Helpers::FormTagHelper + class WithActiveStorageRoutesControllers < ActionController::Base + test_routes do + post "/rails/active_storage/direct_uploads" => "active_storage/direct_uploads#create", as: :rails_direct_uploads + end + + def url_options + { host: "testtwo.host" } + end + end + def setup super @controller = BasicController.new @@ -178,6 +188,33 @@ class FormTagHelperTest < ActionView::TestCase assert_dom_equal "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>", file_field_tag("picsplz", class: "pix") end + def test_file_field_tag_with_direct_upload_when_rails_direct_uploads_url_is_not_defined + assert_dom_equal( + "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>", + file_field_tag("picsplz", class: "pix", direct_upload: true) + ) + end + + def test_file_field_tag_with_direct_upload_when_rails_direct_uploads_url_is_defined + @controller = WithActiveStorageRoutesControllers.new + + assert_dom_equal( + "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\" data-direct-upload-url=\"http://testtwo.host/rails/active_storage/direct_uploads\"/>", + file_field_tag("picsplz", class: "pix", direct_upload: true) + ) + end + + def test_file_field_tag_with_direct_upload_dont_mutate_arguments + original_options = { class: "pix", direct_upload: true } + + assert_dom_equal( + "<input name=\"picsplz\" type=\"file\" id=\"picsplz\" class=\"pix\"/>", + file_field_tag("picsplz", original_options) + ) + + assert_equal({ class: "pix", direct_upload: true }, original_options) + end + def test_password_field_tag actual = password_field_tag expected = %(<input id="password" name="password" type="password" />) diff --git a/actionview/test/template/test_case_test.rb b/actionview/test/template/test_case_test.rb index f2f64ffa71..05e5f21ce4 100644 --- a/actionview/test/template/test_case_test.rb +++ b/actionview/test/template/test_case_test.rb @@ -282,6 +282,14 @@ module ActionView @customers = [DeveloperStruct.new("Eloy"), DeveloperStruct.new("Manfred")] assert_match(/Hello: EloyHello: Manfred/, render(file: "test/list")) end + + test "is able to use helpers that depend on the view flow" do + assert_not content_for?(:foo) + + content_for :foo, "bar" + assert content_for?(:foo) + assert_equal "bar", content_for(:foo) + end end class AssertionsTest < ActionView::TestCase diff --git a/actionview/test/ujs/public/test/data-confirm.js b/actionview/test/ujs/public/test/data-confirm.js index 229b9e1466..d1ea82ea7e 100644 --- a/actionview/test/ujs/public/test/data-confirm.js +++ b/actionview/test/ujs/public/test/data-confirm.js @@ -300,7 +300,7 @@ asyncTest('clicking on the children of a disabled button should not trigger a co window.confirm = function(msg) { message = msg; return false } $('button[data-confirm][disabled]') - .html("<strong>Click me</strong>") + .html('<strong>Click me</strong>') .bindNative('confirm', function() { App.assertCallbackNotInvoked('confirm') }) diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js index 161a92ac11..9bbefc18f2 100644 --- a/actionview/test/ujs/public/test/data-remote.js +++ b/actionview/test/ujs/public/test/data-remote.js @@ -411,7 +411,7 @@ asyncTest('form buttons should only be serialized when clicked', 4, function() { asyncTest('changing a select option without "data-url" attribute still fires ajax request to current location', 1, function() { var currentLocation, ajaxLocation - buildSelect({'data-url': ''}); + buildSelect({'data-url': ''}) $('select[data-remote]') .bindNative('ajax:beforeSend', function(e, xhr, settings) { |