New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make public asset use explicit #26226

Merged
merged 29 commits into from Aug 31, 2016

Conversation

Projects
None yet
8 participants
@schneems
Member

schneems commented Aug 19, 2016

Currently when an asset isn't found the behavior is to pass the string through. For example a valid asset will return a url from pipeline

asset_path("application.js")
# => assets/application-123098udasvi0mnafd.js

While if you make a typo, you won't get an error or anything, it just falls through:

asset_path("app1icati0n.js")
# => "app1icati0n.js"

This is bad. No exceptions are raised, the error goes undetected.

This PR makes adds an explicit api for when you want to use an asset that is not in the asset pipeline. If you want an asset from the "public" directory you can now explicitly say so by using public_folder: true

asset_path("app1icati0n.js", public_folder: true)
# => "app1icati0n.js"

Now there is no confusion by sprockets-rails about the intent of the method call. Likewise in rails/sprockets-rails#375 we are adding a flag unknown_asset_fallback. When it is set to true then the current fallback behavior will happen, but a deprecation will be emitted. When it is set to false an exception will be raised so you know immediately that the asset was not found.

We are defaulting it to false for all new apps.

# Unknown asset fallback will return the path passed in when the given
# asset is not present in the asset pipeline.
Rails.application.config.assets.unknown_asset_fallback = false

While this patch is intended to go along with the sprockets-rails patch, it should still function with older sprockets-rails versions, however no deprecations or exceptions will be raised.

@schneems

This comment has been minimized.

Show comment
Hide comment
@schneems

schneems Aug 19, 2016

Member

The sprockets-rails PR is rails/sprockets-rails#375

Member

schneems commented Aug 19, 2016

The sprockets-rails PR is rails/sprockets-rails#375

@schneems schneems referenced this pull request Aug 19, 2016

Merged

Deprecate asset fallback #375

@alexcameron89

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_tag_helper.rb Outdated
@alexcameron89

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@matthewd

This comment has been minimized.

Show comment
Hide comment
@matthewd

matthewd Aug 20, 2016

Member

This feels too onerous to me. Particularly, having the asset pipeline hijack image_tag seems out of step with how other similar tag helpers behave.

Member

matthewd commented Aug 20, 2016

This feels too onerous to me. Particularly, having the asset pipeline hijack image_tag seems out of step with how other similar tag helpers behave.

@schneems

This comment has been minimized.

Show comment
Hide comment
@schneems

schneems Aug 20, 2016

Member

@md not sure I follow. Asset pipeline already hijacks these helpers. This patch provides an un-hijack.

Member

schneems commented Aug 20, 2016

@md not sure I follow. Asset pipeline already hijacks these helpers. This patch provides an un-hijack.

@robin850

This comment has been minimized.

Show comment
Hide comment
@robin850

robin850 Aug 20, 2016

Member

👍 on deprecating but can't we just add the :public_folder option to the existing helpers and output the warning if none of :public_folder or :raw is passed ? Thus we don't have to maintain more methods and more documentation for two things that are mostly the same.

Member

robin850 commented Aug 20, 2016

👍 on deprecating but can't we just add the :public_folder option to the existing helpers and output the warning if none of :public_folder or :raw is passed ? Thus we don't have to maintain more methods and more documentation for two things that are mostly the same.

@matthewd

This comment has been minimized.

Show comment
Hide comment
@matthewd

matthewd Aug 21, 2016

Member

@schneems the distinction I'm making is that it currently enhances the helper, but doesn't interfere with non-asset use.

This also creates a cascade problem, where because image_path becomes public_image_path, image_tag must become public_image_tag, and so must some 3rd party helper that calls image_tag, etc.

I propose that we add public_image_path, and deprecate calling image_path with a relative filename that is not a known asset... and that's all.

So, anyone using image_tag with an asset, or a full image URL, or /some/thing.png, is unaffected -- all of those strings mean exactly one thing, and there's no potential for misinterpretation. Anyone currently using image_tag('my/image.png') to refer to something in public/images/my/image.png will be warned to change that to: image_tag(public_image_path('my/image.png')) -- I feel no obligation to offer a public_image_tag, because that formulation is relatively rare. And so the 3rd party helper will also be unaffected: it takes a string in, and the application-level caller introduces the public_image_path helper if they're doing the unusual thing.

