columns_on_demand 4.3.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: 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.