respond_to_faster 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +67 -12
- data/README.md +120 -7
- data/lib/respond_to_faster.rb +10 -2
- data/lib/respond_to_faster/version.rb +1 -1
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b424a2e779015c87f2875079a8ec8df721c23ac3
|
4
|
+
data.tar.gz: d9f71e62dad4f85912e22e4a1cb2922e4e99e3d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e83e14ed3105dcdbd3c56279873311db4ae60426c64a21f0a7fd8066785a3ed0111fc2b3ace7556f069583568c4895809cfeb507fff4661c900d429beba16e8
|
7
|
+
data.tar.gz: e7996d9d301c561e9e64a40cff73e8382eac3919f018f96913111e03a1cebf27ea44d72f34e9439a1c4df4e4afe1e2daef1e72ff1103d42d930e6e4ea6afed22
|
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
@@ -4,3 +4,20 @@ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
|
4
4
|
|
5
5
|
# Specify your gem's dependencies in respond_to_faster.gemspec
|
6
6
|
gemspec
|
7
|
+
|
8
|
+
group :development, :test do
|
9
|
+
if ENV['RAILS_VERSION'] == '5.0'
|
10
|
+
gem 'activerecord', '>= 5.0', '< 5.1'
|
11
|
+
elsif ENV['RAILS_VERSION'] == '4.2'
|
12
|
+
gem 'activerecord', '>= 4.2.6', '< 5.0'
|
13
|
+
elsif ENV['RAILS_VERSION'] == '5.1'
|
14
|
+
gem 'activerecord', '>= 5.1', '< 5.2'
|
15
|
+
else
|
16
|
+
gem 'activerecord', '>= 5.2.0.beta2', '< 5.3'
|
17
|
+
gem 'railties', '>= 5.2.0.beta2', '< 5.3'
|
18
|
+
end
|
19
|
+
|
20
|
+
gem 'benchmark-ips'
|
21
|
+
gem 'sqlite3'
|
22
|
+
gem 'pry-byebug'
|
23
|
+
end
|
data/Gemfile.lock
CHANGED
@@ -1,29 +1,76 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
respond_to_faster (0.1
|
5
|
-
activerecord (>= 5.
|
4
|
+
respond_to_faster (0.0.1)
|
5
|
+
activerecord (>= 5.0, < 6.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
actionpack (5.2.0.beta2)
|
11
|
+
actionview (= 5.2.0.beta2)
|
12
|
+
activesupport (= 5.2.0.beta2)
|
13
|
+
rack (~> 2.0)
|
14
|
+
rack-test (>= 0.6.3)
|
15
|
+
rails-dom-testing (~> 2.0)
|
16
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
17
|
+
actionview (5.2.0.beta2)
|
18
|
+
activesupport (= 5.2.0.beta2)
|
19
|
+
builder (~> 3.1)
|
20
|
+
erubi (~> 1.4)
|
21
|
+
rails-dom-testing (~> 2.0)
|
22
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
23
|
+
activemodel (5.2.0.beta2)
|
24
|
+
activesupport (= 5.2.0.beta2)
|
25
|
+
activerecord (5.2.0.beta2)
|
26
|
+
activemodel (= 5.2.0.beta2)
|
27
|
+
activesupport (= 5.2.0.beta2)
|
28
|
+
arel (>= 9.0)
|
29
|
+
activesupport (5.2.0.beta2)
|
17
30
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
31
|
i18n (~> 0.7)
|
19
32
|
minitest (~> 5.1)
|
20
33
|
tzinfo (~> 1.1)
|
21
|
-
arel (
|
34
|
+
arel (9.0.0)
|
35
|
+
benchmark-ips (2.7.2)
|
36
|
+
builder (3.2.3)
|
37
|
+
byebug (9.1.0)
|
38
|
+
coderay (1.1.2)
|
22
39
|
concurrent-ruby (1.0.5)
|
40
|
+
crass (1.0.3)
|
41
|
+
database_cleaner (1.6.2)
|
23
42
|
diff-lcs (1.3)
|
43
|
+
erubi (1.7.0)
|
24
44
|
i18n (0.9.1)
|
25
45
|
concurrent-ruby (~> 1.0)
|
46
|
+
loofah (2.1.1)
|
47
|
+
crass (~> 1.0.2)
|
48
|
+
nokogiri (>= 1.5.9)
|
49
|
+
method_source (0.9.0)
|
50
|
+
mini_portile2 (2.3.0)
|
26
51
|
minitest (5.10.3)
|
52
|
+
nokogiri (1.8.1)
|
53
|
+
mini_portile2 (~> 2.3.0)
|
54
|
+
pry (0.11.3)
|
55
|
+
coderay (~> 1.1.0)
|
56
|
+
method_source (~> 0.9.0)
|
57
|
+
pry-byebug (3.5.1)
|
58
|
+
byebug (~> 9.1)
|
59
|
+
pry (~> 0.10)
|
60
|
+
rack (2.0.3)
|
61
|
+
rack-test (0.8.2)
|
62
|
+
rack (>= 1.0, < 3)
|
63
|
+
rails-dom-testing (2.0.3)
|
64
|
+
activesupport (>= 4.2.0)
|
65
|
+
nokogiri (>= 1.6)
|
66
|
+
rails-html-sanitizer (1.0.3)
|
67
|
+
loofah (~> 2.0)
|
68
|
+
railties (5.2.0.beta2)
|
69
|
+
actionpack (= 5.2.0.beta2)
|
70
|
+
activesupport (= 5.2.0.beta2)
|
71
|
+
method_source
|
72
|
+
rake (>= 0.8.7)
|
73
|
+
thor (>= 0.18.1, < 2.0)
|
27
74
|
rake (10.5.0)
|
28
75
|
rspec (3.7.0)
|
29
76
|
rspec-core (~> 3.7.0)
|
@@ -38,6 +85,8 @@ GEM
|
|
38
85
|
diff-lcs (>= 1.2.0, < 2.0)
|
39
86
|
rspec-support (~> 3.7.0)
|
40
87
|
rspec-support (3.7.0)
|
88
|
+
sqlite3 (1.3.13)
|
89
|
+
thor (0.20.0)
|
41
90
|
thread_safe (0.3.6)
|
42
91
|
tzinfo (1.2.4)
|
43
92
|
thread_safe (~> 0.1)
|
@@ -46,10 +95,16 @@ PLATFORMS
|
|
46
95
|
ruby
|
47
96
|
|
48
97
|
DEPENDENCIES
|
49
|
-
|
98
|
+
activerecord (>= 5.2.0.beta2, < 5.3)
|
99
|
+
benchmark-ips
|
100
|
+
bundler (~> 1.16)
|
101
|
+
database_cleaner (~> 1.6)
|
102
|
+
pry-byebug
|
103
|
+
railties (>= 5.2.0.beta2, < 5.3)
|
50
104
|
rake (~> 10.0)
|
51
105
|
respond_to_faster!
|
52
106
|
rspec (~> 3.0)
|
107
|
+
sqlite3
|
53
108
|
|
54
109
|
BUNDLED WITH
|
55
|
-
1.16.0
|
110
|
+
1.16.0
|
data/README.md
CHANGED
@@ -1,22 +1,135 @@
|
|
1
|
-
|
1
|
+
RespondToFaster :rocket:
|
2
|
+
========================
|
2
3
|
|
3
|
-
|
4
|
+
[][gem]
|
5
|
+
[][travis]
|
6
|
+
[][gemnasium]
|
7
|
+
|
8
|
+
[gem]: https://rubygems.org/gems/respond_to_faster
|
9
|
+
[travis]: https://travis-ci.org/shioyama/respond_to_faster
|
10
|
+
[gemnasium]: https://gemnasium.com/shioyama/respond_to_faster
|
11
|
+
|
12
|
+
Speed up method response times on results from custom aliased ActiveRecord
|
13
|
+
queries.
|
4
14
|
|
5
15
|
## Usage
|
6
16
|
|
7
|
-
|
17
|
+
Just add the gem to your Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'respond_to_faster', '~> 0.1.0'
|
21
|
+
```
|
22
|
+
|
23
|
+
That's it! Read on to learn about what RespondToFaster is doing under the hood
|
24
|
+
(or checkout the source code, it's only 20 lines long!)
|
25
|
+
|
26
|
+
## Background
|
27
|
+
|
28
|
+
Suppose you have a query with some custom SQL, like this (taken from the
|
29
|
+
[ActiveRecord Querying documentation](http://guides.rubyonrails.org/active_record_querying.html#group)):
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")
|
33
|
+
```
|
34
|
+
|
35
|
+
This query will group orders by date, with each date result responding to the
|
36
|
+
aliases `ordered_date` and `total_price`. So if `order` is the first result
|
37
|
+
returned, this will work:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
order.ordered_date
|
41
|
+
#=> Thu, 14 Dec 2017
|
42
|
+
order.total_price
|
43
|
+
#=> 20.98
|
44
|
+
```
|
45
|
+
|
46
|
+
This is nice, but are those really methods? Let's have a look:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
order.method(:ordered_date)
|
50
|
+
#=> NameError: undefined method `ordered_date' for class `#<Class:0x00559df3a8ef30>'
|
51
|
+
```
|
52
|
+
|
53
|
+
That's strange! No method. So how is the object responding to the
|
54
|
+
`ordered_date` message?
|
55
|
+
|
56
|
+
As usual, Rails is doing some magic under the hood. You can find that magic
|
57
|
+
documented in the [inline
|
58
|
+
docs](https://github.com/rails/rails/blob/fd1304d2aaf5e21df0aac2e8e3f7becdaad15b19/activemodel/lib/active_model/attribute_methods.rb#L415-L420)
|
59
|
+
for `ActiveModel::AttributeMethods`, where there is the somewhat cryptic message:
|
60
|
+
|
61
|
+
> Allows access to the object attributes, which are held in the hash
|
62
|
+
> returned by <tt>attributes</tt>, as though they were first-class
|
63
|
+
> methods. So a <tt>Person</tt> class with a <tt>name</tt> attribute can for example use
|
64
|
+
> <tt>Person#name</tt> and <tt>Person#name=</tt> and never directly use
|
65
|
+
> the attributes hash -- except for multiple assignments with
|
66
|
+
> <tt>ActiveRecord::Base#attributes=</tt>.
|
67
|
+
|
68
|
+
This inline comment dates back to the [very first Rails commit by @dhh in
|
69
|
+
2004](https://github.com/rails/rails/commit/db045dbbf60b53dbe013ef25554fd013baf88134).
|
70
|
+
|
71
|
+
What the code (now in ActiveModel, previously in ActiveRecord) actually does is
|
72
|
+
to override `respond_to?` and `method_missing` to check if a given method call
|
73
|
+
matches a key in the `attributes` hash of the model. If there's a match,
|
74
|
+
ActiveModel "dispatches" to an attribute handler, which returns the result.
|
75
|
+
|
76
|
+
Which is all fine and good, but **method_missing is slow as molasses**. You never
|
77
|
+
really want to be relying on it to return results unless you have no
|
78
|
+
alternative.
|
79
|
+
|
80
|
+
## What this gem does
|
81
|
+
|
82
|
+
So what this gem does is to *remove these overrides*, which have been around
|
83
|
+
since the dawn of Ruby on Rails. Not just tweak them, or override them, but
|
84
|
+
**remove them**.
|
85
|
+
|
86
|
+
Here is the code that does this, just two lines:
|
8
87
|
|
9
88
|
```ruby
|
10
|
-
|
89
|
+
ActiveModel::AttributeMethods.send(:remove_method, :respond_to?)
|
90
|
+
ActiveModel::AttributeMethods.send(:remove_method, :method_missing)
|
91
|
+
```
|
92
|
+
|
93
|
+
For the vast majority of cases, *this will have no impact on your ActiveRecord
|
94
|
+
objects*. AR has grown over the years to the point where most attribute methods
|
95
|
+
are defined, so these fallbacks are not necessary.
|
96
|
+
|
97
|
+
The one exception is the example earlier with the custom aliased query. In this
|
98
|
+
case, depending on the query, some custom attributes are needed on the objects
|
99
|
+
returned, and ActiveRecord still relies on this (very slow) mechanism to make
|
100
|
+
the magic work.
|
101
|
+
|
102
|
+
But this is a high price to pay for some simple magic. This gem instead
|
103
|
+
*defines the methods* on the singleton class of the objects returned, so that
|
104
|
+
you never need to go to `method_missing`. This makes things **much faster**, as
|
105
|
+
much as 5-10 times faster.
|
106
|
+
|
107
|
+
## Caveats
|
108
|
+
|
109
|
+
If you don't use any custom querying with aliases like the one above, this gem
|
110
|
+
might not do much for you. It should make `respond_to?` a bit faster, but you
|
111
|
+
may not even notice that.
|
112
|
+
|
113
|
+
However, if you've got some crazy heavy SQL logic somewhere deep in your
|
114
|
+
application, and you're finding it takes forever, give this gem a shot and tell
|
115
|
+
me what you find! I'd like to get this eventually merged into ActiveRecord so
|
116
|
+
I'd like to know about any issues you encounter in your application.
|
11
117
|
|
12
118
|
## Contributing
|
13
119
|
|
14
|
-
Bug reports and pull requests are welcome on GitHub at
|
120
|
+
Bug reports and pull requests are welcome on GitHub at
|
121
|
+
https://github.com/shioyama/respond_to_faster. This project is intended to be a
|
122
|
+
safe, welcoming space for collaboration, and contributors are expected to
|
123
|
+
adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
|
124
|
+
conduct.
|
15
125
|
|
16
126
|
## License
|
17
127
|
|
18
|
-
The gem is available as open source under the terms of the [MIT
|
128
|
+
The gem is available as open source under the terms of the [MIT
|
129
|
+
License](https://opensource.org/licenses/MIT).
|
19
130
|
|
20
131
|
## Code of Conduct
|
21
132
|
|
22
|
-
Everyone interacting in the RespondToFaster project’s codebases, issue
|
133
|
+
Everyone interacting in the RespondToFaster project’s codebases, issue
|
134
|
+
trackers, chat rooms and mailing lists is expected to follow the [code of
|
135
|
+
conduct](https://github.com/shioyama/respond_to_faster/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/respond_to_faster.rb
CHANGED
@@ -1,10 +1,18 @@
|
|
1
|
+
require "active_record"
|
1
2
|
require "respond_to_faster/version"
|
2
3
|
|
3
4
|
module RespondToFaster
|
4
5
|
def init_with(coder)
|
5
6
|
super.tap do
|
6
|
-
(attribute_names - self.class.attribute_names).
|
7
|
-
|
7
|
+
unless (uncached_attrs = attribute_names - self.class.attribute_names).empty?
|
8
|
+
klass = self.class
|
9
|
+
mod_name = "RespondToFaster_#{uncached_attrs.hash.abs}".freeze
|
10
|
+
if klass.const_defined?(mod_name)
|
11
|
+
singleton_class.include klass.const_get(mod_name)
|
12
|
+
else
|
13
|
+
uncached_attrs.each &singleton_class.method(:define_attribute_method)
|
14
|
+
klass.const_set(mod_name, singleton_class.send(:generated_attribute_methods))
|
15
|
+
end
|
8
16
|
end
|
9
17
|
end
|
10
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: respond_to_faster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Salzberg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-12-
|
11
|
+
date: 2017-12-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,7 +16,7 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '5.
|
19
|
+
version: '5.0'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: '6.0'
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '5.
|
29
|
+
version: '5.0'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: '6.0'
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '3.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: database_cleaner
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.6'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '1.6'
|
75
89
|
description: Patches ActiveRecord to make models returned from custom queries respond
|
76
90
|
much faster.
|
77
91
|
email:
|
@@ -80,6 +94,7 @@ executables: []
|
|
80
94
|
extensions: []
|
81
95
|
extra_rdoc_files: []
|
82
96
|
files:
|
97
|
+
- CHANGELOG.md
|
83
98
|
- CODE_OF_CONDUCT.md
|
84
99
|
- Gemfile
|
85
100
|
- Gemfile.lock
|