columns_on_demand 4.3.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: 8ea8794dded2d78daf705ceaab6df85b8a150fcb
4
- data.tar.gz: 0549493f7b65d99480413e6a24d5016d76984251
3
+ metadata.gz: c49cd47105f630d84e2f6ad368006311ee60c3c3
4
+ data.tar.gz: c239c30034961fd54de59f856c101c027d21bb36
5
5
  SHA512:
6
- metadata.gz: 7f984d613b9eeccb2d14fab83e1dd6948a970d3b482876e6095a6605674bf80b8878afd4ec19b8206a4c830341e3b05ac52a7fb30d40f08ff7edfb8198e630da
7
- data.tar.gz: ae1a56951d46b3703d3156d2275a099a0de958139c6b978c5805064ce28f7b2ddb361458b6462388346cf580de233bfbc95d9f1228694bd5989e40095a80f70f
6
+ metadata.gz: 3408bb8a756cd559c996ca3afdb7b7994a9137bc94ae19e97b365f12c845da1d154353e6b1900b28e631cee76d8cb73289bffe09f46bd71300d560a5be94ff17
7
+ data.tar.gz: 3ffd5d76e1fc8f8ce5a260927faa6dd022c6d151bea1c72e0fea4e7c8804f81443f265e6b89960747d38d4d7d4690e5d101a635fc5deb16e399c91b312adafa3
data/README.md CHANGED
@@ -21,10 +21,9 @@ Compatibility
21
21
 
22
22
  Supports mysql, mysql2, postgresql, and sqlite3.
23
23
 
24
- Currently tested against Rails 4.2.3, 4.1.12, 4.0.13, 3.2.18, on Ruby 2.1.5.
24
+ Currently tested against Rails 5.1 (up to 5.1.0beta1), 5.0 (up to 5.0.2), and 4.2 (up to 4.2.7), on Ruby 2.3.4.
25
25
 
26
- Was also previously tested compatible with 2.3.14, 3.0.17, and 3.1.8 on Ruby 2.0.0
27
- or 1.8.7 as appropriate, and may still work for them.
26
+ For earlier versions of Rails, use an older version of the gem.
28
27
 
29
28
 
30
29
  Example
@@ -45,4 +45,5 @@ EOF
45
45
  gem.add_development_dependency "mysql2"
46
46
  gem.add_development_dependency "pg"
47
47
  gem.add_development_dependency "sqlite3"
48
+ gem.add_development_dependency "byebug"
48
49
  end
@@ -5,30 +5,11 @@ module ColumnsOnDemand
5
5
  self.columns_to_load_on_demand = columns_to_load_on_demand.empty? ? blob_and_text_columns : columns_to_load_on_demand.collect(&:to_s)
6
6
 
7
7
  extend ClassMethods
8
- include InstanceMethods
8
+ prepend InstanceMethods
9
9
 
10
10
  class <<self
11
- unless ActiveRecord::VERSION::MAJOR > 3 ||
12
- (ActiveRecord.const_defined?(:AttributeMethods) &&
13
- ActiveRecord::AttributeMethods::const_defined?(:Serialization) &&
14
- ActiveRecord::AttributeMethods::Serialization::const_defined?(:Attribute))
15
- alias_method_chain :define_read_method_for_serialized_attribute, :columns_on_demand
16
- end
17
- alias_method_chain :reset_column_information, :columns_on_demand
18
- end
19
- alias_method_chain :attributes, :columns_on_demand
20
- alias_method_chain :attribute_names, :columns_on_demand
21
- alias_method_chain :read_attribute, :columns_on_demand
22
- alias_method_chain :read_attribute_before_type_cast, :columns_on_demand
23
- if ActiveRecord::AttributeMethods::Read.instance_methods.include?(:_read_attribute)
24
- alias_method_chain :_read_attribute, :columns_on_demand
25
- end
26
- alias_method_chain :missing_attribute, :columns_on_demand
27
- alias_method_chain :reload, :columns_on_demand
28
- if ActiveRecord::AttributeMethods::Dirty.instance_methods.include?(:attribute_changed_in_place?)
29
- alias_method_chain :attribute_changed_in_place?, :columns_on_demand
30
- elsif ActiveRecord::AttributeMethods::Dirty.instance_methods.include?(:changed_attributes)
31
- alias_method_chain :changed_in_place?, :columns_on_demand
11
+ alias reset_column_information_without_columns_on_demand reset_column_information
12
+ alias reset_column_information reset_column_information_with_columns_on_demand
32
13
  end
