Commit Graph

60 Commits

Author SHA1 Message Date
Adam Hess
98ed2406f7 cause rails to correctly place optional path parameters
previously, if you specify a url parameter that is part of the path as false it would include that part of the path as parameter at the end of the url instead of in the path for example:
`get "(/optional/:optional_id)/things" => "foo#foo", as: :things`
`things_path(optional_id: false)  # => /things?optional_id=false`

this is not the case for empty string,
`things_path(optional_id: '')  # => "/things"

this is due to a quark in how path parameters get removed from the parameters.

we where doing `(paramter || recall).nil?` which returns nil if both values are nil however it also return nil if one value is false and the other value is nil. i.e. `(false || nil).nil # => nil` which is confusing.

After this change, `true` and `false` will be treated the same when used as optional path parameters. meaning now,

```
get '(this/:my_bool)/that' as: :that

that_path(my_bool: true) # => `/this/true/that`
that_path(my_bool: false) # => `/this/false/that`
```
fixes: https://github.com/rails/rails/issues/42280

Co-authored-by: Ryuta Kamizono <kamipo@gmail.com>
2021-05-27 18:21:01 -07:00
eileencodes
437ab2031e
Convert route params array into object
This PR converts the route params array into an object and moves the
error generation code into its own object as well. This is a refactoring
of internal code to make it easier to change and simplier for internals
to interface with. We have two new objects:

1) `RouteWithParams`

Previously `#generate` returned an array of parameterized parts for a
route. We have now turned this into an object with a `path` and a
`params methods` to be able to access the parts we need.

2) `MissingRoute`

`#generate` was also responsible for constructing the message for the
error. We've moved this code into the new `MissingRoute` object so it
does that work instead.

This change makes these methods reusable throughout the code and easier
for future refactoring we plan to do.

Co-authored-by: Aaron Patterson <aaron.patterson@gmail.com>
2020-06-05 13:18:49 -04:00
Aaron Patterson
7df20f408c
get the method name all the way to the exception 2020-05-11 15:19:26 -07:00
Aaron Patterson
aea233e9f2
Add did you mean ssupport to UrlGeneration errors
This commit is a PoC for adding did you mean support to Url Generation
errors.  I think we can do much better than this, but I wanted to try
something out as a first pass.
2020-05-11 15:17:15 -07:00
Aaron Patterson
26f5325756
Clean up the generate method signature
Journey is a private API inside Rails now, so lets start cleaning up
some of its signatures.  In this case, we always wan to parameterize
things the same way, so lets just do that in the code (rather than
passing some strategy object in).
2020-05-11 14:15:26 -07:00
Adam Hegyi
e0eff2c3dd Memoize regex when checking missing route keys
When the route definition has parameters, we can supply a regex for
validation purposes:

    get "/a/:b" => "test#index", constraints: { b: /abc/ }, as: test

This regex is going to be used to check the supplied values during
link generation:

    test_path("abc") # check "abc" against /abc/ regex

The link generation code checks each parameter. To properly validate the
parameter, it creates a new regex with start and end of string modifiers:

    /\A#{original_regex}\Z/

This means for each link generation the code stringifies the existing
regex and creates a new one. When a new regex is created, it needs to be
compiled, for large regexes this can take quite a bit of time.

This change memoizes the generated regex for each route when constrains
are given. It also removes the RegexCaseComparator class since it is not
in use anymore.
2020-01-08 08:36:29 +01:00
Rafael Mendonça França
e67fdc5aeb
Revert "Merge pull request #37504 from utilum/no_implicit_conversion_of_nil"
This reverts commit 4e105385d046e2aeab16955943df97c5eefa3a6f, reversing
changes made to 62b43839098bbbbfc4be789128d33dc0612f1ab3.

The change in Ruby that made those changes required was reverted in
8852fa8760
2019-12-09 11:50:39 -03:00
utilum
2ca6830831 TypeError Regexp#match?(nil) in Ruby Head
Aa of ruby/ruby@2a22a6b2d8 calling
`Regexp#match?(nil)` raises an exception.

