flex_columns 1.0.1 → 1.0.2

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: d79e3b16e2f818cfee4e0cdf530a78ebc49bfbdd
4
- data.tar.gz: 03593078cb27e5cac1d7b38090147ac5590f7b8f
3
+ metadata.gz: acb66f010a776e668841ca7f12dc0854cad5b4c9
4
+ data.tar.gz: 91492dda4eab46d062ae8958c62c5e31da33a339
5
5
  SHA512:
6
- metadata.gz: 4072eb037511e12a1adfc2d34982568445bfcedb386013d2d50ded737e40f4fa259df9c5a87cf39dff86b4d2c82da01caec0c2055473cfd4d819be501e5b9e10
7
- data.tar.gz: aea2b505ba4af83bbd3408d066402bdded39d4a06309d5ba4b2341a17ad00ccfbd0098bfe7149ba93ecee28c45f7d6c40dde03da22b27d3b56dc02b71d5a1f0e
6
+ metadata.gz: 4469119cb126a4e5767910b6ce4df02037afa3003d735be05baf0a0158ee45c3871b5c29292181c09823ac416184cb2e692f8ad31c9dfd66cea0187d65b5e3d6
7
+ data.tar.gz: 6614f83b721f635efa9e80f4f194237436890e66f35a662af0ed100d4d5bc4e90436abe859e89332ecc5f913134903e041bb66332a7d424560e64a5187bf0908
data/.gitignore CHANGED
@@ -15,4 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
- /spec_database_config.rb
18
+ /spec_database_config.rb*
data/flex_columns.gemspec CHANGED
@@ -45,20 +45,6 @@ Gem::Specification.new do |s|
45
45
 
46
46
  s.add_dependency "activesupport", ">= 3.0", "<= 4.99.99"
47
47
 
48
- ar_import_version = case ar_version
49
- when nil then nil
50
- when 'master', /^4\.0\./ then '~> 0.4.1'
51
- when /^3\.0\./ then '~> 0.2.11'
52
- when /^3\.1\./, /^3\.2\./ then '~> 0.3.1'
53
- else raise "Don't know what activerecord-import version to require for activerecord version #{ar_version.inspect}!"
54
- end
55
-
56
- if ar_import_version
57
- s.add_dependency("activerecord-import", ar_import_version)
58
- else
59
- s.add_dependency("activerecord-import")
60
- end
61
-
62
48
  require File.expand_path(File.join(File.dirname(__FILE__), 'spec', 'flex_columns', 'helpers', 'database_helper'))
63
49
  database_gem_name = FlexColumns::Helpers::DatabaseHelper.maybe_database_gem_name
64
50
 
@@ -80,7 +80,6 @@ module FlexColumns
80
80
 
81
81
  @field_contents_by_field_name = nil
82
82
  @unknown_field_contents_by_key = nil
83
- @touched = false
84
83
  end
85
84
 
86
85
  # Returns the data for the given +field_name+. Raises FlexColumns::Errors::NoSuchFieldError if there is no field
@@ -109,8 +108,6 @@ module FlexColumns
109
108
 
110
109
  old_value = field_contents_by_field_name[field_name]
111
110
 
112
- @touched = true if old_value != new_value
113
-
114
111
  # We deliberately delete from the hash anything that's being set to +nil+; this is so that we don't end up just
115
112
  # binding keys to +nil+, and returning them in #keys, etc. (Yes, this means that you can't distinguish a key
116
113
  # explicitly set to +nil+ from a key that's not present; this is different from Ruby's semantics for a Hash,
@@ -129,17 +126,56 @@ module FlexColumns
129
126
  field_contents_by_field_name.keys
130
127
  end
131
128
 
