| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The exact inspect output of a BigDecimal is out of scope for what we're trying
to communicate about `dup` and `duplicable?` here.
Adding two examples distracts is disctracting, so keep the docs from before
since our minimal version is Ruby 2.2.2.
[ Koichi ITO, Jon Moss, Kasper Timm Hansen ]
This reverts commit 2163874dedaf83e67599c2930c2686caa165fbad, reversing
changes made to 46fdbc5290335ed38fa9fe2b6b0ef8abe4eccb1b.
|
|
|
|
| |
cf. https://github.com/ruby/bigdecimal/pull/42
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Ruby 2.4.0 has trouble duplicating certain symbols created from
strings via `to_sym`.
It didn't happen with `'symbol'.to_sym.dup` for some reason, but
works fine with the longer string sample.
Once a newer Ruby version with a fix is released we'll get have
a failing test case we can fix.
Ref: #27532
|
|
|
|
| |
See [this test](https://gist.github.com/utilum/78918f1b64f8b61ee732cb266db7c43a).
|
|
|
|
|
|
|
| |
`NilClass`, `FalseClass`, `TrueClass`, `Symbol` and `Numeric` can dup
with Ruby 2.4+.
Ref: https://bugs.ruby-lang.org/issues/12979
|
|
|
|
|
|
|
|
|
| |
A few have been left for aesthetic reasons, but have made a pass
and removed most of them.
Note that if the method `foo` returns an array, `foo << 1`
is a regular push, nothing to do with assignments, so
no self required.
|
| |
|
|
|
|
|
| |
The current code base is not uniform. After some discussion,
we have chosen to go with double quotes by default.
|
| |
|
|\
| |
| | |
Adds `not_in?` onto Object
|
| | |
|
|/ |
|
|
|
|
|
|
| |
Some casual benchmarks showed a 2x factor.
All credit goes to @nurse.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When the Pathname object is converted as JSON,
it should be a string that means itself.
Expected:
```
>> Pathname.new('/path/to/somewhere.txt').as_json
"/path/to/somewhere.txt"
```
Actual:
```
>> Pathname.new('/path/to/somewhere.txt').as_json
{"path"=>"/path/to/somewhere.txt"}
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
When the URI object is converted as JSON,
it is expected that it is a string that means its URI.
Expected:
```
>> URI.parse('http://example.com').as_json
"http://example.com"
```
Actual:
```
>> URI.parse('http://example.com').as_json
{"scheme"=>"http",
"user"=>nil,
"password"=>nil,
"host"=>"example.com",
"port"=>80,
"path"=>"",
"query"=>nil,
"opaque"=>nil,
"fragment"=>nil,
"parser"=>
{"regexp"=>
{"SCHEME"=>"(?-mix:\\A[A-Za-z][A-Za-z0-9+\\-.]*\\z)",
"USERINFO"=>"(?-mix:\\A(?:%\\h\\h|[!$&-.0-;=A-Z_a-z~])*\\z)",
"HOST"=>
"(?-mix:\\A(?:(?<IP-literal>\\[(?:(?<IPv6address>(?:\\h{1,4}:){6}(?<ls32>\\h{1,4}:\\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5]|\\d)\\.\\g<dec-octet>\\.\\g<dec-octet>\\.\\g<dec-octet>))|::(?:\\h{1,4}:){5}\\g<ls32>|\\h{,4}::(?:\\h{1,4}:){4}\\g<ls32>|(?:(?:\\h{1,4}:)?\\h{1,4})?::(?:\\h{1,4}:){3}\\g<ls32>|(?:(?:\\h{1,4}:){,2}\\h{1,4})?::(?:\\h{1,4}:){2}\\g<ls32>|(?:(?:\\h{1,4}:){,3}\\h{1,4})?::\\h{1,4}:\\g<ls32>|(?:(?:\\h{1,4}:){,4}\\h{1,4})?::\\g<ls32>|(?:(?:\\h{1,4}:){,5}\\h{1,4})?::\\h{1,4}|(?:(?:\\h{1,4}:){,6}\\h{1,4})?::)|(?<IPvFuture>v\\h+\\.[!$&-.0-;=A-Z_a-z~]+))\\])|\\g<IPv4address>|(?<reg-name>(?:%\\h\\h|[!$&-.0-9;=A-Z_a-z~])*))\\z)",
"ABS_PATH"=>
"(?-mix:\\A\\/(?:%\\h\\h|[!$&-.0-;=@-Z_a-z~])*(?:\\/(?:%\\h\\h|[!$&-.0-;=@-Z_a-z~])*)*\\z)",
"REL_PATH"=>
"(?-mix:\\A(?:%\\h\\h|[!$&-.0-;=@-Z_a-z~])+(?:\\/(?:%\\h\\h|[!$&-.0-;=@-Z_a-z~])*)*\\z)",
"QUERY"=>"(?-mix:\\A(?:%\\h\\h|[!$&-.0-;=@-Z_a-z~\\/?])*\\z)",
"FRAGMENT"=>"(?-mix:\\A(?:%\\h\\h|[!$&-.0-;=@-Z_a-z~\\/?])*\\z)",
"OPAQUE"=>"(?-mix:\\A(?:[^\\/].*)?\\z)",
"PORT"=>
"(?-mix:\\A[\\x09\\x0a\\x0c\\x0d ]*\\d*[\\x09\\x0a\\x0c\\x0d ]*\\z)"}}}
```
|
|
|
|
|
|
|
|
| |
Ruby 2.4 unifies Fixnum and Bignum into Integer: https://bugs.ruby-lang.org/issues/12005
* Forward compat with new unified Integer class in Ruby 2.4+.
* Backward compat with separate Fixnum/Bignum in Ruby 2.2 & 2.3.
* Drops needless Fixnum distinction in docs, preferring Integer.
|
|
|
|
|
|
|
|
|
|
| |
This commit undoes 54243fe.
Reason: Further investigation has shown the benefit is not so clear
generally speaking.
There is a long discussion and several benchmarks in the PR #24658
if you are interested in the details.
|
|
|
|
|
|
| |
This alternative flows better.
[Richard Schneeman & Xavier Noria]
|
|
|
|
|
|
|
|
|
| |
Further investigation seems to disprove that backtracking is the
reason why the positive variant is slower, see
https://github.com/rails/rails/pull/24658#issuecomment-213079710
so, just say nothing about it, only assert it is slower.
|
|
|
|
|
|
|
|
|
|
|
|
| |
When you come here without context, it is important to hightlight that
checking the predicate is worthwhile due to the observation that blank
strings are often empty. So you complicate the code (which has a cost
in terms of readability and aesthetics), but statistically makes sense.
Then, you also need to explain why the second operand is so convoluted.
Otherwise, you wonder why this line is written precisely this way. That
is what code comments are for.
|
|
|
|
| |
Follow up to #24658.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Follow up on https://github.com/rails/rails/commit/697384df36a939e565b7c08725017d49dc83fe40#commitcomment-17184696.
The regex to detect a blank string `/\A[[:space:]]*\z/` will loop through every character in the string to ensure that all of them are a `:space:` type. We can invert this logic and instead look for any non-`:space:` characters. When that happens, we would return on the first character found and the regex engine does not need to keep looking.
Thanks @nellshamrell for the regex talk at LSRC.
By defining a "blank" string as any string that does not have a non-whitespace character (yes, double negative) we can get a substantial speed bump.
Also an inline regex is (barely) faster than a regex in a constant, since it skips the constant lookup. A regex literal is frozen by default.
```ruby
require 'benchmark/ips'
def string_generate
str = " abcdefghijklmnopqrstuvwxyz\t".freeze
str[rand(0..(str.length - 1))] * rand(0..23)
end
strings = 100.times.map { string_generate }
ALL_WHITESPACE_STAR = /\A[[:space:]]*\z/
Benchmark.ips do |x|
x.report('current regex ') { strings.each {|str| str.empty? || ALL_WHITESPACE_STAR === str } }
x.report('+ instead of * ') { strings.each {|str| str.empty? || /\A[[:space:]]+\z/ === str } }
x.report('not a non-whitespace char') { strings.each {|str| str.empty? || !(/[[:^space:]]/ === str) } }
x.compare!
end
# Warming up --------------------------------------
# current regex
# 1.744k i/100ms
# not a non-whitespace char
# 2.264k i/100ms
# Calculating -------------------------------------
# current regex
# 18.078k (± 8.9%) i/s - 90.688k
# not a non-whitespace char
# 23.580k (± 7.1%) i/s - 117.728k
# Comparison:
# not a non-whitespace char: 23580.3 i/s
# current regex : 18078.2 i/s - 1.30x slower
```
This makes the method roughly 30% faster `(23.580 - 18.078)/18.078 * 100`.
cc/ @fxn
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
See the rationale in the comment in this patch.
To benchmark this I ran a number of variations, ultimately narrowing to
require 'benchmark/ips'
str = ''
regexp = /\A[[:space:]]*\z/
Benchmark.ips do |x|
x.report('regexp') { regexp === str }
x.report('empty') { str.empty? || regexp === str }
x.compare!
end
This benchmark has consistently reported speedups around 3.5x:
Calculating -------------------------------------
regexp 69.197k i/100ms
empty 115.468k i/100ms
-------------------------------------------------
regexp 2. 6.3%) i/s - 13.839M
empty 9. 8.8%) i/s - 47.804M
Comparison:
empty: 9642607.6 i/s
regexp: 2768351.9 i/s - 3.48x slower
Sometimes even reaching 4x.
Running the same bechmark on strings of 10 or 100 characters (with
whitespace or present) has shown a slowdown of just about 1.01/1.02.
Marginal, we seem to have a worthwhile trade-off here.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
`each_with_object` allocates an array for each kv pair. Switching to
the slightly more verbose but less allocatey `each_pair` eliminates
array allocations. Eliminating this allocation returns AR objects to
have constant array allocations regardless of the number of columns the
object has.
Here is test code:
```ruby
require 'active_record'
class Topic < ActiveRecord::Base
end
20.times do |i|
Process.waitpid fork {
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Base.connection.instance_eval do
create_table(:topics) do |t|
t.string :title, limit: 250
t.string :author_name
t.string :author_email_address
t.string :parent_title
t.string :type
t.string :group
i.times do |j|
t.string :"aaa#{j}"
end
t.timestamps null: true
end
end
ObjectSpace::AllocationTracer.setup(%i{type})
Topic.create title: "aaron" # heat cache
result = ObjectSpace::AllocationTracer.trace do
10.times do |i|
Topic.create title: "aaron #{i}"
end
end
puts "#{Topic.columns.length},#{(result.find { |k,v| k.first == :T_ARRAY }.last.first / 10)}"
}
end
```
Before this commit:
```
9,166
10,167
11,168
12,169
13,170
14,171
15,172
16,173
17,174
18,175
19,176
20,177
21,178
22,179
23,180
24,181
25,182
26,183
27,184
28,185
```
After:
```
9,157
10,157
11,157
12,157
13,157
14,157
15,157
16,157
17,157
18,157
19,157
20,157
21,157
22,157
23,157
24,157
25,157
26,157
27,157
28,157
```
Left side is the number of columns, right is the number of allocations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
deep_dup'ing a hash will dup the keys as well as the values. Since
string keys from the source hash will be frozen, and the dup'd objects
are immediately dup'd and frozen on insert in to the hash, the end user
will only ever see two frozen strings. Since the strings are immutable,
this commit just cheats a little and reuses the immutable strings.
Just to reiterate, before this commit, deep duping a hash that looks
like this: `{ "foo" => "bar" }` will generate two new instances of
"foo". One is created when `deep_dup` is called on "foo", and the other
is created when the newly allocated "foo" string is inserted in to the
hash. The user never sees the intermediate "foo", and both copies of
"foo" that the user *can* access will be frozen, so in this case we just
reuse the existing frozen key.
The upshot is that after this change, string allocations on AR
allocations become constant regardless of the number of columns the
model has.
```ruby
require 'active_record'
class Topic < ActiveRecord::Base
end
20.times do |i|
Process.waitpid fork {
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
ActiveRecord::Base.connection.instance_eval do
create_table(:topics) do |t|
t.string :title, limit: 250
t.string :author_name
t.string :author_email_address
t.string :parent_title
t.string :type
t.string :group
i.times do |j|
t.integer :"aaa#{j}"
end
t.timestamps null: true
end
end
ObjectSpace::AllocationTracer.setup(%i{type})
Topic.create title: "aaron" # heat cache
result = ObjectSpace::AllocationTracer.trace do
10.times do |i|
Topic.create title: "aaron #{i}"
end
end
puts "#{Topic.columns.length},#{(result.find { |k,v| k.first == :T_STRING }.last.first / 10)}"
}
end
```
If you run the above script before this commit, the output looks like
this:
```
[aaron@TC rails (master)]$ be ruby -rallocation_tracer test.rb
9,105
10,107
11,109
12,111
13,113
14,115
15,117
16,119
17,121
18,123
19,125
20,127
21,129
22,131
23,133
24,135
25,137
26,139
27,141
28,143
```
The left column is the number of methods, the right column is the number
of string allocations.
Running against this commit, the output is:
```
[aaron@TC rails (master)]$ be ruby -rallocation_tracer test.rb
9,87
10,87
11,87
12,87
13,87
14,87
15,87
16,87
17,87
18,87
19,87
20,87
21,87
22,87
23,87
24,87
25,87
26,87
27,87
28,87
```
As you can see, there is now only a constant number of strings
allocated, regardless of the number of columns the model has.
|
| |
|
|
|
|
|
|
|
| |
The concept of a blank date or time doesn't make sense so we can short
circuit the calls for `blank?` on these classes to gain small speed boost.
Fixes #21657
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The perf gain is relatively minor but consistent:
```
Calculating -------------------------------------
0.zero? 137.091k i/100ms
1.zero? 137.350k i/100ms
0 == 0 142.207k i/100ms
1 == 0 144.724k i/100ms
-------------------------------------------------
0.zero? 8.893M (± 6.5%) i/s - 44.280M
1.zero? 8.751M (± 6.4%) i/s - 43.677M
0 == 0 10.033M (± 7.0%) i/s - 49.915M
1 == 0 9.814M (± 8.0%) i/s - 48.772M
```
And try! is quite a big hotspot for us so every little gain is appreciable.
|
| |
|
| |
|
|
|
|
|
|
| |
* Rename `ActiveSupport::Try` => `ActiveSupport::Tryable`
* Include the modules inline
* `private` indentation
|
|
|
|
|
|
|
|
|
|
|
|
| |
`Delegator` inherits from `BasicObject`, which means that it will not
have `Object#try` defined. It will then delegate the call to the
underlying object, which will not (necessarily) respond to the method
defined in the enclosing `Delegator`.
This patches `Delegator` with the `#try` method to work around the
surprising behaviour.
Fixes #5790
|
| |
|
| |
|
| |
|
|
|
|
|
|
|
| |
Thanks @fbernier for suggestion! <3
At this moment we can use Module#prepend in all all cases
except of Range because of the bug [1] in MRI 2.2
[1] https://bugs.ruby-lang.org/issues/10847
|
| |
|
|
|
|
|
| |
Now that Rails requires Ruby >= 2.0, there is no need to check whether
`BigDecimal` exists or not.
|
| |
|
|\ |
|
| |
| |
| |
| |
| |
| | |
Also add doc examples for `Object.nil`.
[ci skip]
|
|/ |
|
|
|
|
|
|
|
|
| |
@rafaelfranca suggested in f7c7bcd9 that code examples should display
the result after `# =>` and not after `#=>`.
This commit replaces *all* the occurrences of `#=>` in the code documentation
(mostly added by me :sob:) with the suggested `# =>`.
|
| |
|
|
|
|
|
|
|
| |
@carlosantoniodasilva pointed out that when `@person` is nil then this would blow up when you ended up calling `#first`on `nil`.
> "there’s no way to break a try chain when you enter it :D"
[ci skip]
|