flex_columns 1.0.2 → 1.0.3

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: acb66f010a776e668841ca7f12dc0854cad5b4c9
4
- data.tar.gz: 91492dda4eab46d062ae8958c62c5e31da33a339
3
+ metadata.gz: 2875989d866789ac577e31552738fa6466139eb0
4
+ data.tar.gz: 0976a94f461db06c5157dce3e0831ff5ce07c1c7
5
5
  SHA512:
6
- metadata.gz: 4469119cb126a4e5767910b6ce4df02037afa3003d735be05baf0a0158ee45c3871b5c29292181c09823ac416184cb2e692f8ad31c9dfd66cea0187d65b5e3d6
7
- data.tar.gz: 6614f83b721f635efa9e80f4f194237436890e66f35a662af0ed100d4d5bc4e90436abe859e89332ecc5f913134903e041bb66332a7d424560e64a5187bf0908
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)
@@ -1,4 +1,4 @@
1
1
  module FlexColumns
2
2
  # The current version of FlexColumns.
3
- VERSION = "1.0.2"
3
+ VERSION = "1.0.3"
4
4
  end
@@ -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.2
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-25 00:00:00.000000000 Z
11
+ date: 2014-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json