Member

matthewd commented Aug 21, 2016

@schneems the distinction I'm making is that it currently enhances the helper, but doesn't interfere with non-asset use.

This also creates a cascade problem, where because image_path becomes public_image_path, image_tag must become public_image_tag, and so must some 3rd party helper that calls image_tag, etc.

I propose that we add public_image_path, and deprecate calling image_path with a relative filename that is not a known asset... and that's all.

So, anyone using image_tag with an asset, or a full image URL, or /some/thing.png, is unaffected -- all of those strings mean exactly one thing, and there's no potential for misinterpretation. Anyone currently using image_tag('my/image.png') to refer to something in public/images/my/image.png will be warned to change that to: image_tag(public_image_path('my/image.png')) -- I feel no obligation to offer a public_image_tag, because that formulation is relatively rare. And so the 3rd party helper will also be unaffected: it takes a string in, and the application-level caller introduces the public_image_path helper if they're doing the unusual thing.

@schneems

This comment has been minimized.

Show comment
Hide comment
@schneems

schneems Aug 21, 2016

Member

I never liked the idea of introducing so many new helpers, method lookup time isn't free and more methods can mean slower performance over time. I do like the idea of just exposing the public_folder: true API. It's basically almost already implemented by this patch, and makes intent clear.

The code in rails needs to enable people to run the same app they do today without deprecations. It also needs to be written in such a way that we can detect correctly when we should be deprecating (and eventually raising). I'm not in love with the idea of something like image_tag(public_image_path('my/image.png')), it looks a bit gnarly and the only way I can see to detect when it's not being used correctly is to have public_image_path return a special object instead of a string.

I'm going to explore modifying this patch to get rid of the public_* helpers, which I think everyone agrees are not the best idea.

Side Notes

I wanted to note that I don't like the API of passing in an asset that starts with a slash as indicating a full path. I deprecated that. I think it's too easy to accidentally start an asset with a slash and not realize that you're bypassing the whole asset pipeline, silently. At the end of the day I want an error to show up in development if someone uses one of these helpers with anything that won't resolve to an asset that exists and render in production.

I also wanted to note a special case in asset_path for the full url option: When you call image_tag with something like image_tag("https://www.schneems.com/smile.png") the call will eventually call asset_path before it calls compute_asset_path which is the main entry point to the asset pipeline. Before that can happen

def asset_path(source, options = {})
  raise ArgumentError, "nil is not a valid asset source" if source.nil?

  source = source.to_s
  return "" if source.blank?
  return source if URI_REGEXP.match?(source) # <====== HERE

This last line will return true since we're giving it a full URI so the asset lookup never happens, no deprecation is thrown. So we don't need to handle that case for skipping deprecations.

Thanks for all then input. Let me know if you need more clarification on any of this, or want to respond to any of the sub-points.

Member

schneems commented Aug 21, 2016

I never liked the idea of introducing so many new helpers, method lookup time isn't free and more methods can mean slower performance over time. I do like the idea of just exposing the public_folder: true API. It's basically almost already implemented by this patch, and makes intent clear.

The code in rails needs to enable people to run the same app they do today without deprecations. It also needs to be written in such a way that we can detect correctly when we should be deprecating (and eventually raising). I'm not in love with the idea of something like image_tag(public_image_path('my/image.png')), it looks a bit gnarly and the only way I can see to detect when it's not being used correctly is to have public_image_path return a special object instead of a string.

I'm going to explore modifying this patch to get rid of the public_* helpers, which I think everyone agrees are not the best idea.

Side Notes

I wanted to note that I don't like the API of passing in an asset that starts with a slash as indicating a full path. I deprecated that. I think it's too easy to accidentally start an asset with a slash and not realize that you're bypassing the whole asset pipeline, silently. At the end of the day I want an error to show up in development if someone uses one of these helpers with anything that won't resolve to an asset that exists and render in production.

I also wanted to note a special case in asset_path for the full url option: When you call image_tag with something like image_tag("https://www.schneems.com/smile.png") the call will eventually call asset_path before it calls compute_asset_path which is the main entry point to the asset pipeline. Before that can happen

