respond_to_faster 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f06087ed36268670b30677984ce24720fe47b07
4
- data.tar.gz: 993129ec0042fa434b31f2a5b3ce49c21248c96d
3
+ metadata.gz: b424a2e779015c87f2875079a8ec8df721c23ac3
4
+ data.tar.gz: d9f71e62dad4f85912e22e4a1cb2922e4e99e3d7
5
5
  SHA512:
6
- metadata.gz: '0929b03566c46db822d280dd117e11b94595435b1d0a99feb58972b1a23c9529d2dfdd21c19c3c898d9bd07bf568805ac89527414d3983867d9b3ad474b7da49'
7
- data.tar.gz: 883278f1fc91b09eb4d2458c705cdd6f9e98ba8b90685e1ad42331c570c076c669b4f6ef25b42c8f4161c02b0c9592fa49c8efaad3b6dbe63b4f96c7c2ccdfe5
6
+ metadata.gz: 1e83e14ed3105dcdbd3c56279873311db4ae60426c64a21f0a7fd8066785a3ed0111fc2b3ace7556f069583568c4895809cfeb507fff4661c900d429beba16e8
7
+ data.tar.gz: e7996d9d301c561e9e64a40cff73e8382eac3919f018f96913111e03a1cebf27ea44d72f34e9439a1c4df4e4afe1e2daef1e72ff1103d42d930e6e4ea6afed22
@@ -0,0 +1,7 @@
1
+ # RespondToFaster Changelog
2
+
3
+ ## 0.1
4
+
5
+ ### 0.1.0
6
+ * Cache modules across singleton classes [#1](https://github.com/shioyama/respond_to_faster/pull/1)
7
+
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
@@ -1,29 +1,76 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- respond_to_faster (0.1.0)
5
- activerecord (>= 5.1, < 6.0)
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
- activemodel (5.1.4)
11
- activesupport (= 5.1.4)
12
- activerecord (5.1.4)
13
- activemodel (= 5.1.4)
14
- activesupport (= 5.1.4)
15
- arel (~> 8.0)
16
- activesupport (5.1.4)
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 (8.0.0)
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
- bundler (~> 1.16.a)
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.pre.2
110
+ 1.16.0
data/README.md CHANGED
@@ -1,22 +1,135 @@
1
- # RespondToFaster :rocket:
1
+ RespondToFaster :rocket:
2
+ ========================
2
3
 
3
- Speed up methods on results from custom ActiveRecord queries.
4
+ [![Gem Version](https://badge.fury.io/rb/respond_to_faster.svg)][gem]
5
+ [![Build Status](https://travis-ci.org/shioyama/respond_to_faster.svg?branch=master)][travis]
6
+ [![Dependency Status](https://gemnasium.com/shioyama/respond_to_faster.svg)][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
- Suppose you have a query with some custom SQL, like this:
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
- posts = Post.select(:title, :content)
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 https://github.com/shioyama/respond_to_faster. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
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 License](https://opensource.org/licenses/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 trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/shioyama/respond_to_faster/blob/master/CODE_OF_CONDUCT.md).
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).
@@ -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).each do |name|
7
- singleton_class.define_attribute_method name
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
@@ -1,3 +1,3 @@
1
1
  module RespondToFaster
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  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.1
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-18 00:00:00.000000000 Z
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.1'
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.1'
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