flex_columns 1.0.2 → 1.0.3
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/lib/flex_columns/contents/flex_column_contents_base.rb +18 -0
- data/lib/flex_columns/has_flex_columns.rb +43 -1
- data/lib/flex_columns/version.rb +1 -1
- data/spec/flex_columns/system/basic_system_spec.rb +51 -0
- data/spec/flex_columns/unit/contents/flex_column_contents_base_spec.rb +11 -0
- data/spec/flex_columns/unit/has_flex_columns_spec.rb +63 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2875989d866789ac577e31552738fa6466139eb0
|
4
|
+
data.tar.gz: 0976a94f461db06c5157dce3e0831ff5ce07c1c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6605b81107eacb261547aaa2f0f1ebd5110f65b09ff7166c2f15cb104019e2fc696db59dfc72f4e796359b81ed3dca6f62baff7d857d3cfe35535e5fda1b105
|
7
|
+
data.tar.gz: 61c57838d0b1b06a19775e8f00f70edbdecf56d6fc8a4185d5ebed273a40c1cbd91e324e7c797c55fb92b4fa1449dfd2efaf7076a3a3133cee7bc5ea4941830f
|
@@ -71,6 +71,24 @@ module FlexColumns
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
|
74
|
+
# See the comment above FlexColumns::HasFlexColumns#read_attribute_for_serialization -- this is responsible for
|
75
|
+
# correctly turning a flex-column object into a hash for serializing *the entire enclosing ActiveRecord model*.
|
76
|
+
#
|
77
|
+
# Most importantly, this method has NOTHING to do with our internal 'serialize a column as JSON' mechanisms. It
|
78
|
+
# is ONLY called if you try to serialize the enclosing ActiveRecord instance.
|
79
|
+
def to_hash_for_serialization
|
80
|
+
@column_data.to_hash
|
81
|
+
end
|
82
|
+
|
83
|
+
# Make sure this flex-column object itself is smart enough to turn itself into JSON correctly.
|
84
|
+
#
|
85
|
+
# Most importantly, this method has NOTHING to do with our internal 'serialize a column as JSON' mechanisms. It
|
86
|
+
# is ONLY called if you try to serialize something that in turn points directly to (i.e., not via the enclosing
|
87
|
+
# ActiveRecord object) this flex-column object.
|
88
|
+
def as_json(options = { })
|
89
|
+
to_hash_for_serialization
|
90
|
+
end
|
91
|
+
|
74
92
|
# Returns a Hash, appropriate for integration into the payload of an ActiveSupport::Notification call, that
|
75
93
|
# describes the model instance we're created from (or raw String, if that's the case). This is used by the
|
76
94
|
# calls made to ActiveSupport::Notifications when a flex-column object is serialized or deserialized, and is used
|
@@ -66,10 +66,47 @@ module FlexColumns
|
|
66
66
|
out
|
67
67
|
end
|
68
68
|
|
69
|
+
# When ActiveRecord serializes an entire ActiveRecord object (for example, if you call #to_json on it), it
|
70
|
+
# reads each column individually using this method -- which, in the default implementation, just calls #send.
|
71
|
+
# (Well, it's actually *aliased* to #send, but it has the same effect.)
|
72
|
+
#
|
73
|
+
# However, if you're serializing an ActiveRecord model that contains a flex column, you almost certainly just
|
74
|
+
# want that to behave as if the flex_column is a Hash, and serialize it that way. So we override it to do just
|
75
|
+
# that right here.
|
76
|
+
def read_attribute_for_serialization(attribute_name)
|
77
|
+
if self.class._has_flex_column_named?(attribute_name)
|
78
|
+
_flex_column_object_for(attribute_name).to_hash_for_serialization
|
79
|
+
else
|
80
|
+
super(attribute_name)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# See above. We're actually overriding *both* methods, because #read_attribute_for_serialization doesn't exist
|
85
|
+
# in ActiveRecord 3.0.x.
|
86
|
+
def as_json(options = { })
|
87
|
+
flex_column_names = self.class._all_flex_column_names
|
88
|
+
out = super(:except => flex_column_names)
|
89
|
+
|
90
|
+
flex_columns_hash = { }
|
91
|
+
flex_column_names.each do |column_name|
|
92
|
+
hash = _flex_column_object_for(column_name).to_hash_for_serialization
|
93
|
+
flex_columns_hash[column_name] = hash unless hash.empty?
|
94
|
+
end
|
95
|
+
|
96
|
+
if include_root_in_json && out.keys.length == 1
|
97
|
+
out[out.keys.first].merge!(flex_columns_hash)
|
98
|
+
else
|
99
|
+
out.merge!(flex_columns_hash)
|
100
|
+
end
|
101
|
+
|
102
|
+
out
|
103
|
+
end
|
104
|
+
|
69
105
|
# When you reload a model object, we should reload its flex-column objects, too.
|
70
106
|
def reload(*args)
|
71
|
-
super(*args)
|
107
|
+
out = super(*args)
|
72
108
|
@_flex_column_objects = { }
|
109
|
+
out
|
73
110
|
end
|
74
111
|
|
75
112
|
# This little-know ActiveRecord method gets called to produce a string for #inspect for a particular attribute.
|
@@ -114,6 +151,11 @@ module FlexColumns
|
|
114
151
|
_flex_column_classes.map(&:column_name)
|
115
152
|
end
|
116
153
|
|
154
|
+
# Does this model have a flex column with the given name?
|
155
|
+
def _has_flex_column_named?(column_name)
|
156
|
+
_all_flex_column_names.include?(_flex_column_normalize_name(column_name))
|
157
|
+
end
|
158
|
+
|
117
159
|
# Normalizes the name of a flex column, so we're consistent when using it for things like hash keys, no matter
|
118
160
|
# how the client specifies it to us.
|
119
161
|
def _flex_column_normalize_name(flex_column_name)
|
data/lib/flex_columns/version.rb
CHANGED
@@ -250,6 +250,57 @@ describe "FlexColumns basic operations" do
|
|
250
250
|
output['foo'].should == 47.2
|
251
251
|
end
|
252
252
|
|
253
|
+
it "should allow you to call #reload, and return that same record" do
|
254
|
+
user = ::User.new
|
255
|
+
user.name = 'User 1'
|
256
|
+
user.wants_email = [ 1, 2, 3 ]
|
257
|
+
user.save!
|
258
|
+
|
259
|
+
user.name = 'User 2'
|
260
|
+
user.wants_email = 'bonko'
|
261
|
+
|
262
|
+
new_user = user.reload
|
263
|
+
new_user.name.should == 'User 1'
|
264
|
+
new_user.wants_email.should == [ 1, 2, 3 ]
|
265
|
+
new_user.id.should == user.id
|
266
|
+
|
267
|
+
user.name.should == 'User 1'
|
268
|
+
user.wants_email.should == [ 1, 2, 3 ]
|
269
|
+
|
270
|
+
new_user.should be(user)
|
271
|
+
end
|
272
|
+
|
273
|
+
# JSON.dump() simply doesn't work at all with ActiveRecord objects in earlier ActiveRecord versions; see:
|
274
|
+
# http://stackoverflow.com/questions/8406924/json-dump-on-any-activerecord-object-fails
|
275
|
+
if ActiveRecord::VERSION::MAJOR >= 4 ||
|
276
|
+
(ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR >= 1)
|
277
|
+
it "should let you turn an entire ActiveRecord object into JSON properly, treating a flex column as a Hash" do
|
278
|
+
user = ::User.new
|
279
|
+
user.id = 12345
|
280
|
+
user.name = 'User 1'
|
281
|
+
user.wants_email = [ 1, 2, 3 ]
|
282
|
+
|
283
|
+
$stderr.puts "ATTRIBUTES: #{user.attributes.keys.sort.inspect}"
|
284
|
+
|
285
|
+
user_json = JSON.dump(user)
|
286
|
+
parsed = JSON.parse(user_json)
|
287
|
+
|
288
|
+
# ActiveRecord::Base.include_root_in_json may default to different things in different versions of
|
289
|
+
# ActiveRecord; here, we accept JSON in either format.
|
290
|
+
base = parsed
|
291
|
+
base = parsed['user'] if base.keys == %w{user}
|
292
|
+
|
293
|
+
base.keys.sort.should == %w{id name user_attributes more_attributes}.sort
|
294
|
+
base['id'].should == 12345
|
295
|
+
base['name'].should == 'User 1'
|
296
|
+
h = base['user_attributes']
|
297
|
+
h.class.should == Hash
|
298
|
+
h.keys.sort.should == %w{wants_email}
|
299
|
+
h['wants_email'].should == [ 1, 2, 3 ]
|
300
|
+
[ nil, { } ].include?(base['more_attributes']).should be
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
253
304
|
it "should remove keys entirely when they're set to nil, but not if they're set to false" do
|
254
305
|
define_model_class(:User, 'flexcols_spec_users') do
|
255
306
|
flex_column :user_attributes do
|
@@ -173,6 +173,17 @@ describe FlexColumns::Contents::FlexColumnContentsBase do
|
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
+
describe "JSON serialization" do
|
177
|
+
it "should return ColumnData#to_hash_for_serialization for #to_hash_for_serialization and #as_json" do
|
178
|
+
expect_column_data_creation(@json_string)
|
179
|
+
@instance = @klass.new(@model_instance)
|
180
|
+
|
181
|
+
allow(@column_data).to receive(:to_hash).and_return({ :a => :b, :c => :d })
|
182
|
+
@instance.to_hash_for_serialization.should == { :a => :b, :c => :d }
|
183
|
+
@instance.as_json.should == { :a => :b, :c => :d }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
176
187
|
describe "#before_save!" do
|
177
188
|
it "should do nothing if created with a raw string" do
|
178
189
|
expect_column_data_creation(@json_string)
|
@@ -163,6 +163,57 @@ describe FlexColumns::HasFlexColumns do
|
|
163
163
|
@klass.flex_column(:bar)
|
164
164
|
end
|
165
165
|
|
166
|
+
describe "#read_attribute_for_serialization" do
|
167
|
+
it "should call through to the flex-column object for flex columns, and flex columns only" do
|
168
|
+
@superclass.class_eval do
|
169
|
+
def read_attribute_for_serialization(attribute_name)
|
170
|
+
"rafs_#{attribute_name}_rafs"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
instance = @klass.new
|
175
|
+
|
176
|
+
fcc_foo_instance = double("fcc_foo_instance")
|
177
|
+
expect(@fcc_foo).to receive(:new).once.with(instance).and_return(fcc_foo_instance)
|
178
|
+
hash_for_serialization = double("hash_for_serialization")
|
179
|
+
expect(fcc_foo_instance).to receive(:to_hash_for_serialization).once.with().and_return(hash_for_serialization)
|
180
|
+
|
181
|
+
instance.read_attribute_for_serialization('foo').should be(hash_for_serialization)
|
182
|
+
instance.read_attribute_for_serialization('baz').should == "rafs_baz_rafs"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#as_json" do
|
187
|
+
it "should call through to the flex-column object for flex columns, and flex columns only" do
|
188
|
+
@superclass.class_eval do
|
189
|
+
def as_json(options)
|
190
|
+
@superclass_as_json_options ||= [ ]
|
191
|
+
@superclass_as_json_options << options
|
192
|
+
{ :z => 123, :bbb => 456}
|
193
|
+
end
|
194
|
+
end
|
195
|
+
instance = @klass.new
|
196
|
+
|
197
|
+
fcc_foo_instance = double("fcc_foo_instance")
|
198
|
+
expect(@fcc_foo).to receive(:new).once.with(instance).and_return(fcc_foo_instance)
|
199
|
+
expect(fcc_foo_instance).to receive(:to_hash_for_serialization).once.with().and_return({ :aaa => 111, :bbb => 222 })
|
200
|
+
|
201
|
+
fcc_bar_instance = double("fcc_bar_instance")
|
202
|
+
expect(@fcc_bar).to receive(:new).once.with(instance).and_return(fcc_bar_instance)
|
203
|
+
expect(fcc_bar_instance).to receive(:to_hash_for_serialization).once.with().and_return({ :aaa => 234, :ccc => 'xxx' })
|
204
|
+
|
205
|
+
allow(instance).to receive(:include_root_in_json).with().and_return(false)
|
206
|
+
|
207
|
+
instance.as_json.should == { :z => 123, :bbb => 456,
|
208
|
+
:foo => { :aaa => 111, :bbb => 222 },
|
209
|
+
:bar => { :aaa => 234, :ccc => 'xxx' }
|
210
|
+
}
|
211
|
+
instance.instance_variable_get("@superclass_as_json_options").should == [
|
212
|
+
{ :except => [ :foo, :bar ] }
|
213
|
+
]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
166
217
|
it "should return the same DynamicMethodsModule every time" do
|
167
218
|
@klass._flex_column_dynamic_methods_module.should be(@dmm)
|
168
219
|
@klass._flex_column_dynamic_methods_module.should be(@dmm)
|
@@ -282,11 +333,12 @@ describe FlexColumns::HasFlexColumns do
|
|
282
333
|
instance._flex_column_object_for(' bAr ').should be(fcc_bar_instance)
|
283
334
|
end
|
284
335
|
|
285
|
-
it "should re-create flex-column objects on reload, and call super" do
|
336
|
+
it "should re-create flex-column objects on reload, and call super and return its value" do
|
286
337
|
@superclass.class_eval do
|
287
338
|
def reload
|
288
339
|
@reloads ||= 0
|
289
340
|
@reloads += 1
|
341
|
+
:reload_return_yo
|
290
342
|
end
|
291
343
|
|
292
344
|
def reloads
|
@@ -308,7 +360,7 @@ describe FlexColumns::HasFlexColumns do
|
|
308
360
|
instance._flex_column_object_for(:bar).should be(fcc_bar_instance)
|
309
361
|
|
310
362
|
instance.reloads.should == 0
|
311
|
-
instance.reload
|
363
|
+
instance.reload.should == :reload_return_yo
|
312
364
|
instance.reloads.should == 1
|
313
365
|
|
314
366
|
fcc_foo_instance_2 = double("fcc_foo_instance_2")
|
@@ -324,6 +376,15 @@ describe FlexColumns::HasFlexColumns do
|
|
324
376
|
@klass._all_flex_column_names.sort_by(&:to_s).should == [ :foo, :bar ].sort_by(&:to_s)
|
325
377
|
end
|
326
378
|
|
379
|
+
it "should answer whether a flex-column name has been defined" do
|
380
|
+
@klass._has_flex_column_named?(:foo).should be
|
381
|
+
@klass._has_flex_column_named?('foo').should be
|
382
|
+
@klass._has_flex_column_named?(:bar).should be
|
383
|
+
@klass._has_flex_column_named?('bar').should be
|
384
|
+
@klass._has_flex_column_named?(:baz).should_not be
|
385
|
+
@klass._has_flex_column_named?('baz').should_not be
|
386
|
+
end
|
387
|
+
|
327
388
|
it "should normalize column names properly" do
|
328
389
|
@klass._flex_column_normalize_name(:baz).should == :baz
|
329
390
|
@klass._flex_column_normalize_name(:' bAz ').should == :baz
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flex_columns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Geweke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|