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 +4 -4
- data/README +2 -3
- data/constant_table_saver.gemspec +2 -3
- data/lib/constant_table_saver/version.rb +1 -1
- data/lib/constant_table_saver.rb +72 -120
- data/test/constant_table_saver_test.rb +1 -53
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0478d2577af80dc5fc4c7bf860321b72c49e2c1
|
4
|
+
data.tar.gz: 27060f22739b2cdb8a0cbf36f56ebe186e9b6333
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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
|
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
|
-
|
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"
|
data/lib/constant_table_saver.rb
CHANGED
@@ -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 ==
|
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
|
-
|
31
|
-
|
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
|
-
|
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
|
+
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:
|
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
|
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
|
-
|
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.
|
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
|