33
14
  end
34
15
 
@@ -36,12 +17,6 @@ module ColumnsOnDemand
36
17
  @columns_to_select = nil
37
18
  reset_column_information_without_columns_on_demand
38
19
  end
39
-
40
- def define_read_method_for_serialized_attribute_with_columns_on_demand(attr_name)
41
- define_read_method_for_serialized_attribute_without_columns_on_demand(attr_name)
42
- scope = method_defined?(:generated_attribute_methods) ? generated_attribute_methods : self
43
- scope.module_eval("def #{attr_name}_with_columns_on_demand; ensure_loaded('#{attr_name}'); #{attr_name}_without_columns_on_demand; end; alias_method_chain :#{attr_name}, :columns_on_demand", __FILE__, __LINE__)
44
- end
45
20
 
46
21
  def blob_and_text_columns
47
22
  columns.inject([]) do |blob_and_text_columns, column|
@@ -72,13 +47,13 @@ module ColumnsOnDemand
72
47
  !columns_to_load_on_demand.include?(attr_name) || @attributes.key?(attr_name) || new_record? || columns_loaded.include?(attr_name)
73
48
  end
74
49
 
75
- def attributes_with_columns_on_demand
50
+ def attributes
76
51
  load_attributes(*columns_to_load_on_demand.reject {|attr_name| column_loaded?(attr_name)})
77
- attributes_without_columns_on_demand
52
+ super
78
53
  end
79
54
 
80
- def attribute_names_with_columns_on_demand
81
- (attribute_names_without_columns_on_demand + columns_to_load_on_demand).uniq.sort
55
+ def attribute_names
56
+ (super + columns_to_load_on_demand).uniq.sort
82
57
  end
83
58
 
84
59
  def load_attributes(*attr_names)
@@ -87,42 +62,15 @@ module ColumnsOnDemand
87
62
  values = self.class.connection.select_rows(
88
63
  "SELECT #{attr_names.collect {|attr_name| self.class.connection.quote_column_name(attr_name)}.join(", ")}" +
89
64
  " FROM #{self.class.quoted_table_name}" +
90
- " WHERE #{self.class.connection.quote_column_name(self.class.primary_key)} = #{self.class.quote_value(id, self.class.columns_hash[self.class.primary_key])}")
65
+ " WHERE #{self.class.connection.quote_column_name(self.class.primary_key)} = #{self.class.connection.quote(id)}")
91
66
  row = values.first || raise(ActiveRecord::RecordNotFound, "Couldn't find #{self.class.name} with ID=#{id}")
92
67
 
93
68
  attr_names.each_with_index do |attr_name, i|
94
69
  columns_loaded << attr_name
95
70
  value = row[i]
96
71
 
97
- if @attributes.respond_to?(:write_from_database)
98
- # activerecord 4.2 or later, which make it easy to replicate the normal typecasting and deserialization logic
99
- @attributes.write_from_database(attr_name, value)
100
-
101
- elsif coder = self.class.serialized_attributes[attr_name]
102
- # activerecord 4.1 or earlier, with a serialized column
103
- # for some database adapters, @column_types_override gets populated with type data from query used to load the record originally.
104
- # this is fine, but unfortunately some special-case "decorate_columns" code in ActiveRecord will wrap those types in serialization
105
- # objects, and it does this for each column listed in @serialized_column_names *even if they are not present in the query results*.
106
- # as a result it unfortunately overrides the normal @column_type with a @column_type_override with a nil @column, which explodes
107
- # when it tries to run the typecast. make it use the normal @column_type value, since we know that we've loading the regular column.
108
- @column_types_override.delete(attr_name) if @column_types_override
109
-
110
- if ActiveRecord.const_defined?(:AttributeMethods) &&
111
- ActiveRecord::AttributeMethods::const_defined?(:Serialization) &&
112
- ActiveRecord::AttributeMethods::Serialization::const_defined?(:Attribute)
113
- # in 3.2 @attributes has a special Attribute struct to help cache both serialized and unserialized forms
114
- @attributes[attr_name] = ActiveRecord::AttributeMethods::Serialization::Attribute.new(coder, value, :serialized)
115
- elsif ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1
116
- # from 3.1 it has the deserialized form
117
- @attributes[attr_name] = coder.load value
118
- else
119
- # in 2.3 an 3.0, @attributes has the serialized form
120
- @attributes[attr_name] = value
121
- end
122
- else
123
- # activerecord 4.1 or earlier, with a regular unserialized column
124
- @attributes[attr_name] = value
125
- end
72
+ # activerecord 4.2 or later, which make it easy to replicate the normal typecasting and deserialization logic
73
+ @attributes.write_from_database(attr_name, value)
126
74
  end