129
+ # Returns a representation of this data as a Hash. This should *not* be used in +flex_columns+ to manipulate
130
+ # data, as it does not contain a full representation of a column (in particular, unknown-field data is not
131
+ # represented in the returned Hash); however, it's useful to construct a string (e.g.,
132
+ # FlexColumnsContentsBase#inspect) to help with debugging.
133
+ def to_hash
134
+ deserialize_if_necessary!
135
+ field_contents_by_field_name.dup.with_indifferent_access
136
+ end
137
+
132
138
  # Does nothing, other than making sure the JSON has been deserialized. This therefore has the effect both of
133
139
  # ensuring that the stored data (if any) is valid, and also will remove any unknown keys (on save) if
134
140
  # +:unknown_fields+ was set to +:delete+.
135
141
  def touch!
136
142
  deserialize_if_necessary!
137
- @touched = true
138
143
  end
139
144
 
140
- # Has this object been modified in any way?
141
- def touched?
142
- !! @touched
145
+ # Has this object been deserialized? If it's been deserialized, then we need to do things like run validations
146
+ # on it, save it back to the database when someone calls #save! on the parent object, and so on.
147
+ #
148
+ # Not at all obvious: originally, we had a method called #touched? that let you know whether the given object
149
+ # had been changed at all. It simply got set on +#[]=+, above. The problem with this is that very frequently,
150
+ # +flex_columns+ is used to store complex data structures (because that's one of the things that's dramatically
151
+ # easier in a serialized JSON blob than in a traditional relational structure). But if you have an array stored,
152
+ # and you call #<< on it to append an element, then +#[]=+ never gets called at all -- because it's still the
153
+ # same object, just with different contents.
154
+ #
155
+ # We could have worked around this by saving off a copy of each field when we deserialized, then comparing them
156
+ # using a deep equality (#== should work just fine) to determine if they've changed. However, this adds very
157
+ # significant overhead to each and every single use of a +flex_column+ object, whether or not you rely on or
158
+ # care about this kind of tracking -- we would have to #dup every flex column field every single time we
159
+ # deserialized, and, if you have large objects in there, that can get extremely expensive.
160
+ #
161
+ # Since almost every object in Ruby is mutable -- even Strings -- there aren't really any easy wins here.
162
+ # Numbers are the only commonplace object that aren't, and it's not going to be a common use case that someone
163
+ # uses a +flex_column+ with fields that each simply store one single number. (Storing an array or a hash of
164
+ # numbers is much more common, but then you're talking about Arrays and Hashes, which are back to being mutable.)
165
+ #
166
+ # Another option would be to #freeze all of the fields on a flex column, thus requiring clients to reassign them
167
+ # with a new object if they wanted to change them at all. That, however, presents an API that most users would
168
+ # hate -- I don't want to say <tt>user.prefs_map = user.prefs_map.merge(:foo => bar)</tt>; I want to just say
169
+ # <tt>user.prefs_map[:foo] = bar</tt>.
170
+ #
171
+ # Instead, once we deserialize a field, we just assume that it has changed. While this may end up causing the
172
+ # client to do extra work at times, it's much higher-performance than doing the tracking every time.
173
+ #
174
+ # (There is definitely room to add code that would make this configurable, on a per-flex-column or even per-field
175
+ # basis. As always, patches are welcome; as of this writing, it seems likely that it might just not be an issue
176
+ # big enough to worry about.)
177
+ def deserialized?
178
+ !! field_contents_by_field_name
143
179
  end
144
180
 
145
181
  # Returns a String with the current contents of this object as JSON. (This will deserialize from JSON, if it
@@ -181,7 +217,8 @@ module FlexColumns
181
217
  end
182
218
  end
183
219
 
184
- if length_limit && out.length > length_limit
220
+ actual_length = out ? out.length : 0
221
+ if length_limit && actual_length > length_limit
185
222
  raise FlexColumns::Errors::JsonTooLongError.new(data_source, length_limit, out)
186
223
  end
187
224
 
@@ -345,7 +382,7 @@ module FlexColumns
345
382
  # If we haven't yet deserialized the JSON string, do it now, and store the data appropriately. This also
346
383
  # checks for a validly-encoded string.
347
384
  def deserialize_if_necessary!
348
- unless field_contents_by_field_name
385
+ unless deserialized?
349
386
  raw_data = storage_string || ''
350
387
 
351
388
  # PostgreSQL's JSON data type, combined with recent-enough adapters and ActiveRecord, will return JSON as a
@@ -108,23 +108,18 @@ module FlexColumns
108
108
  column_data[field_name] = new_value
109
109
  end
110
110
 
111
- # A flex column has been "touched" if it has had at least one field changed to a different value than it had
112
- # before, or if someone has called #touch! on it. If a column has not been touched, validations are not run on it,
113
- # nor is it re-serialized back out to the database on save!. Generally, this is a good thing: it increases
114
- # performance substantially for times when you haven't actually changed the flex column's contents at all. It does
115
- # mean that invalid data won't be detected and unknown fields won't be removed (if you've specified
116
- # <tt>:unknown_fields => delete</tt>), however.
117
- #
118
- # There may be times, however, when you want to make sure the column is stored back out (including removing any
119
- # unknown fields, if you selected that option), or to make sure that validations get run, no matter what.
120
- # In this case, you can call #touch!.
111
+ # Sometimes you want to deserialize a flex column explicitly, without actually changing anything in it. (For
112
+ # example, if you set <tt>:unknown_fields => :delete</tt>, then unknown fields are removed from a column only if
113
+ # it has been deserialized before you save it.) While you could accomplish this by simply accessing any field
114
+ # of the column, it's cleaner and more clear what you're doing to just call this method.
121
115
  def touch!
122
116
  column_data.touch!
123
117
  end
124
118
 
125
- # Has at least one field in the column been changed, or has someone called #touch! ?
126
- def touched?
127
- column_data.touched?
119
+ # Has the column been deserialized? A column is deserialized if someone has tried to read from or write to it,
120
+ # or if someone has called #touch!.
121
+ def deserialized?
122
+ column_data.deserialized?
128
123
  end
129
124
 
130
125
  # Called via the ActiveRecord::Base#before_validation hook that gets installed on the enclosing model instance.
@@ -139,6 +134,25 @@ module FlexColumns
139
134
  end
140
135
  end
141
136
 
137
+ INSPECT_MAXIMUM_LENGTH_FOR_ANY_ATTRIBUTE_VALUE = 100
138
+
139
+ # **NOTE**: This method *WILL* deserialize the contents of the column, if it hasn't already been deserialized.
140
+ # This is extremely useful for debugging, and almost certainly what you want, but if, for some reason, you
141
+ # call #inspect on every single instance of a flex-column you get back from the database, you'll incur a
142
+ # needless performance penalty. You have been warned.
143
+ def inspect
144
+ string_hash = { }
145
+ column_data.to_hash.each do |k,v|
146
+ v_string = v.to_s
147
+ if v_string.length > INSPECT_MAXIMUM_LENGTH_FOR_ANY_ATTRIBUTE_VALUE
148
+ v_string = "#{v_string[0..(INSPECT_MAXIMUM_LENGTH_FOR_ANY_ATTRIBUTE_VALUE - 1)]}..."
149
+ end
150
+ string_hash[k] = v_string
151
+ end
152
+
153
+ "<#{self.class.name}: #{string_hash.inspect}>"
154
+ end
155
+
142
156
  # Returns a JSON string representing the current contents of this flex column. Note that this is _not_ always
143
157
  # exactly what gets stored in the database, because of binary columns and compression; for that, use
144
158
  # #to_stored_data, below.
@@ -146,11 +146,11 @@ module FlexColumns
146
146
 
147
147
  # Given a model instance, do we need to save this column? This is true under one of two cases:
148
148
  #
149
- # * Someone has changed ("touched") at least one of the flex-column fields (or called #touch! on it);
149
+ # * Someone has deserialized the column by accessing it (or calling #touch! on it);
150
150
  # * The column is non-NULL, and there's no data in it right now. (Saving it will populate it with an empty string.)
151
151
  def requires_serialization_on_save?(model)
152
152
  maybe_flex_object = model._flex_column_object_for(column_name, false)
153
- out = true if maybe_flex_object && maybe_flex_object.touched?
153
+ out = true if maybe_flex_object && maybe_flex_object.deserialized?
154
154
  out ||= true if ((! column.null) && (! model[column_name]))
155
155
  out
156
156
  end
@@ -38,7 +38,7 @@ module FlexColumns
38
38
  # whether you've changed that particular attribute or not.
39
39
  def _flex_columns_before_validation!
40
40
  _all_present_flex_column_objects.each do |flex_column_object|
41
- flex_column_object.before_validation! if flex_column_object.touched?
41
+ flex_column_object.before_validation!
42
42
  end
43
43
  end
44
44
 
@@ -72,6 +72,25 @@ module FlexColumns
72
72
  @_flex_column_objects = { }
73
73
  end
74
74
 
75
+ # This little-know ActiveRecord method gets called to produce a string for #inspect for a particular attribute.
76
+ # Because the default implementation uses #read_attribute, if we don't override it, it will simply return our
77
+ # actual string in the database; if this is compressed data, this is meaningless to a programmer. So we override
78
+ # this to instead deserialize the column and call #inspect on the actual FlexColumnContentsBase object, which
79
+ # shows interesting information.
80
+ #
81
+ # **NOTE**: See the warning comment above FlexColumnContentsBase#inspect, which points out that this will
82
+ # deserialize the column if it hasn't already -- so calling this has a performance penalty. This should be fine,
83
+ # since calling #inspect in bulk isn't something a program should be doing in production mode anyway, but it's
84
+ # worth noting.
85
+ def attribute_for_inspect(attr_name)
86
+ cn = self.class._all_flex_column_names
87
+ if cn.include?(attr_name.to_sym)
88
+ _flex_column_object_for(attr_name).inspect
89
+ else
90
+ super(attr_name)
91
+ end
92
+ end
93
+
75
94
  private
76
95
  # Returns the Hash that we keep flex-column objects in, indexed by column name.
77
96
  def _flex_column_objects
@@ -1,4 +1,4 @@
1
1
  module FlexColumns
2
2
  # The current version of FlexColumns.
3
- VERSION = "1.0.1"
3
+ VERSION = "1.0.2"
4
4
  end
@@ -40,6 +40,57 @@ describe "FlexColumns basic operations" do
40
40
  user2.user_attributes.keys.should == [ :wants_email ]
41
41
  end
42
42
 
43
+ it "shouldn't complain if there is no data, but you still touch a field" do
44
+ user = ::User.new
45
+ user.name = 'User 1'
46
+ user.user_attributes.wants_email
47
+ user.save!
48
+ end
49
+
50
+ # This test case was created from a found bug: we were assuming that unless you called "#{method}=" on one of
51
+ # the attributes of a flex column, then we didn't need to serialize and save the flex column. However, that's not
52
+ # true, for exactly the reasons seen below (maybe you modified an object that was referred to from the field,
53
+ # but didn't change that field itself). See also the comment above ColumnData#deserialized?.
54
+ it "should still save its data even if you change something nested down deep" do
55
+ user = ::User.new
56
+ user.name = 'User 1'
57
+ user.user_attributes.wants_email = { 'foo' => { 'bar' => [ 1, 2, 3 ] } }
58
+ user.save!
59
+
60
+ user_again = ::User.find(user.id)
61
+ user_again.user_attributes.wants_email['foo']['bar'] << 4
62
+ user_again.save!
63
+
64
+ user_yet_again = ::User.find(user.id)
65
+ user_yet_again.user_attributes.wants_email['foo']['bar'].should == [ 1, 2, 3, 4 ]
66
+ end
67
+
68
+ it "should return useful data for the column on #inspect, deserializing if necessary" do
69
+ user = ::User.new
70
+ user.name = 'User 1'
71
+ user.wants_email = 'whatEVER, yo'
72
+ user.save!
73
+
74
+ user_again = ::User.find(user.id)
75
+ s = user_again.user_attributes.inspect
76
+ s.should match(/UserAttributesFlexContents/i)
77
+ s.should match(/wants_email/i)
78
+ s.should match(/whatEVER, yo/)
79
+ end
80
+
81
+ it "should return useful data for the column on #inspect from the parent AR model" do
82
+ user = ::User.new
83
+ user.name = 'User 1'
84
+ user.wants_email = 'whatEVER, yo'
85
+ user.save!
86
+
87
+ user_again = ::User.find(user.id)
88
+ s = user_again.inspect
89
+ s.should match(/UserAttributesFlexContents/i)
90
+ s.should match(/wants_email/i)
91
+ s.should match(/whatEVER, yo/)
92
+ end
93
+
43
94
  it "should store its data as standard JSON" do
44
95
  user = ::User.new
45
96
  user.name = 'User 1'
@@ -60,7 +111,7 @@ describe "FlexColumns basic operations" do
60
111
  contents['wants_email'].should == 'sometimes'
61
112
  end
62
113
 
63
- it "should not modify that JSON if you don't write to it with a different value, but should if you touch it" do
114
+ it "should not modify that JSON if you don't touch it, but should if you do" do
64
115
  define_model_class(:UserBackdoor, 'flexcols_spec_users') { }
65
116
 
66
117
  weirdly_spaced_json = ' { "wants_email" : "boop" } '
@@ -72,15 +123,7 @@ describe "FlexColumns basic operations" do
72
123
 
73
124
  user = ::User.find(user_bd.id)
74
125
  user.name.should == 'User 1'
75
- user.wants_email.should == 'boop'
76
- user.wants_email = 'boop'
77
- user.save!
78
-
79
- user_bd_again = ::UserBackdoor.find(user_bd.id)
80
- user_bd_again.name.should == 'User 1'
81
- user_bd_again.user_attributes.should == weirdly_spaced_json
82
-
83
- user.user_attributes.touch!
126
+ user.wants_email
84
127
  user.save!
85
128
 
86
129
  user_bd_again = ::UserBackdoor.find(user_bd.id)
@@ -99,6 +99,33 @@ describe "FlexColumns compression operations" do
99
99
  data.should_not match(/bar/)
100
100
  end
101
101
 
102
+ describe "#inspect" do
103
+ it "should decompress and deserialize data for #inspect on the column itself, but should abbreviate" do
104
+ user = ::User.new
105
+ user.name = 'User 1'
106
+ user.foo = 'foo' * 1000
107
+ user.save!
108
+
109
+ user_again = ::User.find(user.id)
110
+ s = user_again.user_attributes.inspect
111
+ s.should match(/UserAttributesFlexContents/i)
112
+ s.should match(/foofoofoofoo/i)
113
+ s.length.should < 1000
114
+ end
115
+
116
+ it "should decompress and deserialize data for #inspect on the parent, too" do
117
+ user = ::User.new
118
+ user.name = 'User 1'
119
+ user.foo = 'foo' * 1000
120
+ user.save!
121
+
122
+ user_again = ::User.find(user.id)
123
+ s = user_again.inspect
124
+ s.should match(/UserAttributesFlexContents/i)
125
+ s.should match(/foofoofoofoo/i)
126
+ end
127
+ end
128
+
102
129
  it "should read compressed data fine, even if told not to compress new data" do
103
130
  user = ::User.new
104
131
  user.name = 'User 1'
@@ -63,7 +63,7 @@ describe "FlexColumns performance" do
63
63
  @deserializations[0][:raw_data].should == user_again.user_attributes.to_json
64
64
  end
65
65
 
66
- it "should not deserialize columns if they aren't touched" do
66
+ it "should not deserialize columns if they aren't accessed" do
67
67
  user = ::User.new
68
68
  user.name = 'User 1'
69
69
  user.wants_email = 'foo'
@@ -75,7 +75,7 @@ describe "FlexColumns unknown fields" do
75
75
  user_bd_again.some_unknown_attribute.should be_nil
76
76
  end
77
77
 
78
- it "should not delete unknown fields if asked to, but we only read from the model" do
78
+ it "should delete unknown fields if asked to, even if we only read from the model" do
79
79
  @user_bd.wants_email = 'foo'
80
80
  @user_bd.save!
81
81
 
@@ -98,7 +98,7 @@ describe "FlexColumns unknown fields" do
98
98
 
99
99
  user_bd_again = ::UserBackdoor.find(@user_bd.id)
100
100
  user_bd_again.wants_email.should == 'foo'
101
- user_bd_again.some_unknown_attribute.should == 'bongo'
101
+ user_bd_again.some_unknown_attribute.should_not be
102
102
  end
103
103
 
104
104
  it "should have a method that explicitly will purge unknown methods, even if deserialization hasn't happened for any other reason, but not before then" do
@@ -131,22 +131,16 @@ describe FlexColumns::Contents::ColumnData do
131
131
  lambda { instance.touch! }.should raise_error(FlexColumns::Errors::UnparseableJsonInDatabaseError)
132
132
  end
133
133
 
134
- it "should not be touched if you simply read from it" do
135
- @instance.touched?.should_not be
134
+ it "should be deserialized if you simply read from it" do
135
+ @instance.deserialized?.should_not be
136
136
  @instance[:foo]
137
- @instance.touched?.should_not be
137
+ @instance.deserialized?.should be
138
138
  end
139
139
 
140
- it "should not be touched if you set a field to the same thing" do
141
- @instance.touched?.should_not be
142
- @instance[:foo] = 'bar'
143
- @instance.touched?.should_not be
144
- end
145
-
146
- it "should be touched if you set a field to something different" do
147
- @instance.touched?.should_not be
140
+ it "should be deserialized if you set a field to something different" do
141
+ @instance.deserialized?.should_not be
148
142
  @instance[:foo] = 'baz'
149
- @instance.touched?.should be
143
+ @instance.deserialized?.should be
150
144
  end
151
145
  end
152
146
 
@@ -159,13 +153,34 @@ describe FlexColumns::Contents::ColumnData do
159
153
  parsed['baz'].should == 'quux'
160
154
  end
161
155
 
162
- it "should return JSON data with #to_json" do
163
- json = @instance.to_json
164
- parsed = JSON.parse(json)
165
- parsed.keys.sort.should == %w{foo bar baz}.sort
166
- parsed['foo'].should == 'bar'
167
- parsed['bar'].should == 123
168
- parsed['baz'].should == 'quux'
156
+ describe "#to_hash" do
157
+ it "should return a hash with the data in it, with indifferent access" do
158
+ h = @instance.to_hash
159
+ h.keys.sort.should == %w{foo bar baz}.sort
160
+ h['foo'].should == 'bar'
161
+ h['bar'].should == 123
162
+ h['baz'].should == 'quux'
163
+ h[:foo].should == 'bar'
164
+ h[:bar].should == 123
165
+ h[:baz].should == 'quux'
166
+ end
167
+
168
+ it "should deserialize if needed" do
169
+ h = new_with_string(@json_string).to_hash
170
+ h.keys.sort.should == %w{foo bar baz}.sort
171
+ h['foo'].should == 'bar'
172
+ h['bar'].should == 123
173
+ h['baz'].should == 'quux'
174
+ end
175
+
176
+ it "should not return unknown fields" do
177
+ h = new_with_string({ 'foo' => 'bar', 'baz' => 123, 'quux' => 'whatever' }.to_json).to_hash
178
+ h.keys.sort.should == %w{foo baz}.sort
179
+ h['foo'].should == 'bar'
180
+ h['baz'].should == 123
181
+ h['bar'].should be_nil
182
+ h['quux'].should be_nil
183
+ end
169
184
  end
