constant_table_saver 4.2.0 → 5.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: e2578ce8faa60c69b0c71cac907a097168fefcda
4
- data.tar.gz: 8b53a074ed7d5f9d919de2b0de4e8d1a8cbb8140
3
+ metadata.gz: c0478d2577af80dc5fc4c7bf860321b72c49e2c1
4
+ data.tar.gz: 27060f22739b2cdb8a0cbf36f56ebe186e9b6333
5
5
  SHA512:
6
- metadata.gz: ce00c64f33fe8a7aee4291579ecddd24d5d18f0aadaeafbaf4d7cd3b7074f11c8a32e97c154d9e6a769fb7b664e18bb08343bfe72c0dd642b5c1419dfea247ec
7
- data.tar.gz: bd8e42cdf8dc69619c13b195799fe886c0d41873ed061229c95b37c3ec30fda113ed9964871d5781bf829e0ff3504158a481525253673332bf649774406f7d39
6
+ metadata.gz: 3784c8649039aca241ae5e3b87b4b94d219c4c159d91b79d261dc1af3fa7795e4368e4ab97535290c0fec3c759d3729a5959988012bb7f10cd7d0b915fd854f2
7
+ data.tar.gz: 559071bc2fbadd429c6c2634a1f3eb71e27258c9dc260c38a60681527adf3b8436b4945a932cf83b7bc1e9e2c5898132879a87adc34a729a18486d8bd144ae6a
data/README CHANGED
@@ -11,10 +11,9 @@ named after the name field you specify.
11
11
  Compatibility
12
12
  =============
13
13
 
14
- Currently tested against Rails 4.2 (up to 4.2.3), 4.1 (up to 4.1.12), 4.0 (up to 4.0.13), and 3.2 (3.2.18), on Ruby 2.1.5.
14
+ Currently tested against Rails 5.1 (5.1.0beta2) and 5.0 (up to 5.0.2) and 4.2 (up to 4.2.7), on Ruby 2.3.4.
15
15
 
16
- Was also previously tested compatible with 2.3.14, 3.0.17, and 3.1.8 on Ruby 2.0.0
17
- or 1.8.7 as appropriate, and may still work for them.
16
+ For earlier versions of Rails, use an older version of the gem.
18
17
 
19
18
 
20
19
  Example
@@ -16,10 +16,9 @@ named after the name field you specify.
16
16
  Compatibility
17
17
  =============
18
18
 
19
- Currently tested against Rails 4.2 (up to 4.2.3), 4.1 (up to 4.1.12), 4.0 (up to 4.0.13), and 3.2 (3.2.18), on Ruby 2.1.5.
19
+ Currently tested against Rails 5.1 (5.1.0beta2) and 5.0 (up to 5.0.2) and 4.2 (up to 4.2.7), on Ruby 2.3.4.
20
20
 
21
- Was also previously tested compatible with 2.3.14, 3.0.17, and 3.1.8 on Ruby 2.0.0
22
- or 1.8.7 as appropriate, and may still work for them.
21
+ For earlier versions of Rails, use an older version of the gem.
23
22
  EOF
24
23
  gem.has_rdoc = false
25
24
  gem.author = "Will Bryant"
@@ -1,3 +1,3 @@
1
1
  module ConstantTableSaver
2
- VERSION = '4.2.0'
2
+ VERSION = '5.1.0'
3
3
  end
@@ -7,11 +7,13 @@ module ConstantTableSaver
7
7
  options.assert_valid_keys(:name, :name_prefix, :name_suffix)
8
8
  class_attribute :constant_table_options, :instance_writer => false
9
9
  self.constant_table_options = options
10
+
11
+ @constant_record_methods = nil
10
12
 
11
- if ActiveRecord::VERSION::MAJOR == 3
12
- extend ActiveRecord3ClassMethods
13
- else
13
+ if ActiveRecord::VERSION::MAJOR == 4
14
14
  extend ActiveRecord4ClassMethods
15
+ else
16
+ extend ActiveRecord5ClassMethods
15
17
  end