127
75
  end
128
76
 
@@ -130,39 +78,39 @@ module ColumnsOnDemand
130
78
  load_attributes(attr_name.to_s) unless column_loaded?(attr_name.to_s)
131
79
  end
132
80
 
133
- def changed_in_place_with_columns_on_demand?(attr_name)
134
- column_loaded?(attr_name) && changed_in_place_without_columns_on_demand?(attr_name)
81
+ def changed_in_place?(attr_name)
82
+ column_loaded?(attr_name) && super(attr_name)
135
83
  end
136
84
 
137
- def attribute_changed_in_place_with_columns_on_demand?(attr_name)
138
- column_loaded?(attr_name) && attribute_changed_in_place_without_columns_on_demand?(attr_name)
85
+ def attribute_changed_in_place?(attr_name)
86
+ column_loaded?(attr_name) && super(attr_name)
139
87
  end
140
88
 
141
- def read_attribute_with_columns_on_demand(attr_name, &block)
89
+ def read_attribute(attr_name, &block)
142
90
  ensure_loaded(attr_name)
143
- read_attribute_without_columns_on_demand(attr_name, &block)
91
+ super(attr_name, &block)
144
92
  end
145
93
 
146
- def read_attribute_before_type_cast_with_columns_on_demand(attr_name)
94
+ def read_attribute_before_type_cast(attr_name)
147
95
  ensure_loaded(attr_name)
148
- read_attribute_before_type_cast_without_columns_on_demand(attr_name)
96
+ super(attr_name)
149
97
  end
150
98
 
151
- def _read_attribute_with_columns_on_demand(attr_name, &block)
99
+ def _read_attribute(attr_name, &block)
152
100
  ensure_loaded(attr_name)
153
- _read_attribute_without_columns_on_demand(attr_name, &block)
101
+ super(attr_name, &block)
154
102
  end
155
103
 
156
- def missing_attribute_with_columns_on_demand(attr_name, *args)
104
+ def missing_attribute(attr_name, *args)
157
105
  if columns_to_load_on_demand.include?(attr_name)
158
106
  load_attributes(attr_name)
159
107
  else
160
- missing_attribute_without_columns_on_demand(attr_name, *args)
108
+ super(attr_name, *args)
161
109
  end
162
110
  end
163
111
 
164
- def reload_with_columns_on_demand(*args)
165
- reload_without_columns_on_demand(*args).tap do
112
+ def reload(*args)
113
+ super(*args).tap do
166
114
  columns_loaded.clear
167
115
  columns_to_load_on_demand.each do |attr_name|
168
116
  if @attributes.respond_to?(:reset)
@@ -177,37 +125,16 @@ module ColumnsOnDemand
177
125
  end
178
126
  end
179
127
 
180
- module RelationMethodsArity2
181
- def build_select_with_columns_on_demand(arel, selects)
182
- if (selects.empty? || selects == [table[Arel.star]] || selects == ['*']) && klass < ColumnsOnDemand::InstanceMethods
183
- build_select_without_columns_on_demand(arel, [default_select(true)])
184
- else
185
- build_select_without_columns_on_demand(arel, selects)
186
- end
187
- end
188
- end
189
-
190
- module RelationMethodsArity1
191
- def build_select_with_columns_on_demand(arel)
128
+ module RelationMethods
129
+ def build_select(arel)
192
130
  if (select_values.empty? || select_values == [table[Arel.star]] || select_values == ['*']) && klass < ColumnsOnDemand::InstanceMethods