def asset_path(source, options = {})
  raise ArgumentError, "nil is not a valid asset source" if source.nil?

  source = source.to_s
  return "" if source.blank?
  return source if URI_REGEXP.match?(source) # <====== HERE

This last line will return true since we're giving it a full URI so the asset lookup never happens, no deprecation is thrown. So we don't need to handle that case for skipping deprecations.

Thanks for all then input. Let me know if you need more clarification on any of this, or want to respond to any of the sub-points.

@matthewd

This comment has been minimized.

Show comment
Hide comment
@matthewd

matthewd Aug 21, 2016

Member

I wanted to note that I don't like the API of passing in an asset that starts with a slash as indicating a full path. I deprecated that.

IMO it's no more acceptable for image_tag('/a/url') to start insisting on assets than it would be for link_to('/a/url') to start complaining about the argument not matching a route: they're both direct convenience wrappers for their corresponding HTML tags, and it's only a secondary / additional convenience that they can intuit a "better" bonus behaviour for some arguments.

I think it's too easy to accidentally start an asset with a slash and not realize that you're bypassing the whole asset pipeline, silently.

It's not supposed to be; that's why we turned on digests in development. If you use /assets/my/image.png, that's already supposed to complain & fail. And if you use /my/image.png, it won't complain, but it also won't ever work -- and I could likely be convinced that should raise if my/image.png is a valid asset.

The only other possibility is that someone manually determines and then uses /assets/my/image-012345etc.png. That's going to go pretty poorly, but feels like a reasonable limit-point to me... the gun is braced to point horizontally and down-range: if you still manage to get your foot in front of it, good luck to you.