[utilum, eregon, eugeneius]
2019-11-03 09:26:46 +02:00
Akira Matsuda
4a9ef5e120 Use Regext#match? where MatchData is not needed 2019-09-17 07:59:04 +09:00
Ryuta Kamizono
c81af6ae72 Enable Layout/EmptyLinesAroundAccessModifier cop
We sometimes say "✂️ newline after `private`" in a code review (e.g.
https://github.com/rails/rails/pull/18546#discussion_r23188776,
https://github.com/rails/rails/pull/34832#discussion_r244847195).

Now `Layout/EmptyLinesAroundAccessModifier` cop have new enforced style
`EnforcedStyle: only_before` (https://github.com/rubocop-hq/rubocop/pull/7059).

That cop and enforced style will reduce the our code review cost.
2019-06-13 12:00:45 +09:00
Alberto Almagro
fb9117e190 Keep part when scope option has value
When a route was defined within an optional scope, if that route didn't
take parameters the scope was lost when using path helpers. This patch
ensures scope is kept both when the route takes parameters or when it
doesn't.

Fixes #33219
2019-05-22 23:03:54 +02:00
yuuji.yaginuma
1b86d90136 Enable Performance/UnfreezeString cop
In Ruby 2.3 or later, `String#+@` is available and `+@` is faster than `dup`.

```ruby
# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "benchmark-ips"
end

Benchmark.ips do |x|
  x.report('+@') { +"" }
  x.report('dup') { "".dup }
  x.compare!
end
```

```
$ ruby -v benchmark.rb
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
Warming up --------------------------------------
                  +@   282.289k i/100ms
                 dup   187.638k i/100ms
Calculating -------------------------------------
                  +@      6.775M (± 3.6%) i/s -     33.875M in   5.006253s
                 dup      3.320M (± 2.2%) i/s -     16.700M in   5.032125s

Comparison:
                  +@:  6775299.3 i/s
                 dup:  3320400.7 i/s - 2.04x  slower

```
2018-09-23 08:56:55 +09:00
Koichi ITO
d2901bd517 [Action Pack] rubocop -a --only Layout/EmptyLineAfterMagicComment 2017-07-11 13:12:32 +09:00
Kir Shatrov
b3f3d49fd6 Prepare AP and AR to be frozen string friendly 2017-07-06 21:30:43 +03:00
Matthew Draper
87b3e226d6 Revert "Merge pull request #29540 from kirs/rubocop-frozen-string"
This reverts commit 3420a14590c0e6915d8b6c242887f74adb4120f9, reversing
changes made to afb66a5a598ce4ac74ad84b125a5abf046dcf5aa.
2017-07-02 02:15:17 +09:30
Kir Shatrov
cfade1ec7e Enforce frozen string in Rubocop 2017-07-01 02:11:03 +03:00
Hrvoje Šimić
1c6747999a
[docs] fix ActionDispatch documentation 2017-03-13 17:05:12 +01:00
Jon Moss
0713265fd1 Use next instead of break; avoid terminating whole loop
We want to avoid terminating the whole loop here, because it will cause
parameters that should be removed to not be removed, since we are
terminating early. In this specific case, `param2` is processed before
`param1` due to the reversing of `route.parts`, and since `param2` fails
the check on this line, it would previously cause the whole loop to
fail, and `param1` would still be in `parameterized_parts`. Now, we are
simply calling `next`, which is the intended behavior.

Introduced by 8ca8a2d773b942c4ea76baabe2df502a339d05b1.

Fixes #27454.
2016-12-29 10:40:47 -05:00
Ben Hughes
f1525dac11 Optimize Journey::Route#score
Scoring routes based on constraints repeated many type conversions that
could be performed in the outer loop.  Determinations of score and
fitness also used Array operations that required allocations.  Against
my benchmark with a large routeset, this reduced object allocations by
over 30x and wall time by over 3x.
2016-12-28 17:19:15 -08:00
Rafael Mendonça França
fe1f4b2ad5
Add more rubocop rules about whitespaces 2016-10-29 01:17:49 -02:00
Ryuta Kamizono
b56eaa2e15 Fix :stopdoc: to :startdoc: [ci skip] 2016-10-28 11:32:04 +09:00
Rafael Mendonça França
3b50fb6b2f
Remove all Journey constant from public API
There were never public API only there by mistake.

[ci skip]
2016-10-26 15:25:45 -02:00
Chris Carter
0b32e2dff3 Show an "unmatched constraints" error for mismatching and present params
Currently a misleading "missing required keys" error is thrown when a param
fails to match the constraints of a particular route. This commit ensures that
these params are recognised as unmatching rather than missing.

Note: this means that a different error message will be provided between
optimized and non-optimized path helpers, due to the fact that the former does
not check constraints when matching routes.

Fixes #26470.
2016-10-03 09:52:14 +01:00
Rafael Mendonça França
55f9b8129a
Add three new rubocop rules
Style/SpaceBeforeBlockBraces
Style/SpaceInsideBlockBraces
Style/SpaceInsideHashLiteralBraces

Fix all violations in the repository.
2016-08-16 04:30:11 -03:00
Xavier Noria
628e51ff10 applies new string literal convention in actionpack/lib
The current code base is not uniform. After some discussion,
we have chosen to go with double quotes by default.
2016-08-06 18:51:43 +02:00
Andrew White
8ca8a2d773 Refactor handling of :action default in routing
The longstanding convention in Rails is that if the :action parameter
is missing or nil then it defaults to 'index'. Up until Rails 5.0.0.beta1
this was handled slightly differently than other routing defaults by
deleting it from the route options and adding it to the recall parameters.

With the recent focus of removing unnecessary duplications this has
exposed a problem in this strategy - we are now mutating the request's
path parameters and causing problems for later url generation. This will
typically affect url_for rather a named url helper since the latter
explicitly pass :controller, :action, etc.

The fix is to add a default for :action in the route class if the path
contains an :action segment and no default is passed. This change also
revealed an issue with the parameterized part expiry in that it doesn't
follow a right to left order - as soon as a dynamic segment is required
then all other segments become required.

Fixes #23019.
2016-02-16 09:52:26 +00:00
eileencodes
ec14aad419 Fix route creation when format is a blank string
Commit bff61ba, while reducing allocations, caused a regression when an empty
format is passed to a route.

This can happen in cases where you're using an anchor tag, for example:
`https://example.com/parent/575256966.#child_1032289285`.

Because of this change `format` was getting sent in
`parameterized_parts` when previously it was not included. This resulted
in blank `format`'s being returned as `.` when if there was an extension
included it would be `.extension`. Since there was no extension this
caused incorrect URL's.

The test shows this would result in `/posts/show/1.` instead of
`/posts/show/1` which causes bad urls since the format is not present.
2015-09-02 09:18:46 -04:00
Aaron Patterson
a293812bff only keep one hash of named routes
The outer router object already keeps a hash of named routes, so we
should just use that.
2015-08-14 18:06:48 -07:00
Jean Boussier
32133db710 Array#any? is slower and not the inverse of Array#empty?
```
empty_array = []
small_array = [1] * 30
bigger_array = [1] * 300

Benchmark.ips do |x|
  x.report('empty !empty?') { !empty_array.empty? }
  x.report('small !empty?') { !small_array.empty? }
  x.report('bigger !empty?') { !bigger_array.empty? }

  x.report('empty any?') { empty_array.any? }
  x.report('small any?') { small_array.any? }
  x.report('bigger any?') { bigger_array.any? }
end
```

```
Calculating -------------------------------------
       empty !empty?   132.059k i/100ms
       small !empty?   133.974k i/100ms
      bigger !empty?   133.848k i/100ms
          empty any?   106.924k i/100ms
          small any?    85.525k i/100ms
         bigger any?    86.663k i/100ms
-------------------------------------------------
       empty !empty?      8.522M (± 7.9%) i/s -     42.391M
       small !empty?      8.501M (± 8.5%) i/s -     42.202M
      bigger !empty?      8.434M (± 8.6%) i/s -     41.894M
          empty any?      4.161M (± 8.3%) i/s -     20.743M
          small any?      2.654M (± 5.2%) i/s -     13.256M
         bigger any?      2.642M (± 6.4%) i/s -     13.173M
```

Ref: https://github.com/rails/rails/pull/21057#discussion_r35902468
2015-07-30 15:12:37 -04:00
schneems
22f5924014 Use delete_if instead of each; delete(key)
It is slightly faster:

```
Calculating -------------------------------------
        each; delete    35.166k i/100ms
           delete_if    36.416k i/100ms
-------------------------------------------------
        each; delete    478.026k (± 8.5%) i/s -      2.391M
           delete_if    485.123k (± 7.9%) i/s -      2.440M
```
2015-07-30 12:34:48 -05:00
schneems
61dae88254 Remove (another) array allocation
We don't always need an array when generating a url with the formatter. We can be lazy about allocating the `missing_keys` array. This saves us:

35,606 bytes and 889 objects per request
2015-07-30 12:31:05 -05:00
schneems
4d2ccc119c Remove array allocation
THe only reason we were allocating an array is to get the "missing_keys" variable in scope of the error message generator. Guess what? Arrays kinda take up a lot of memory, so by replacing that with a nil, we save:

35,303 bytes and 886 objects per request
2015-07-30 12:31:05 -05:00
schneems
bff61ba27c Avoid calling to_s on nil in journey/formatter
When `defaults[key]` in `generate` in the journey formatter is called, it often returns a `nil` when we call `to_s` on a nil, it allocates an empty string. We can skip this check when the default value is nil.

This change buys us 35,431 bytes of memory and 887 fewer objects per request.

Thanks to @matthewd for help with the readability
2015-07-30 12:30:47 -05:00
schneems
097ec6fb7c Speed up journey missing_keys
Most routes have a `route.path.requirements[key]` of `/[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/` yet every time this method is called a new regex is generated on the fly with `/\A#{DEFAULT_INPUT}\Z/`. OBJECT ALLOCATIONS BLERG!

This change uses a special module that implements `===` so it can be used in a case statement to pull out the default input. When this happens, we use a pre-generated regex.

This change buys us 1,643,465 bytes of memory and 7,990 fewer objects per request.
2015-07-29 20:41:57 -05:00
schneems
9b8258814e Speed up journey extract_parameterized_parts
Micro optimization: `reverse.drop_while` is slower than `reverse_each.drop_while`. This doesn't save any object allocations.

Second, `keys_to_keep` is typically a very small array. The operation `parameterized_parts.keys - keys_to_keep` actually allocates two arrays. It is quicker (I benchmarked) to iterate over each and check inclusion in array manually.

This change buys us 1774 fewer objects per request
2015-07-29 20:41:57 -05:00
Yang Bo
a77de09812 sort_by instead of sort
it is avoid sort errot within different and mixed keys.
used `sort_by` + `block` to list parameter by keys.
keep minimum changes
2015-04-08 22:18:56 +08:00
Rafael Mendonça França
e4cfd353a4 Remove deprecated option use_route in controller tests 2015-01-04 11:58:41 -03:00
Godfrey Chan
23b21f6182 Don't show the warning if we're already raising the error anyway
If the route set is empty, or if none of the routes matches with a score > 0,
there is no point showing the deprecation message because we are already be
raising the `ActionController::UrlGenerationError` mentioned in the warning.

In this case it is the expected behavior and the user wouldn't have to take any
actions.
2014-11-23 01:46:51 -08:00
Godfrey Chan
c23bb156fe Deprecate passing an invalid name to Formatter#generate
The internal tests that (incorrectly) relied on this were already fixed in
938d130. However, we cannot simply fix this bug because the guides prior to
b7b9e92 recommended a workaround that relies on this buggy behavior.

Reference #17453
2014-11-23 01:40:32 -08:00
Accessd
2224bf7992 fix url generation error message 2014-10-21 12:10:48 +04:00
Aaron Patterson
932386be8a recall should be path_parameters, also make it required
"recall" is a terrible name.  This variable contains the parameters that
we got from the path (e.g. for "/posts/1" it has :controller => "posts",
:id => "1").  Since it contains the parameters we got from the path,
"path_parameters" is a better name.  We always pass path_parameters to
`generate`, so lets make it required.
2014-07-17 11:26:59 -07:00
Viktar Basharymau
fd03569210 Remove AD::Journey::Formatter#verify_required_parts!
Nobody uses this private method, maybe it is a leftover from some old
refactoring. Let's delete it.
2014-05-23 18:00:50 +03:00
Rafael Mendonça França
70ec49c87a Merge pull request #15254 from DNNX/formatter-refactoring-3
Rename `stack` to `queue`
2014-05-22 16:02:23 -03:00
Rafael Mendonça França
35c160066a Merge pull request #15252 from DNNX/formatter-refactoring-2
Remove unnecessary `Hash#to_a` call
2014-05-22 15:40:15 -03:00
Viktar Basharymau
2dafd87edd Rename stack to queue
Because it is used as a queue (FIFO), not as a stack (LIFO).

* http://en.wikipedia.org/wiki/Stack_(abstract_data_type)
* http://en.wikipedia.org/wiki/Queue_(data_structure)
2014-05-22 21:33:26 +03:00
Viktar Basharymau
ee92c61689 Remove unnecessary Hash#to_a call
Inspired by 931ee4186b

```ruby

def stat(num)
  start = GC.stat(:total_allocated_object)
  num.times { yield }
  total_obj_count = GC.stat(:total_allocated_object) - start
  puts "#{total_obj_count / num} allocations per call"
end

h = { 'x' => 'y' }

stat(100) { h.     each { |pair| pair } }
stat(100) { h.to_a.each { |pair| pair } }

__END__
1 allocations per call
2 allocations per call
```
2014-05-22 21:16:56 +03:00
Viktar Basharymau
eba8b70d4e Use break instead of next in AD::Journey::Formatter#match_route
The array is sorted in descending order, so there is no point in
iterating further if we met a negative item - all the rest will be
negative too.
2014-05-22 17:44:25 +03:00
Aaron Patterson
7bc25f0035 do not mutate parameters, let the caller do mutations 2014-05-21 14:27:30 -07:00
Aaron Patterson
089d9baa33 we don't use this parameter for anything, so rm 2014-05-20 16:06:59 -07:00
Erik Michaels-Ober
b11ebf1d80 Replace map.flatten(1) with flat_map 2014-02-28 22:46:25 -08:00