193
131
  arel.project(*arel_columns([default_select(true)]))
194
132
  else
195
- build_select_without_columns_on_demand(arel)
133
+ super(arel)
196
134
  end
197
135
  end
198
136
  end
199
137
  end
200
138
 
201
139
  ActiveRecord::Base.send(:extend, ColumnsOnDemand::BaseMethods)
202
-
203
- if ActiveRecord.const_defined?(:Relation)
204
- if ActiveRecord::Relation.instance_method(:build_select).arity == 1
205
- # 4.2.1 and above
206
- ActiveRecord::Relation.send(:include, ColumnsOnDemand::RelationMethodsArity1)
207
- else
208
- # 4.2.0 and below
209
- ActiveRecord::Relation.send(:include, ColumnsOnDemand::RelationMethodsArity2)
210
- end
211
-
212
- ActiveRecord::Relation.alias_method_chain :build_select, :columns_on_demand
213
- end
140
+ ActiveRecord::Relation.send(:prepend, ColumnsOnDemand::RelationMethods)
@@ -1,3 +1,3 @@
1
1
  module ColumnsOnDemand
2
- VERSION = '4.3.0'
2
+ VERSION = '5.1.0'
3
3
  end
@@ -43,7 +43,11 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
43
43
  end
44
44
 
45
45
  fixtures :all
46
- self.use_transactional_fixtures = true
46
+ if respond_to?(:use_transactional_tests=)
47
+ self.use_transactional_tests = true
48
+ else
49
+ self.use_transactional_fixtures = true
50
+ end
47
51
 
48
52
  test "it lists explicitly given columns for loading on demand" do
49
53
  assert_equal ["file_data", "processing_log", "original_filename"], Explicit.columns_to_load_on_demand
@@ -54,11 +58,11 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
54
58
  end
55
59
 
56
60
  test "it selects all the other columns for loading eagerly" do
57
- assert_match /\W*id\W*, \W*results\W*, \W*processed_at\W*/, Explicit.default_select(false)
58
- assert_match /\W*explicits\W*.results/, Explicit.default_select(true)
61
+ assert_match(/\W*id\W*, \W*results\W*, \W*processed_at\W*/, Explicit.default_select(false))
62
+ assert_match(/\W*explicits\W*.results/, Explicit.default_select(true))
59
63
 
60
- assert_match /\W*id\W*, \W*original_filename\W*, \W*processed_at\W*/, Implicit.default_select(false)
61
- assert_match /\W*implicits\W*.original_filename/, Implicit.default_select(true)
64
+ assert_match(/\W*id\W*, \W*original_filename\W*, \W*processed_at\W*/, Implicit.default_select(false))
65
+ assert_match(/\W*implicits\W*.original_filename/, Implicit.default_select(true))
62
66
  end
63
67
 
64
68
  test "it doesn't load the columns_to_load_on_demand straight away when finding the records" do
@@ -92,10 +96,10 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
92
96
 
93
97
  record = Implicit.first
94
98
  assert_not_loaded record, "file_data"
95
- assert_equal nil, record.file_data
99
+ assert_nil record.file_data
96
100
  assert_loaded record, "file_data"
97
101
  assert_no_queries do
98
- assert_equal nil, record.file_data
102
+ assert_nil record.file_data
99
103
  end
100
104
  end
101
105
 
@@ -156,6 +160,7 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
156
160
 
157
161
  assert_not_loaded record, "file_data"
158
162
  assert_equal "New file data", record.file_data
163
+ assert_not_equal old_object_id, record.file_data.object_id
159
164
  end
160
165
 
161
166
  test "it doesn't override custom select() finds" do
@@ -194,42 +199,7 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
194
199
  assert_equal "This is the file data!", record.file_data # check it doesn't raise
195
200
  end
196
201
 