(Which of those three is the one that worries you? The first certainly used to be a big problem, but that's why we fixed it... or at least I thought we did.)


I'm not in love with the idea of something like image_tag(public_image_path('my/image.png')), it looks a bit gnarly

My first thought was to just kill the /images magic completely... given that the asset pipeline has been the "right" path for a long time now, I'm dubious that it sees much use. And people that are using it could just switch to /images/my/image.png -- the magic of the asset pipeline seems worthwhile for what it gives you... inserting 8 characters feels a bit more opaque-for-the-sake-of-it.

With that context, the above doesn't trouble me overmuch... the two methods are at very different conceptual levels, just like link_to(new_post_comment_path(@post)). Nice API is nice, but we're not here to prevent people from ever having to call more than one method.

and the only way I can see to detect when it's not being used correctly is to have public_image_path return a special object instead of a string.

It'd be returning an absolute URL /images/my/image.png, whereas I only want to complain about a bare my/image.png.

This last line will return true since we're giving it a full URI so the asset lookup never happens, no deprecation is thrown.

👍

Member

matthewd commented Aug 21, 2016

I wanted to note that I don't like the API of passing in an asset that starts with a slash as indicating a full path. I deprecated that.

IMO it's no more acceptable for image_tag('/a/url') to start insisting on assets than it would be for link_to('/a/url') to start complaining about the argument not matching a route: they're both direct convenience wrappers for their corresponding HTML tags, and it's only a secondary / additional convenience that they can intuit a "better" bonus behaviour for some arguments.

I think it's too easy to accidentally start an asset with a slash and not realize that you're bypassing the whole asset pipeline, silently.

It's not supposed to be; that's why we turned on digests in development. If you use /assets/my/image.png, that's already supposed to complain & fail. And if you use /my/image.png, it won't complain, but it also won't ever work -- and I could likely be convinced that should raise if my/image.png is a valid asset.

The only other possibility is that someone manually determines and then uses /assets/my/image-012345etc.png. That's going to go pretty poorly, but feels like a reasonable limit-point to me... the gun is braced to point horizontally and down-range: if you still manage to get your foot in front of it, good luck to you.

(Which of those three is the one that worries you? The first certainly used to be a big problem, but that's why we fixed it... or at least I thought we did.)


I'm not in love with the idea of something like image_tag(public_image_path('my/image.png')), it looks a bit gnarly

My first thought was to just kill the /images magic completely... given that the asset pipeline has been the "right" path for a long time now, I'm dubious that it sees much use. And people that are using it could just switch to /images/my/image.png -- the magic of the asset pipeline seems worthwhile for what it gives you... inserting 8 characters feels a bit more opaque-for-the-sake-of-it.

With that context, the above doesn't trouble me overmuch... the two methods are at very different conceptual levels, just like link_to(new_post_comment_path(@post)). Nice API is nice, but we're not here to prevent people from ever having to call more than one method.

and the only way I can see to detect when it's not being used correctly is to have public_image_path return a special object instead of a string.

It'd be returning an absolute URL /images/my/image.png, whereas I only want to complain about a bare my/image.png.

This last line will return true since we're giving it a full URI so the asset lookup never happens, no deprecation is thrown.

👍

@schneems

This comment has been minimized.

Show comment
Hide comment
@schneems

schneems Aug 22, 2016

Member

I updated to use public_folder: true as the API and got rid of the public_* methods. Need to update sprockets-rails patch.

Member

schneems commented Aug 22, 2016

I updated to use public_folder: true as the API and got rid of the public_* methods. Need to update sprockets-rails patch.

@schneems

This comment has been minimized.

Show comment
Hide comment
@schneems

schneems Aug 22, 2016

Member

Updated sprockets-rails patch, was pretty minimal.

Member

schneems commented Aug 22, 2016

Updated sprockets-rails patch, was pretty minimal.

@matthewd

View changes

Show outdated Hide outdated ...ib/rails/generators/rails/app/templates/config/initializers/assets.rb.tt Outdated
@prathamesh-sonpatki

View changes

Show outdated Hide outdated guides/source/asset_pipeline.md Outdated
@schneems

This comment has been minimized.

Show comment
Hide comment
@schneems

schneems Aug 26, 2016

Member

Updated to Rails.application.config.assets.unknown_asset_fallback = <%= options[:update] ? true : false %> and mentioned the specific version of sprockets-rails in the assets guide,

Member

schneems commented Aug 26, 2016

Updated to Rails.application.config.assets.unknown_asset_fallback = <%= options[:update] ? true : false %> and mentioned the specific version of sprockets-rails in the assets guide,

@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated actionview/lib/action_view/helpers/asset_url_helper.rb Outdated
@robin850

View changes

Show outdated Hide outdated guides/source/asset_pipeline.md Outdated
@robin850

View changes

Show outdated Hide outdated guides/source/configuring.md Outdated
@robin850

This comment has been minimized.

Show comment
Hide comment
@robin850

robin850 Aug 26, 2016

Member

This is looking very good ! 👍

Just a nit-pick again regarding documentation though 😄 it looks like you didn't document the :public_poster_folder option for the video_tag method, this may be useful.

Member

robin850 commented Aug 26, 2016

This is looking very good ! 👍

Just a nit-pick again regarding documentation though 😄 it looks like you didn't document the :public_poster_folder option for the video_tag method, this may be useful.

@vipulnsward

View changes

Show outdated Hide outdated guides/source/asset_pipeline.md Outdated
@vipulnsward

View changes

Show outdated Hide outdated guides/source/configuring.md Outdated
@vipulnsward

This comment has been minimized.

Show comment
Hide comment
@vipulnsward

vipulnsward Aug 29, 2016

Member

@schneems is there a way to test the request does send in requested public resource, I see the test cases for debug output that check something does not exist.

Member

vipulnsward commented Aug 29, 2016

@schneems is there a way to test the request does send in requested public resource, I see the test cases for debug output that check something does not exist.

# - An extension name can be specified manually with <tt>extname</tt>.
#
# asset_path("foo", skip_pipeline: true, extname: ".js") # => "/foo.js"
# asset_path("foo.css", skip_pipeline: true, extname: ".js") # => "/foo.css.js"

This comment has been minimized.

@kaspth

kaspth Aug 30, 2016

Member

Should these last 3 examples use skip_pipeline?

@kaspth

kaspth Aug 30, 2016

Member

Should these last 3 examples use skip_pipeline?

This comment has been minimized.

@schneems

schneems Aug 30, 2016

Member

Makes the example more clear IMHO

@schneems

schneems Aug 30, 2016

Member

Makes the example more clear IMHO

This comment has been minimized.

@kaspth

kaspth Aug 30, 2016

Member

Yet I was confused by the usage 😄

I'm not objecting to using the option necessarily, but why does using it make the examples more clear?

@kaspth

kaspth Aug 30, 2016

Member

Yet I was confused by the usage 😄

I'm not objecting to using the option necessarily, but why does using it make the examples more clear?

This comment has been minimized.

@schneems

schneems Aug 30, 2016

Member

The assetpipeline does a bunch of things and I don't want asset pipeline magic being confused with this option. Adding a long fingerprint to the example makes it less clear that an extension was added to the output. What it doesn't do is replace an extension name, so foo.css does not become foo.js. Also technically this option doesn't change the extension output, it changes the input that goes into the asset pipeline. It's harder to show.

@schneems

schneems Aug 30, 2016

Member

The assetpipeline does a bunch of things and I don't want asset pipeline magic being confused with this option. Adding a long fingerprint to the example makes it less clear that an extension was added to the output. What it doesn't do is replace an extension name, so foo.css does not become foo.js. Also technically this option doesn't change the extension output, it changes the input that goes into the asset pipeline. It's harder to show.

This comment has been minimized.

@kaspth

kaspth Aug 30, 2016

Member

Okay, sounds good 👍

@kaspth

kaspth Aug 30, 2016

Member

Okay, sounds good 👍

@@ -282,8 +285,11 @@ def image_alt(src)
# video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
# # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
def video_tag(*sources)

This comment has been minimized.

@kaspth

kaspth Aug 30, 2016

Member

We could remove the options wrangling with a keyword argument:

def video_tag(*sources, skip_pipeline_on_poster: nil)
  multiple_sources_tag_builder("video", sources) do |options|
    options[:poster] = path_to_image(options[:poster], skip_pipeline: skip_pipeline_on_poster) if options[:poster]
    options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
  end
end

I think that would also save us a block argument shadowing local variable warning from options. 😁

@kaspth

kaspth Aug 30, 2016

Member

We could remove the options wrangling with a keyword argument:

def video_tag(*sources, skip_pipeline_on_poster: nil)
  multiple_sources_tag_builder("video", sources) do |options|
    options[:poster] = path_to_image(options[:poster], skip_pipeline: skip_pipeline_on_poster) if options[:poster]
    options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
  end
end

I think that would also save us a block argument shadowing local variable warning from options. 😁

This comment has been minimized.

@schneems

schneems Aug 30, 2016

Member

It's turtles all the way down, other method signatures that this gets passed to use extract_options.

Better to do that in a separate PR.

@schneems

schneems Aug 30, 2016

Member

It's turtles all the way down, other method signatures that this gets passed to use extract_options.

Better to do that in a separate PR.

This comment has been minimized.

@schneems

schneems Aug 30, 2016

Member

Oh, i see what you're saying. I still want to do that in a different PR.

@schneems

schneems Aug 30, 2016

Member

Oh, i see what you're saying. I still want to do that in a different PR.

This comment has been minimized.

@kaspth

kaspth Aug 30, 2016

Member

I'm not generally objecting to extract_options!, I agree that's out of scope. I'm objecting to using it in video_tag because the diff says it didn't use it before. But messing around in IRB showed me that we can't mix keyword arguments with a symbol keyed options hash without using **options, so that doesn't save us much here anyway. 👍 to keeping it like you had it 😁

@kaspth

kaspth Aug 30, 2016

Member

I'm not generally objecting to extract_options!, I agree that's out of scope. I'm objecting to using it in video_tag because the diff says it didn't use it before. But messing around in IRB showed me that we can't mix keyword arguments with a symbol keyed options hash without using **options, so that doesn't save us much here anyway. 👍 to keeping it like you had it 😁

@schneems schneems merged commit 4ba40bc into rails:master Aug 31, 2016

2 checks passed

codeclimate Code Climate has skipped analysis of this commit.
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

claudiob added a commit to claudiob/rails that referenced this pull request Mar 14, 2017

schneems added a commit that referenced this pull request Mar 14, 2017

@endverbraucher endverbraucher referenced this pull request Jan 24, 2018

Merged

Upgrade rails #123

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment