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 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