16
18
  extend ClassMethods
17
19
  extend NameClassMethods if constant_table_options[:name]
@@ -27,8 +29,10 @@ module ConstantTableSaver
27
29
  def reset_cache_with_constant_tables(*args)
28
30
  reset_cache_without_constant_tables(*args).tap { ConstantTableSaver.reset_all_caches }
29
31
  end
30
- alias_method_chain :create_fixtures, :constant_tables
31
- alias_method_chain :reset_cache, :constant_tables
32
+ alias :create_fixtures_without_constant_tables :create_fixtures
33
+ alias :create_fixtures :create_fixtures_with_constant_tables
34
+ alias :reset_cache_without_constant_tables :reset_cache
35
+ alias :reset_cache :reset_cache_with_constant_tables
32
36
  end unless klass.respond_to?(:create_fixtures_with_constant_tables)
33
37
  end
34
38
  end
@@ -47,6 +51,69 @@ module ConstantTableSaver
47
51
  @constant_record_methods.each {|method_id| (class << self; self; end;).send(:remove_method, method_id)} if @constant_record_methods
48
52
  @cached_records = @cached_records_by_id = @constant_record_methods = @cached_blank_scope = @find_by_sql = nil
49
53
  end
54
+
55
+ def _to_sql_with_binds(sql, binds)
56
+ if sql.respond_to?(:to_sql)
57
+ # an arel object
58
+ connection.to_sql(sql, binds)
59
+ else
60
+ # a plain string
61
+ sql
62
+ end
63
+ end
64
+
65
+ def relation
66
+ super.tap do |s|
67
+ class << s
68
+ # we implement find_some here because we'd have to use partial string matching to catch
69
+ # this case in find_by_sql, which would be ugly. (we do the other cases in find_by_sql
70
+ # because it's simpler & the only place to catch things like association find queries.)
71
+ def find_some(ids)
72
+ return super if @values.present? # special cases such as offset and limit
73
+ ids.collect {|id| find_one(id)}
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ module ActiveRecord5ClassMethods
81
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
82
+ @find_by_sql ||= {
83
+ :all => all.to_sql,
84
+ :id => relation.where(relation.table[primary_key].eq(Arel::Nodes::BindParam.new)).limit(1).arel,
85
+ :first => relation.order(relation.table[primary_key].asc).limit(1).arel,
86
+ :last => relation.order(relation.table[primary_key].desc).limit(1).arel,
87
+ }
88
+
89
+ @limit_one ||= ActiveRecord::Attribute.with_cast_value("LIMIT", 1, ActiveRecord::Type::Value.new)
90
+
91
+ _sql = _to_sql_with_binds(sql, binds)
92
+
93
+ if binds.empty?
94
+ if _sql == @find_by_sql[:all]
95
+ return @cached_records ||= super(relation.to_sql).each(&:freeze)
96
+ end
97
+
98
+ elsif binds.size == 1 &&
99
+ binds.last == @limit_one
100
+ if _sql == _to_sql_with_binds(@find_by_sql[:first], binds)
101
+ return [relation.to_a.first].compact
102
+ elsif _sql == _to_sql_with_binds(@find_by_sql[:last], binds)
103
+ return [relation.to_a.last].compact
104
+ end
105
+
106
+ elsif binds.size == 2 &&
107
+ binds.last == @limit_one &&
108
+ binds.first.is_a?(ActiveRecord::Relation::QueryAttribute) &&
109
+ binds.first.name == primary_key &&
110
+ _sql == _to_sql_with_binds(@find_by_sql[:id], binds) # we have to late-render the find(id) SQL because mysql2 on 4.1 and later requires the bind variables to render the SQL, and errors out with a nil dereference otherwise
111
+ @cached_records_by_id ||= relation.to_a.index_by {|record| record.id.to_param}
112
+ return [@cached_records_by_id[binds.first.value.to_param]].compact
113
+ end
114
+
115
+ super(sql, binds, preparable: preparable, &block)
116
+ end
50
117
  end
51
118
 
52
119
  module ActiveRecord4ClassMethods
@@ -81,121 +148,6 @@ module ConstantTableSaver
81
148
 
82
149
  super
83
150
  end
84
-
85
- def _to_sql_with_binds(sql, binds)
86
- if sql.respond_to?(:to_sql)
87
- # an arel object
88
- connection.to_sql(sql, binds)
89
- else
90
- # a plain string
91
- sql
92
- end
93
- end
94
-
95
- def relation
96
- super.tap do |s|
97
- class << s
98
- # we implement find_some here because we'd have to use partial string matching to catch
99
- # this case in find_by_sql, which would be ugly. (we do the other cases in find_by_sql
100
- # because it's simpler & the only place to catch things like association find queries.)
101
- def find_some(ids)
102
- return super if @values.present? # special cases such as offset and limit
103
- ids.collect {|id| find_one(id)}
104
- end
105
- end
106
- end
107
- end
108
- end
109
-
110
- module ActiveRecord3ClassMethods
111
- def scoped(options = nil)
112
- return super if options
113
- return super if respond_to?(:current_scoped_methods, true) && current_scoped_methods
114
- return super if respond_to?(:current_scope, true) && current_scope
115
- @cached_blank_scope ||= super.tap do |s|
116
- class << s
117
- def to_a
118
- return @records if loaded?
119
- super.each(&:freeze)
120
- end
121
-
122
- def find(*args)
123
- # annoyingly, the call to find to load a belongs_to passes :conditions => nil, which causes
124
- # the base find method to apply_finder_options and construct an entire new scope, which is
125
- # unnecessary and also means that it bypasses our find_one implementation (we don't interfere
126
- # with scopes or finds that actually do apply conditions etc.), so we check as a special case
127
- return find_with_ids(args.first) if args.length == 2 && args.last == {:conditions => nil}
128
- super
129
- end
130
-
131
- def find_first
132
- # the normal scope implementation would cache this anyway, but we force a load of all records,
133
- # since otherwise if the app used .first before using .all there'd be unnecessary queries
134
- to_a.first
135
- end
136
-
137
- def find_last
138
- # as for find_first
139
- to_a.last
140
- end
141
-
142
- def find_one(id)
143
- # see below re to_param
144
- cached_records_by_id[id.to_param] || raise(::ActiveRecord::RecordNotFound, "Couldn't find #{name} with ID=#{id}")
145
- end
146
-
147
- def find_some(ids)
148
- # see below re to_param
149
- ids.collect {|id| cached_records_by_id[id.to_param]}.tap do |results| # obviously since find_one caches efficiently, this isn't inefficient as it would be for real finds
150
- results.compact!
151
- raise(::ActiveRecord::RecordNotFound, "Couldn't find all #{name.pluralize} with IDs #{ids.join ','} (found #{results.size} results, but was looking for #{ids.size}") unless results.size == ids.size
152
- end
153
- end
154
-
155
- # in Rails 3.1 the associations code was rewritten to generalise its sql generation to support
156
- # more complex relationships (eg. nested :through associations). however unfortunately, during
157
- # this work the implementation of belongs_to associations was changed so that it no longer calls
158
- # one of the basic find_ methods above; instead a vanilla target scope is constructed, a where()
159
- # scope to add the constraint that the primary key = the FK value is constructed, the two are
160
- # merged, and then #first is called on that scope. frustratingly, all this complexity means that
161
- # our find_ hooks above are no longer called when dereferencing a belongs_to association; they
162
- # work fine and are used elsewhere, but we have to explicitly handle belongs_to target scope
163
- # merging to avoid those querying, which is a huge PITA. because we want to ensure that we don't
164
- # end up accidentally caching other scope requests, we explicitly build a list of the possible
165
- # ARel constrained scopes - indexing them by their expression in SQL so that we don't need to
166
- # code in the list of all the possible ARel terms. we then go one step further and make this
167
- # cached scope pre-loaded using the record we already have - there's sadly no external way to do
168
- # this so we have to shove in the instance variables.
169
- #
170
- # it will be clear that this was a very problematic ActiveRecord refactoring.
171
- def belongs_to_record_scopes
172
- @belongs_to_record_scopes ||= to_a.each_with_object({}) do |record, results|
173
- scope_that_belongs_to_will_want = where(table[primary_key].eq(record.id))
174
- scope_that_belongs_to_will_want.instance_variable_set("@loaded", true)
175
- scope_that_belongs_to_will_want.instance_variable_set("@records", [record])
176
- results[scope_that_belongs_to_will_want.to_sql] = scope_that_belongs_to_will_want
177
- end.freeze
178
- end
179
-
180
- def merge(other)
181
- if belongs_to_record_scope = belongs_to_record_scopes[other.to_sql]
182
- return belongs_to_record_scope
183
- end
184
-
185
- super other
186
- end
187
-
188
- private
189
- def cached_records_by_id
190
- # we'd like to use the same as ActiveRecord's finder_methods.rb, which uses:
191
- # id = id.id if ActiveRecord::Base === id
192
- # but referencing ActiveRecord::Base here segfaults my ruby 1.8.7
193
- # (2009-06-12 patchlevel 174) [universal-darwin10.0]! instead we use to_param.
194
- @cached_records_by_id ||= all.index_by {|record| record.id.to_param}
195
- end
196
- end
197
- end
198
- end
199
151
  end
200
152
 
201
153
  module NameClassMethods
@@ -54,8 +54,6 @@ class ConstantTableSaverTest < ActiveSupport::TestCase
54
54
  assert_queries(0, &block)
55
55
  end
56
56
 
57
- DEPRECATED_FINDERS = ActiveRecord::VERSION::MAJOR == 3 || (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR == 0)
58
-
59
57
  test "it caches all() results" do
60
58
  @pies = StandardPie.all.to_a
61
59
  assert_queries(1) do
@@ -66,16 +64,6 @@ class ConstantTableSaverTest < ActiveSupport::TestCase
66
64
  end
67
65
  end
68
66
 
69
- test "it caches find(:all) results" do
70
- @pies = StandardPie.find(:all).to_a
71
- assert_queries(1) do
72
- assert_equal @pies.collect(&:attributes), ConstantPie.find(:all).to_a.collect(&:attributes)
73
- end
74
- assert_no_queries do
75
- assert_equal @pies.collect(&:attributes), ConstantPie.find(:all).to_a.collect(&:attributes)
76
- end
77
- end if DEPRECATED_FINDERS
78
-
79
67
  test "it caches find(id) results" do
80
68
  @pie = StandardPie.find(1)
81
69
  @other_pie = StandardPie.find(2)
@@ -100,16 +88,6 @@ class ConstantTableSaverTest < ActiveSupport::TestCase
100
88
  end
101
89
  end
102
90
 
103
- test "it caches find(:first) results" do
104
- @pie = StandardPie.find(:first)
105
- assert_queries(1) do
106
- assert_equal @pie.attributes, ConstantPie.find(:first).attributes
107
- end
108
- assert_no_queries do
109
- assert_equal @pie.attributes, ConstantPie.find(:first).attributes
110
- end
111
- end if DEPRECATED_FINDERS
112
-
113
91
  test "it caches first() results" do
114
92
  @pie = StandardPie.first
115
93
  assert_queries(1) do
@@ -119,16 +97,6 @@ class ConstantTableSaverTest < ActiveSupport::TestCase
119
97
  assert_equal @pie.attributes, ConstantPie.first.attributes
120
98
  end
121
99
  end
122
-
123
- test "it caches find(:last) results" do
124
- @pie = StandardPie.find(:last)
125
- assert_queries(1) do
126
- assert_equal @pie.attributes, ConstantPie.find(:last).attributes
127
- end
128
- assert_no_queries do
129
- assert_equal @pie.attributes, ConstantPie.find(:last).attributes
130
- end
131
- end if DEPRECATED_FINDERS
132
100
 
133
101
  test "it caches last() results" do
