Permalink
Browse files

Deprecate the `only_path` option on `*_path` helpers.

In cases where this option is set to `true`, the option is redundant and can
be safely removed; otherwise, the corresponding `*_url` helper should be
used instead.

Fixes #17294.

See also #17363.

[Dan Olson, Godfrey Chan]
  • Loading branch information...
chancancode committed Oct 28, 2014
1 parent 8607577 commit aa1fadd48fb40dd9396a383696134a259aa59db9
View
@@ -1,3 +1,13 @@
+* Deprecate the `only_path` option on `*_path` helpers.
+
+ In cases where this option is set to `true`, the option is redundant and can
+ be safely removed; otherwise, the corresponding `*_url` helper should be
+ used instead.
+
+ Fixes #17294.
+
+ *Dan Olson*, *Godfrey Chan*
+
* Improve Journey compliance to RFC 3986.
The scanner in Journey failed to recognize routes that use literals
@@ -134,8 +134,8 @@ def add(name, route)
@url_helpers_module.send :undef_method, url_name
end
routes[key] = route
- define_url_helper @path_helpers_module, route, path_name, route.defaults, name, PATH
- define_url_helper @url_helpers_module, route, url_name, route.defaults, name, FULL
+ define_url_helper @path_helpers_module, route, path_name, route.defaults, name, LEGACY
+ define_url_helper @url_helpers_module, route, url_name, route.defaults, name, UNKNOWN
@path_helpers << path_name
@url_helpers << url_name
@@ -322,6 +322,30 @@ def define_url_helper(mod, route, name, opts, route_key, url_strategy)
PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
FULL = ->(options) { ActionDispatch::Http::URL.full_url_for(options) }
UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
+ LEGACY = ->(options) {
+ if options.key?(:only_path)
+ if options[:only_path]
+ ActiveSupport::Deprecation.warn \
+ "You are calling a `*_path` helper with the `only_path` option " \
+ "expicitly set to `true`. This option will stop working on " \
+ "path helpers in Rails 5. Simply remove the `only_path: true` " \
+ "argument from your call as it is redundant when applied to a " \
+ "path helper."
+
+ PATH.call(options)
+ else
+ ActiveSupport::Deprecation.warn \
+ "You are calling a `*_path` helper with the `only_path` option " \
+ "expicitly set to `false`. This option will stop working on " \

This comment has been minimized.

Show comment
Hide comment
@carlosantoniodasilva

carlosantoniodasilva Oct 28, 2014

Member

I think you meant explicitly (both branches)

@carlosantoniodasilva

carlosantoniodasilva Oct 28, 2014

Member

I think you meant explicitly (both branches)

This comment has been minimized.

Show comment
Hide comment
@fxn

fxn Oct 28, 2014

Member

Yeah, and regarding style I don't find this to be very idiomatic, I would personally prefer a heredoc with a strip_heredoc call as we do elsewhere.

@fxn

fxn Oct 28, 2014

Member

Yeah, and regarding style I don't find this to be very idiomatic, I would personally prefer a heredoc with a strip_heredoc call as we do elsewhere.

This comment has been minimized.

Show comment
Hide comment
@chancancode

chancancode Oct 28, 2014

Member

Typo, will fix 👍

@fxn regarding the deprecation messages, I think that was discussed somewhere recently. I'll try to find it. The main reason for using this style is that heredoc encodes the line wrapping as part of the string, which results in some awkward output if they user's terminal is not exactly 80 characters wide. Since the deprecation messages don't have a long life span we figured we would just put up with this (and be careful not to miss a \ on one of those lines. Is there a better alternative?

@chancancode

chancancode Oct 28, 2014

Member

Typo, will fix 👍

@fxn regarding the deprecation messages, I think that was discussed somewhere recently. I'll try to find it. The main reason for using this style is that heredoc encodes the line wrapping as part of the string, which results in some awkward output if they user's terminal is not exactly 80 characters wide. Since the deprecation messages don't have a long life span we figured we would just put up with this (and be careful not to miss a \ on one of those lines. Is there a better alternative?

This comment has been minimized.

Show comment
Hide comment
@fxn

fxn Oct 28, 2014

Member

@chancancode yeah, the heredoc encodes the newlines, and strip_heredoc was thought to be able to write indented heredocs without the indentation in the final string.

There's then a rule to keep deprecation messages in one single line?

@fxn

fxn Oct 28, 2014

Member

@chancancode yeah, the heredoc encodes the newlines, and strip_heredoc was thought to be able to write indented heredocs without the indentation in the final string.

There's then a rule to keep deprecation messages in one single line?

This comment has been minimized.

Show comment
Hide comment
@chancancode

chancancode Oct 28, 2014

Member

I think it was 8c52480 and some campfire discussion.. should have looped you in 😄

I guess it's more of a recent "trend", because our deprecation messages got more detailed over time, often with code snippets that suggest migration paths to the user (which is great, I think)... so it becomes more desirable to have control over when a newline is inserted and when it's not (e.g.

"Ambiguous source reflection for through association. Please " \
"specify a :source directive on your declaration like:\n" \
"\n" \
" class #{active_record.name} < ActiveRecord::Base\n" \
" #{macro} :#{name}, #{example_options}\n" \
" end"
), and the console width problem becomes more prominent with these long messages.

If that doesn't make sense we can definitely revisit this though!

@chancancode

chancancode Oct 28, 2014

Member

I think it was 8c52480 and some campfire discussion.. should have looped you in 😄

I guess it's more of a recent "trend", because our deprecation messages got more detailed over time, often with code snippets that suggest migration paths to the user (which is great, I think)... so it becomes more desirable to have control over when a newline is inserted and when it's not (e.g.

"Ambiguous source reflection for through association. Please " \
"specify a :source directive on your declaration like:\n" \
"\n" \
" class #{active_record.name} < ActiveRecord::Base\n" \
" #{macro} :#{name}, #{example_options}\n" \
" end"
), and the console width problem becomes more prominent with these long messages.

If that doesn't make sense we can definitely revisit this though!

This comment has been minimized.

Show comment
Hide comment
@chancancode

chancancode Oct 28, 2014

Member

Another example where the indentation and line breaks are important:

CALLBACK_WARN_MESSAGE = "Currently, Active Record suppresses errors raised " \
"within `after_rollback`/`after_commit` callbacks and only print them to " \
"the logs. In the next version, these errors will no longer be suppressed. " \
"Instead, the errors will propagate normally just like in other Active " \
"Record callbacks.\n" \
"\n" \
"You can opt into the new behavior and remove this warning by setting:\n" \
"\n" \
" config.active_record.raise_in_transactional_callbacks = true"

It looks pretty gross, but fortunately we can remove it as soon as we branch.

@chancancode

chancancode Oct 28, 2014

Member

Another example where the indentation and line breaks are important:

CALLBACK_WARN_MESSAGE = "Currently, Active Record suppresses errors raised " \
"within `after_rollback`/`after_commit` callbacks and only print them to " \
"the logs. In the next version, these errors will no longer be suppressed. " \
"Instead, the errors will propagate normally just like in other Active " \
"Record callbacks.\n" \
"\n" \
"You can opt into the new behavior and remove this warning by setting:\n" \
"\n" \
" config.active_record.raise_in_transactional_callbacks = true"

It looks pretty gross, but fortunately we can remove it as soon as we branch.

This comment has been minimized.

Show comment
Hide comment
@fxn

fxn Oct 28, 2014

Member

All those literals with quotes and "\n"s and backslashes are a smell to me. Let's dig a little bit into it to see what can be done.

There are two different topics:

  1. Warn messages should be in one line no matter how long they are?
  2. Given an answer, which code style could we use that keeps the code clean?

In general, I would understand to have one single line if the purpose is to be log-friendly. Even if the line is as long as some we have (for example this one). That's a trade-off, it is not very readable, but it takes one line in the log.

If that was the consensus, we could use squish. See,

ActiveSupport::Deprecation.warn \
  "The association scope '#{name}' is instance dependent (the scope " \
  "block takes an argument). Preloading happens before the individual " \
  "instances are created. This means that there is no instance being " \
  "passed to the association scope. This will most likely result in " \
  "broken or incorrect behavior. Joining, Preloading and eager loading " \
  "of these associations is deprecated and will be removed in the future."

compared to

ActiveSupport::Deprecation.warn(<<-WARNING.squish)
  The association scope '#{name}' is instance dependent (the scope
  block takes an argument). Preloading happens before the individual
  instances are created. This means that there is no instance being
  passed to the association scope. This will most likely result in
  broken or incorrect behavior. Joining, Preloading and eager loading
  of these associations is deprecated and will be removed in the future.
WARNING

If the trade-off is not necessary, then strip_heredoc preserves relative indentation. That is, it computes the min indent and shifts everything that amount to the left. In particular blank lines and code blocks are preserved (relative to the indented shift).

I would prefer warnings to be optimized for people to read, rather than those long lines, but it's not big deal for me if in general one lines are preferred, only the source code could be prettier.

Indeed warn could enforce squish unless verbatim is true or something, not to push this global guideline to all client code.

If we like any of these ideas I would make a pass and unify warnings in one shot. What do you think?

@fxn

fxn Oct 28, 2014

Member

All those literals with quotes and "\n"s and backslashes are a smell to me. Let's dig a little bit into it to see what can be done.

There are two different topics:

  1. Warn messages should be in one line no matter how long they are?
  2. Given an answer, which code style could we use that keeps the code clean?

In general, I would understand to have one single line if the purpose is to be log-friendly. Even if the line is as long as some we have (for example this one). That's a trade-off, it is not very readable, but it takes one line in the log.

If that was the consensus, we could use squish. See,

ActiveSupport::Deprecation.warn \
  "The association scope '#{name}' is instance dependent (the scope " \
  "block takes an argument). Preloading happens before the individual " \
  "instances are created. This means that there is no instance being " \
  "passed to the association scope. This will most likely result in " \
  "broken or incorrect behavior. Joining, Preloading and eager loading " \
  "of these associations is deprecated and will be removed in the future."

compared to

ActiveSupport::Deprecation.warn(<<-WARNING.squish)
  The association scope '#{name}' is instance dependent (the scope
  block takes an argument). Preloading happens before the individual
  instances are created. This means that there is no instance being
  passed to the association scope. This will most likely result in
  broken or incorrect behavior. Joining, Preloading and eager loading
  of these associations is deprecated and will be removed in the future.
WARNING

If the trade-off is not necessary, then strip_heredoc preserves relative indentation. That is, it computes the min indent and shifts everything that amount to the left. In particular blank lines and code blocks are preserved (relative to the indented shift).

I would prefer warnings to be optimized for people to read, rather than those long lines, but it's not big deal for me if in general one lines are preferred, only the source code could be prettier.

Indeed warn could enforce squish unless verbatim is true or something, not to push this global guideline to all client code.

If we like any of these ideas I would make a pass and unify warnings in one shot. What do you think?

This comment has been minimized.

Show comment
Hide comment
@chancancode

chancancode Oct 28, 2014

Member

@fxn I might have been unclear, but the main motivation to improve (human) readability by allowing the user's console to wrap the lines according to its width, rather than enforcing a 80-character wrap in the code.

In this case, (I believe) the new lines and indentation is supposed to draw attention to the migration path (the config to make the warning go away)... and make that easily copy-and-pastable. Would squish take that away? (Or do we care in the end?)

@chancancode

chancancode Oct 28, 2014

Member

@fxn I might have been unclear, but the main motivation to improve (human) readability by allowing the user's console to wrap the lines according to its width, rather than enforcing a 80-character wrap in the code.

In this case, (I believe) the new lines and indentation is supposed to draw attention to the migration path (the config to make the warning go away)... and make that easily copy-and-pastable. Would squish take that away? (Or do we care in the end?)

This comment has been minimized.

Show comment
Hide comment
@fxn

fxn Oct 28, 2014

Member

That's a strange optimization to me, if your console width is 50 columns maybe the line breaks are cumbersome, but with the current long lines if your console is wide (I have one with 171 columns right now over here) then the message is as wide.

It doesn't matter though, if that was discussed I am fine with it. In that case I'd go with squish in warn surely, with a flag that allows you to opt-out.

@fxn

fxn Oct 28, 2014

Member

That's a strange optimization to me, if your console width is 50 columns maybe the line breaks are cumbersome, but with the current long lines if your console is wide (I have one with 171 columns right now over here) then the message is as wide.

It doesn't matter though, if that was discussed I am fine with it. In that case I'd go with squish in warn surely, with a flag that allows you to opt-out.

This comment has been minimized.

Show comment
Hide comment
@fxn

fxn Oct 28, 2014

Member

Ah, regarding that particular case I didn't answer, yeah that would be a use case to opt-out. I think it makes sense to display that config in a code block.

@fxn

fxn Oct 28, 2014

Member

Ah, regarding that particular case I didn't answer, yeah that would be a use case to opt-out. I think it makes sense to display that config in a code block.

+ "path helpers in Rails 5. Use the corresponding `*_url` helper " \
+ "instead."
+
+ FULL.call(options)
+ end
+ else
+ PATH.call(options)
+ end
+ }
# :startdoc:
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
@@ -69,6 +69,86 @@ def call(env)
end
end
+ test "only_path: true with *_url and no :host option" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_url(only_path: true)
+ end
+
+ test "only_path: false with *_url and no :host option" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_raises ArgumentError do
+ assert_equal 'http://example.com/foo', url_helpers.foo_url(only_path: false)
+ end
+ end
+
+ test "only_path: false with *_url and local :host option" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_equal 'http://example.com/foo', url_helpers.foo_url(only_path: false, host: 'example.com')
+ end
+
+ test "only_path: false with *_url and global :host option" do
+ @set.default_url_options = { host: 'example.com' }
+
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_equal 'http://example.com/foo', url_helpers.foo_url(only_path: false)
+ end
+
+ test "only_path: true with *_path" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_deprecated do
+ assert_equal '/foo', url_helpers.foo_path(only_path: true)
+ end
+ end
+
+ test "only_path: false with *_path with global :host option" do
+ @set.default_url_options = { host: 'example.com' }
+
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_deprecated do
+ assert_equal 'http://example.com/foo', url_helpers.foo_path(only_path: false)
+ end
+ end
+
+ test "only_path: false with *_path with local :host option" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_deprecated do
+ assert_equal 'http://example.com/foo', url_helpers.foo_path(only_path: false, host: 'example.com')
+ end
+ end
+
+ test "only_path: false with *_path with no :host option" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_deprecated do
+ assert_raises ArgumentError do
+ assert_equal 'http://example.com/foo', url_helpers.foo_path(only_path: false)
+ end
+ end
+ end
+
test "explicit keys win over implicit keys" do
draw do
resources :foo do

3 comments on commit aa1fadd

@chancancode

This comment has been minimized.

Show comment
Hide comment
@fxn

This comment has been minimized.

Show comment
Hide comment
@fxn

fxn Oct 28, 2014

Member

👍 I think this API makes sense.

Member

fxn replied Oct 28, 2014

👍 I think this API makes sense.

@demonodojo

This comment has been minimized.

Show comment
Hide comment
@demonodojo

demonodojo Jun 16, 2016

I have problems testing decorators with this deprecation. I'm not in a controller, so I get the deprecation warning. How can I mock this to avoid deprecation warning?

I have problems testing decorators with this deprecation. I'm not in a controller, so I get the deprecation warning. How can I mock this to avoid deprecation warning?

Please sign in to comment.