170
185
 
171
186
  it "should accept a Hash as JSON, already parsed by the database stack" do
@@ -188,11 +188,11 @@ describe FlexColumns::Contents::FlexColumnContentsBase do
188
188
  allow(@model_instance).to receive(:_flex_column_object_for).with(:fcn, false).and_return(@instance)
189
189
  end
190
190
 
191
- it "should tell you if it's been touched" do
192
- expect(@column_data).to receive(:touched?).once.with().and_return(true)
193
- @instance.touched?.should be
194
- expect(@column_data).to receive(:touched?).once.with().and_return(false)
195
- @instance.touched?.should_not be
191
+ it "should tell you if it's been deserialized" do
192
+ expect(@column_data).to receive(:deserialized?).once.with().and_return(true)
193
+ @instance.deserialized?.should be
194
+ expect(@column_data).to receive(:deserialized?).once.with().and_return(false)
195
+ @instance.deserialized?.should_not be
196
196
  end
197
197
 
198
198
  it "should save if the class tells it to" do
@@ -224,6 +224,25 @@ describe FlexColumns::Contents::FlexColumnContentsBase do
224
224
  @instance[:xxx].should == :yyy
225
225
  end
226
226
 
227
+ it "should return a useful string for #inspect" do
228
+ expect(@column_data).to receive(:to_hash).once.and_return({ :aaa => 'bbb', :ccc => 'ddd'})
229
+ i = @instance.inspect
230
+ i.should match(/aaa/)
231
+ i.should match(/bbb/)
232
+ i.should match(/ccc/)
233
+ i.should match(/ddd/)
234
+ end
235
+
236
+ it "should abbreviate that hash, if necessary" do
237
+ expect(@column_data).to receive(:to_hash).once.and_return({ :aaa => 'bbb', :ccc => ('ddd' * 1_000)})
238
+ i = @instance.inspect
239
+ i.should match(/aaa/)
240
+ i.should match(/bbb/)
241
+ i.should match(/ccc/)
242
+ i.should match(/ddddddddd/)
243
+ i.length.should < 1_000
244
+ end
245
+
227
246
  it "should delegate to the column data on []=" do
228
247
  expect(@column_data).to receive(:[]=).once.with(:xxx, :yyy).and_return(:zzz)
229
248
  (@instance[:xxx] = :yyy).should == :yyy
@@ -490,19 +490,19 @@ describe FlexColumns::Definition::FlexColumnContentsClass do
490
490
  end
491
491
 
492
492
  describe "#requires_serialization_on_save?" do
493
- it "should be true if there's an object and it has been touched" do
493
+ it "should be true if there's an object and it has been deserialized" do
494
494
  model = double("model")
495
495
  fco = double("fco")
496
496
  allow(model).to receive(:_flex_column_object_for).with(:foo, false).and_return(fco)
497
- allow(fco).to receive(:touched?).with().and_return(true)
497
+ allow(fco).to receive(:deserialized?).with().and_return(true)
498
498
  @klass.requires_serialization_on_save?(model).should be
499
499
  end
500
500
 
501
- it "should be false if there's an object but it hasn't been touched" do
501
+ it "should be false if there's an object but it hasn't been deserialized" do
502
502
  model = double("model")
503
503
  fco = double("fco")
504
504
  allow(model).to receive(:_flex_column_object_for).with(:foo, false).and_return(fco)
505
- allow(fco).to receive(:touched?).with().and_return(false)
505
+ allow(fco).to receive(:deserialized?).with().and_return(false)
506
506
  @klass.requires_serialization_on_save?(model).should_not be