134
102
  @pie = StandardPie.last
@@ -154,7 +122,6 @@ class ConstantTableSaverTest < ActiveSupport::TestCase
154
122
 
155
123
  test "it isn't affected by scopes active at the time of first load" do
156
124
  assert_equal 0, ConstantPie.filled_with_unicorn.all.to_a.size
157
- assert_equal 0, ConstantPie.with_unicorn_filling_scope { ConstantPie.all.to_a.length } if DEPRECATED_FINDERS
158
125
  assert_equal StandardPie.all.to_a.size, ConstantPie.all.to_a.size
159
126
  end
160
127
 
@@ -193,28 +160,9 @@ class ConstantTableSaverTest < ActiveSupport::TestCase
193
160
  end
194
161
  end
195
162
 
196
- test "it doesn't cache find queries with options" do
197
- @pies = StandardPie.all(:select => "id").to_a
198
- @pie = StandardPie.find(1, :select => "id")
199
- assert_queries(3) do
200
- assert_equal @pies.collect(&:attributes), ConstantPie.all(:select => "id").collect(&:attributes)
201
- assert_equal @pies.collect(&:attributes), ConstantPie.find(:all, :select => "id").collect(&:attributes)
202
- assert_equal @pie.attributes, ConstantPie.find(1, :select => "id").attributes
203
- end
204
- assert_queries(3) do
205
- assert_equal @pies.collect(&:attributes), ConstantPie.all(:select => "id").collect(&:attributes)
206
- assert_equal @pies.collect(&:attributes), ConstantPie.find(:all, :select => "id").collect(&:attributes)
207
- assert_equal @pie.attributes, ConstantPie.find(1, :select => "id").attributes
208
- end
209
- end if DEPRECATED_FINDERS
210
-
211
163
  test "it passes the options preventing caching to the underlying query methods" do
212
- assert_equal nil, ConstantPie.where(:filling => 'unicorn').first
213
- assert_equal nil, ConstantPie.first(:conditions => {:filling => 'unicorn'}) if DEPRECATED_FINDERS
214
- assert_equal nil, ConstantPie.find(:first, :conditions => {:filling => 'unicorn'}) if DEPRECATED_FINDERS
164
+ assert_nil nil, ConstantPie.where(:filling => 'unicorn').first
215
165
  assert_equal [], ConstantPie.where(:filling => 'unicorn').all
216
- assert_equal [], ConstantPie.all(:conditions => {:filling => 'unicorn'}) if DEPRECATED_FINDERS
217
- assert_equal [], ConstantPie.find(:all, :conditions => {:filling => 'unicorn'}) if DEPRECATED_FINDERS
218
166
  end
219
167
 
220
168
  test "it creates named class methods if a :name option is given" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: constant_table_saver
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Bryant
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-06 00:00:00.000000000 Z
11
+ date: 2017-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -63,10 +63,9 @@ description: |
63
63
  Compatibility
64
64
  =============
65
65
 
66
- Currently tested against Rails 4.2 (up to 4.2.3), 4.1 (up to 4.1.12), 4.0 (up to 4.0.13), and 3.2 (3.2.18), on Ruby 2.1.5.
66
+ Currently tested against Rails 5.1 (5.1.0beta2) and 5.0 (up to 5.0.2) and 4.2 (up to 4.2.7), on Ruby 2.3.4.
67
67
 
68
- Was also previously tested compatible with 2.3.14, 3.0.17, and 3.1.8 on Ruby 2.0.0
69
- or 1.8.7 as appropriate, and may still work for them.
68
+ For earlier versions of Rails, use an older version of the gem.
70
69
  email: will.bryant@gmail.com
71
70
  executables: []
72
71
  extensions: []
@@ -109,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
108
  version: '0'
110
109
  requirements: []
111
110
  rubyforge_project:
112
- rubygems_version: 2.2.2
111
+ rubygems_version: 2.5.2
113
112
  signing_key:
114
113
  specification_version: 4
115
114
  summary: Caches the records from fixed tables, and provides convenience methods to