constant_table_saver 4.2.0 → 5.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 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