197
- test "it updates the select strings when columns are changed and the column information is reset" do
198
- ActiveRecord::Schema.define(:version => 1) do
199
- create_table :dummies, :force => true do |t|
200
- t.string :some_field
201
- t.binary :big_field
202
- end
203
- end
204
-
205
- class Dummy < ActiveRecord::Base
206
- columns_on_demand
207
- end
208
-
209
- assert_match /\W*id\W*, \W*some_field\W*/, Dummy.default_select(false)
210
-
211
- ActiveRecord::Schema.define(:version => 2) do
212
- create_table :dummies, :force => true do |t|
213
- t.string :some_field
214
- t.binary :big_field
215
- t.string :another_field
216
- end
217
- end
218
-
219
- assert_match /\W*id\W*, \W*some_field\W*/, Dummy.default_select(false)
220
- Dummy.reset_column_information
221
- assert_match /\W*id\W*, \W*some_field\W*, \W*another_field\W*/, Dummy.default_select(false)
222
- end
223
-
224
202
  test "it handles STI models" do
225
- ActiveRecord::Schema.define(:version => 1) do
226
- create_table :stis, :force => true do |t|
227
- t.string :type
228
- t.string :some_field
229
- t.binary :big_field
230
- end
231
- end
232
-
233
203
  class Sti < ActiveRecord::Base
234
204
  columns_on_demand
235
205
  end
@@ -238,8 +208,8 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
238
208
  columns_on_demand :some_field
239
209
  end
240
210
 
241
- assert_match /\W*id\W*, \W*type\W*, \W*some_field\W*/, Sti.default_select(false)
242
- assert_match /\W*id\W*, \W*type\W*, \W*big_field\W*/, StiChild.default_select(false)
211
+ assert_match(/\W*id\W*, \W*type\W*, \W*some_field\W*/, Sti.default_select(false))
212
+ assert_match(/\W*id\W*, \W*type\W*, \W*big_field\W*/, StiChild.default_select(false))
243
213
  end
244
214
 
245
215
  test "it works on child records loaded from associations" do
@@ -322,3 +292,38 @@ class ColumnsOnDemandTest < ActiveSupport::TestCase
322
292
  assert_equal select_sql, reference_sql
323
293
  end
324
294
  end
295
+
296
+ class ColumnsOnDemandSchemaTest < ActiveSupport::TestCase
297
+ if respond_to?(:use_transactional_tests=)
298
+ self.use_transactional_tests = false
299
+ else
300
+ self.use_transactional_fixtures = false
301
+ end
302
+
303
+ test "it updates the select strings when columns are changed and the column information is reset" do
304
+ ActiveRecord::Schema.define(:version => 1) do
305
+ create_table :dummies, :force => true do |t|
306
+ t.string :some_field
307
+ t.binary :big_field
308
+ end
309
+ end
310
+
311
+ class Dummy < ActiveRecord::Base
312
+ columns_on_demand
313
+ end
314
+
315
+ assert_match(/\W*id\W*, \W*some_field\W*/, Dummy.default_select(false))
316
+
317
+ ActiveRecord::Schema.define(:version => 2) do
318
+ create_table :dummies, :force => true do |t|
319
+ t.string :some_field
320
+ t.binary :big_field
321
+ t.string :another_field
322
+ end
323
+ end
324
+
325
+ assert_match(/\W*id\W*, \W*some_field\W*/, Dummy.default_select(false))
326
+ Dummy.reset_column_information
327
+ assert_match(/\W*id\W*, \W*some_field\W*, \W*another_field\W*/, Dummy.default_select(false))
328
+ end
329
+ end
@@ -27,4 +27,10 @@ ActiveRecord::Schema.define(:version => 0) do
27
27
  create_table :serializings, :force => true do |t|
28
28
  t.binary :data
29
29
  end
30
+
31
+ create_table :stis, :force => true do |t|
32
+ t.string :type
33
+ t.string :some_field
34
+ t.binary :big_field
35
+ end
30
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: columns_on_demand
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.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: 2016-06-07 00:00:00.000000000 Z
11
+ date: 2017-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: |
98
112
  Lazily loads large columns on demand.
99
113
 
@@ -158,7 +172,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
172
  version: '0'
159
173
  requirements: []
160
174
  rubyforge_project:
161
- rubygems_version: 2.2.5
175
+ rubygems_version: 2.5.2
162
176
  signing_key:
163
177
  specification_version: 4
164
178
  summary: Lazily loads large columns on demand.