507
507
  end
508
508
 
@@ -168,28 +168,30 @@ describe FlexColumns::HasFlexColumns do
168
168
  @klass._flex_column_dynamic_methods_module.should be(@dmm)
169
169
  end
170
170
 
171
- it "should call through on before_validation to only flex column objects that have been touched" do
171
+ it "should call through on before_validation to all flex column objects, whether or not they've been deserialized" do
172
172
  instance = @klass.new
173
173
  instance._flex_columns_before_validation!
174
174
 
175
175
  fcc_foo_instance = double("fcc_foo_instance")
176
176
  expect(@fcc_foo).to receive(:new).once.with(instance).and_return(fcc_foo_instance)
177
177
  instance._flex_column_object_for(:foo).should be(fcc_foo_instance)
178
- allow(fcc_foo_instance).to receive(:touched?).with().and_return(false)
178
+ allow(fcc_foo_instance).to receive(:deserialized?).with().and_return(false)
179
179
 
180
+ expect(fcc_foo_instance).to receive(:before_validation!).once.with()
180
181
  instance._flex_columns_before_validation!
181
182
 
182
183
 
183
184
  fcc_bar_instance = double("fcc_bar_instance")
184
185
  expect(@fcc_bar).to receive(:new).once.with(instance).and_return(fcc_bar_instance)
185
186
  instance._flex_column_object_for(:bar).should be(fcc_bar_instance)
186
- allow(fcc_bar_instance).to receive(:touched?).with().and_return(true)
187
+ allow(fcc_bar_instance).to receive(:deserialized?).with().and_return(true)
187
188
 
189
+ expect(fcc_foo_instance).to receive(:before_validation!).once.with()
188
190
  expect(fcc_bar_instance).to receive(:before_validation!).once.with()
189
191
  instance._flex_columns_before_validation!
190
192
 
191
- allow(fcc_foo_instance).to receive(:touched?).with().and_return(true)
192
- allow(fcc_bar_instance).to receive(:touched?).with().and_return(true)
193
+ allow(fcc_foo_instance).to receive(:deserialized?).with().and_return(true)
194
+ allow(fcc_bar_instance).to receive(:deserialized?).with().and_return(true)
193
195
 
194
196
  expect(fcc_foo_instance).to receive(:before_validation!).once.with()
195
197
  expect(fcc_bar_instance).to receive(:before_validation!).once.with()
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.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Geweke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-22 00:00:00.000000000 Z
11
+ date: 2014-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -148,20 +148,6 @@ dependencies:
148
148
  - - <=
149
149
  - !ruby/object:Gem::Version
150
150
  version: 4.99.99
151
- - !ruby/object:Gem::Dependency
152
- name: activerecord-import
153
- requirement: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - '>='
156
- - !ruby/object:Gem::Version
157
- version: '0'
158
- type: :runtime
159
- prerelease: false
160
- version_requirements: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - '>='
163
- - !ruby/object:Gem::Version
164
- version: '0'
165
151
  - !ruby/object:Gem::Dependency
166
152
  name: mysql2
167
153
  requirement: !ruby/object:Gem::Requirement
@@ -251,7 +237,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
251
237
  version: '0'
252
238
  requirements: []
253
239
  rubyforge_project:
254
- rubygems_version: 2.1.11
240
+ rubygems_version: 2.2.1
255
241
  signing_key:
256
242
  specification_version: 4
257
243
  summary: Schema-free, structured JSON storage inside a RDBMS.
@@ -284,3 +270,4 @@ test_files:
284
270
  - spec/flex_columns/unit/including/include_flex_columns_spec.rb
285
271
  - spec/flex_columns/unit/util/dynamic_methods_module_spec.rb
286
272
  - spec/flex_columns/unit/util/string_utils_spec.rb
273
+ has_rdoc: