flex_columns 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +38 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +124 -0
  7. data/Rakefile +6 -0
  8. data/flex_columns.gemspec +72 -0
  9. data/lib/flex_columns.rb +15 -0
  10. data/lib/flex_columns/active_record/base.rb +57 -0
  11. data/lib/flex_columns/contents/column_data.rb +376 -0
  12. data/lib/flex_columns/contents/flex_column_contents_base.rb +188 -0
  13. data/lib/flex_columns/definition/field_definition.rb +316 -0
  14. data/lib/flex_columns/definition/field_set.rb +89 -0
  15. data/lib/flex_columns/definition/flex_column_contents_class.rb +327 -0
  16. data/lib/flex_columns/errors.rb +236 -0
  17. data/lib/flex_columns/has_flex_columns.rb +187 -0
  18. data/lib/flex_columns/including/include_flex_columns.rb +179 -0
  19. data/lib/flex_columns/util/dynamic_methods_module.rb +86 -0
  20. data/lib/flex_columns/util/string_utils.rb +31 -0
  21. data/lib/flex_columns/version.rb +4 -0
  22. data/spec/flex_columns/helpers/database_helper.rb +174 -0
  23. data/spec/flex_columns/helpers/exception_helpers.rb +20 -0
  24. data/spec/flex_columns/helpers/system_helpers.rb +47 -0
  25. data/spec/flex_columns/system/basic_system_spec.rb +245 -0
  26. data/spec/flex_columns/system/bulk_system_spec.rb +153 -0
  27. data/spec/flex_columns/system/compression_system_spec.rb +218 -0
  28. data/spec/flex_columns/system/custom_methods_system_spec.rb +120 -0
  29. data/spec/flex_columns/system/delegation_system_spec.rb +175 -0
  30. data/spec/flex_columns/system/dynamism_system_spec.rb +158 -0
  31. data/spec/flex_columns/system/error_handling_system_spec.rb +117 -0
  32. data/spec/flex_columns/system/including_system_spec.rb +285 -0
  33. data/spec/flex_columns/system/json_alias_system_spec.rb +171 -0
  34. data/spec/flex_columns/system/performance_system_spec.rb +218 -0
  35. data/spec/flex_columns/system/postgres_json_column_type_system_spec.rb +85 -0
  36. data/spec/flex_columns/system/types_system_spec.rb +93 -0
  37. data/spec/flex_columns/system/unknown_fields_system_spec.rb +126 -0
  38. data/spec/flex_columns/system/validations_system_spec.rb +111 -0
  39. data/spec/flex_columns/unit/active_record/base_spec.rb +32 -0
  40. data/spec/flex_columns/unit/contents/column_data_spec.rb +520 -0
  41. data/spec/flex_columns/unit/contents/flex_column_contents_base_spec.rb +253 -0
  42. data/spec/flex_columns/unit/definition/field_definition_spec.rb +617 -0
  43. data/spec/flex_columns/unit/definition/field_set_spec.rb +142 -0
  44. data/spec/flex_columns/unit/definition/flex_column_contents_class_spec.rb +733 -0
  45. data/spec/flex_columns/unit/errors_spec.rb +297 -0
  46. data/spec/flex_columns/unit/has_flex_columns_spec.rb +365 -0
  47. data/spec/flex_columns/unit/including/include_flex_columns_spec.rb +144 -0
  48. data/spec/flex_columns/unit/util/dynamic_methods_module_spec.rb +105 -0
  49. data/spec/flex_columns/unit/util/string_utils_spec.rb +23 -0
  50. metadata +286 -0
@@ -0,0 +1,285 @@
1
+ require 'flex_columns'
2
+ require 'flex_columns/helpers/system_helpers'
3
+
4
+ describe "FlexColumns including" do
5
+ include FlexColumns::Helpers::SystemHelpers
6
+
7
+ before :each do
8
+ @dh = FlexColumns::Helpers::DatabaseHelper.new
9
+ @dh.setup_activerecord!
10
+ end
11
+
12
+ context "with standard setup" do
13
+ before :each do
14
+ create_standard_system_spec_tables!
15
+
16
+ migrate do
17
+ drop_table :flexcols_spec_user_preferences rescue nil
18
+ create_table :flexcols_spec_user_preferences, :id => false do |t|
19
+ t.integer :user_id, :null => false
20
+ t.text :attribs1
21
+ t.text :attribs2
22
+ end
23
+
24
+ add_index :flexcols_spec_user_preferences, :user_id, :unique => true
25
+ end
26
+
27
+ define_model_class(:UserPreference, 'flexcols_spec_user_preferences') do
28
+ self.primary_key = :user_id
29
+
30
+ belongs_to :user
31
+
32
+ flex_column :attribs1 do
33
+ field :foo
34
+ field :bar
35
+
36
+ def inc_foo!
37
+ self.foo += 1
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ after :each do
44
+ drop_standard_system_spec_tables!
45
+
46
+ migrate do
47
+ drop_table :flexcols_spec_user_preferences rescue nil
48
+ end
49
+ end
50
+
51
+ it "should include columns appropriately, including flex-column names and defined methods" do
52
+ define_model_class(:User, 'flexcols_spec_users') do
53
+ has_one :preference, :class_name => 'UserPreference'
54
+
55
+ include_flex_columns_from :preference
56
+ end
57
+
58
+ user = ::User.new
59
+ user.name = 'User 1'
60
+
61
+ user.foo = 123
62
+ user.attribs1.bar = 'bar1'
63
+ user.attribs1.foo.should == 123
64
+ user.bar.should == 'bar1'
65
+
66
+ user.inc_foo!
67
+ user.attribs1.foo.should == 124
68
+ user.foo.should == 124
69
+
70
+ user.save!
71
+
72
+ user_again = ::User.find(user.id)
73
+ user_again.foo.should == 124
74
+ user_again.bar.should == 'bar1'
75
+ user_again.attribs1.foo.should == 124
76
+ user_again.attribs1.bar.should == 'bar1'
77
+
78
+ preferences = ::UserPreference.find(user.id)
79
+ preferences.foo.should == 124
80
+ preferences.bar.should == 'bar1'
81
+ preferences.attribs1.foo.should == 124
82
+ preferences.attribs1.bar.should == 'bar1'
83
+ end
84
+
85
+ it "should allow turning off delegation, but should still include base names, prefixed as needed" do
86
+ define_model_class(:User, 'flexcols_spec_users') do
87
+ has_one :preference, :class_name => 'UserPreference'
88
+
89
+ include_flex_columns_from :preference, :delegate => false, :prefix => :abc
90
+ end
91
+
92
+ user = ::User.new
93
+ user.name = 'User 1'
94
+
95
+ user.respond_to?(:foo).should_not be
96
+ user.respond_to?(:bar).should_not be
97
+ user.respond_to?(:abc_foo).should_not be
98
+ user.respond_to?(:abc_bar).should_not be
99
+ user.respond_to?(:inc_foo!).should_not be
100
+ user.respond_to?(:abc_inc_foo!).should_not be
101
+
102
+ user.abc_attribs1.foo = 123
103
+ user.abc_attribs1.bar = 'bar1'
104
+ user.abc_attribs1.inc_foo!.should == 124
105
+ user.abc_attribs1.foo.should == 124
106
+
107
+ user.save!
108
+
109
+ user_again = ::User.find(user.id)
110
+ user_again.abc_attribs1.foo.should == 124
111
+ user_again.abc_attribs1.bar.should == 'bar1'
112
+
113
+ preferences = ::UserPreference.find(user.id)
114
+ preferences.foo.should == 124
115
+ preferences.bar.should == 'bar1'
116
+ end
117
+
118
+ it "should include columns and methods privately, if requested" do
119
+ define_model_class(:User, 'flexcols_spec_users') do
120
+ has_one :preference, :class_name => 'UserPreference'
121
+
122
+ include_flex_columns_from :preference, :visibility => :private
123
+ end
124
+
125
+ user = ::User.new
126
+ user.name = 'User 1'
127
+
128
+ user.respond_to?(:foo).should_not be
129
+ user.respond_to?(:foo=).should_not be
130
+ user.respond_to?(:bar).should_not be
131
+ user.respond_to?(:bar=).should_not be
132
+ user.respond_to?(:inc_foo).should_not be
133
+ user.respond_to?(:attribs1).should_not be
134
+
135
+ lambda { user.foo }.should raise_error(NoMethodError)
136
+ lambda { user.foo = 123 }.should raise_error(NoMethodError)
137
+ lambda { user.bar }.should raise_error(NoMethodError)
138
+ lambda { user.bar = 123 }.should raise_error(NoMethodError)
139
+ lambda { user.inc_foo }.should raise_error(NoMethodError)
140
+ lambda { user.attribs1 }.should raise_error(NoMethodError)
141
+
142
+ user.send(:foo=, 123).should == 123
143
+ user.send(:bar=, 'bar1').should == 'bar1'
144
+ user.send(:inc_foo!).should == 124
145
+ user.send(:foo).should == 124
146
+ user.send(:attribs1).foo.should == 124
147
+
148
+ user.save!
149
+
150
+ user_again = ::User.find(user.id)
151
+ user_again.send(:foo).should == 124
152
+ user_again.send(:bar).should == 'bar1'
153
+ user_again.send(:attribs1).foo.should == 124
154
+
155
+ preferences = ::UserPreference.find(user.id)
156
+ preferences.foo.should == 124
157
+ preferences.bar.should == 'bar1'
158
+ end
159
+
160
+ it "should prefix included method names, if requested" do
161
+ define_model_class(:User, 'flexcols_spec_users') do
162
+ has_one :preference, :class_name => 'UserPreference'
163
+
164
+ include_flex_columns_from :preference, :prefix => 'abc'
165
+ end
166
+
167
+ user = ::User.new
168
+ user.name = 'User 1'
169
+
170
+ lambda { user.send(:foo) }.should raise_error(NoMethodError)
171
+ lambda { user.send(:foo=, 123) }.should raise_error(NoMethodError)
172
+ lambda { user.send(:bar) }.should raise_error(NoMethodError)
173
+ lambda { user.send(:bar=, 123) }.should raise_error(NoMethodError)
174
+ lambda { user.send(:inc_foo!) }.should raise_error(NoMethodError)
175
+ lambda { user.send(:attribs1) }.should raise_error(NoMethodError)
176
+
177
+ user.abc_foo = 123
178
+ user.abc_bar = 'bar1'
179
+ user.abc_inc_foo!.should == 124
180
+ user.abc_foo.should == 124
181
+ user.abc_attribs1.foo.should == 124
182
+ user.abc_attribs1.bar.should == 'bar1'
183
+
184
+ user.save!
185
+
186
+ user_again = ::User.find(user.id)
187
+ user_again.abc_foo.should == 124
188
+ user_again.abc_bar.should == 'bar1'
189
+ user_again.abc_attribs1.foo.should == 124
190
+ user_again.abc_attribs1.bar.should == 'bar1'
191
+
192
+ preferences = ::UserPreference.find(user.id)
193
+ preferences.foo.should == 124
194
+ preferences.bar.should == 'bar1'
195
+ end
196
+ end
197
+
198
+ it "should not clobber methods that already exist, or columns on the included-into object" do
199
+ migrate do
200
+ drop_table :flexcols_spec_users rescue nil
201
+ create_table :flexcols_spec_users do |t|
202
+ t.string :name, :null => false
203
+ t.string :foo
204
+ t.string :quux
205
+ t.string :attribs1
206
+ end
207
+
208
+ drop_table :flexcols_spec_user_preferences rescue nil
209
+ create_table :flexcols_spec_user_preferences, :id => false do |t|
210
+ t.integer :user_id, :null => false
211
+ t.text :attribs1
212
+ end
213
+
214
+ add_index :flexcols_spec_user_preferences, :user_id, :unique => true
215
+ end
216
+
217
+ define_model_class(:UserPreference2, 'flexcols_spec_user_preferences') do
218
+ self.primary_key = :user_id
219
+
220
+ belongs_to :user, :class_name => 'User2'
221
+
222
+ flex_column :attribs1 do
223
+ field :foo
224
+ field :bar
225
+ field :baz
226
+
227
+ def quux
228
+ self.foo
229
+ end
230
+ end
231
+ end
232
+
233
+ define_model_class(:User2, 'flexcols_spec_users') do
234
+ has_one :preference, :class_name => 'UserPreference2', :foreign_key => :user_id
235
+
236
+ include_flex_columns_from :preference
237
+ end
238
+
239
+ define_model_class(:UserBackdoor2, 'flexcols_spec_users') { }
240
+
241
+ define_model_class(:UserPreferenceBackdoor2, 'flexcols_spec_user_preferences') { }
242
+
243
+ user = ::User2.new
244
+ user.name = 'User 1'
245
+ user.foo = 'user_1 foo'
246
+ user.quux = 'user_1 quux'
247
+ user.attribs1 = "user_1 attribs1"
248
+ user.save!
249
+
250
+ user_bd = ::UserBackdoor2.find(user.id)
251
+ user_bd.attribs1.should == 'user_1 attribs1'
252
+ user_bd.foo.should == 'user_1 foo'
253
+ user_bd.quux.should == 'user_1 quux'
254
+
255
+ user.build_preference
256
+ user.preference.foo = 'prefs foo'
257
+ user.preference.bar = 'prefs bar'
258
+ user.preference.baz = 'prefs baz'
259
+ user.preference.quux.should == 'prefs foo'
260
+
261
+ user.preference.attribs1.class.name.should match(/flexcontents/i)
262
+ user.attribs1.should == 'user_1 attribs1'
263
+ user.save!
264
+
265
+
266
+ user_bd = ::UserBackdoor2.find(user.id)
267
+ user_bd.attribs1.should == 'user_1 attribs1'
268
+ user_bd.foo.should == 'user_1 foo'
269
+ user_bd.quux.should == 'user_1 quux'
270
+
271
+ prefs_bd = ::UserPreferenceBackdoor2.where(:user_id => user.id).first
272
+ parsed = JSON.parse(prefs_bd.attribs1)
273
+ parsed.keys.sort.should == %w{foo bar baz}.sort
274
+ parsed['foo'].should == 'prefs foo'
275
+ parsed['bar'].should == 'prefs bar'
276
+ parsed['baz'].should == 'prefs baz'
277
+
278
+ user_again = ::User2.find(user.id)
279
+ user_again.foo.should == 'user_1 foo'
280
+ user_again.quux.should == 'user_1 quux'
281
+ user_again.attribs1.should == 'user_1 attribs1'
282
+ user_again.bar.should == 'prefs bar'
283
+ user_again.baz.should == 'prefs baz'
284
+ end
285
+ end
@@ -0,0 +1,171 @@
1
+ require 'flex_columns'
2
+ require 'flex_columns/helpers/system_helpers'
3
+ require 'flex_columns/helpers/exception_helpers'
4
+
5
+ describe "FlexColumns JSON aliasing" do
6
+ include FlexColumns::Helpers::SystemHelpers
7
+ include FlexColumns::Helpers::ExceptionHelpers
8
+
9
+ before :each do
10
+ @dh = FlexColumns::Helpers::DatabaseHelper.new
11
+ @dh.setup_activerecord!
12
+
13
+ create_standard_system_spec_tables!
14
+
15
+ define_model_class(:UserBackdoor, 'flexcols_spec_users') { }
16
+ end
17
+
18
+ after :each do
19
+ drop_standard_system_spec_tables!
20
+ end
21
+
22
+ it "should store attributes under their JSON alias, but use other names absolutely everywhere else" do
23
+ define_model_class(:User, 'flexcols_spec_users') do
24
+ flex_column :user_attributes do
25
+ field :wants_email, :integer, :json => :we
26
+ field :language_setting, :json => :ls
27
+
28
+ validates :wants_email, :numericality => { :greater_than_or_equal_to => 0 }
29
+ end
30
+ end
31
+
32
+ user = ::User.new
33
+ user.name = 'User 1'
34
+ user.wants_email = 123
35
+ user.language_setting = 'foobar'
36
+ user.save!
37
+
38
+ user_bd = ::UserBackdoor.find(user.id)
39
+ json = user_bd.user_attributes
40
+ parsed = JSON.parse(json)
41
+
42
+ parsed.keys.sort.should == %w{we ls}.sort
43
+ parsed['we'].should == 123
44
+ parsed['ls'].should == 'foobar'
45
+
46
+ # make sure hashes work on original field names
47
+ lambda { user.user_attributes[:we] }.should raise_error(FlexColumns::Errors::NoSuchFieldError)
48
+ lambda { user.user_attributes[:ls] }.should raise_error(FlexColumns::Errors::NoSuchFieldError)
49
+
50
+ # Make sure methods work on original field names
51
+ lambda { user.user_attributes.send(:we) }.should raise_error(NoMethodError)
52
+ lambda { user.user_attributes.send(:ls) }.should raise_error(NoMethodError)
53
+
54
+ # Make sure validations work still
55
+ user.valid?.should be
56
+
57
+ user.wants_email = -123
58
+
59
+ user.valid?.should_not be
60
+ user.errors.keys.should == [ :'user_attributes.wants_email' ]
61
+
62
+ user.user_attributes.valid?.should_not be
63
+ user.user_attributes.errors.keys.should == [ :wants_email ]
64
+ end
65
+
66
+ it "should prohibit conflicting JSON names" do
67
+ e = capture_exception(FlexColumns::Errors::ConflictingJsonStorageNameError) do
68
+ define_model_class(:User, 'flexcols_spec_users') { }
69
+
70
+ define_model_class(:User, 'flexcols_spec_users') do
71
+ flex_column :user_attributes, :unknown_fields => :delete do
72
+ field :wants_email, :json => :aaa
73
+ field :language_setting, :json => :aaa
74
+ end
75
+ end
76
+ end
77
+
78
+ e.model_class.should == ::User
79
+ e.column_name.should == :user_attributes
80
+ e.new_field_name.should == :language_setting
81
+ e.existing_field_name.should == :wants_email
82
+ e.json_storage_name.should == :aaa
83
+
84
+ e.message.should match(/User/i)
85
+ e.message.should match(/user_attributes/i)
86
+ e.message.should match(/language_setting/i)
87
+ e.message.should match(/wants_email/i)
88
+ e.message.should match(/aaa/i)
89
+ end
90
+
91
+ it "should prohibit JSON names from conflicting with non-aliased fields" do
92
+ e = capture_exception(FlexColumns::Errors::ConflictingJsonStorageNameError) do
93
+ define_model_class(:User, 'flexcols_spec_users') { }
94
+
95
+ define_model_class(:User, 'flexcols_spec_users') do
96
+ flex_column :user_attributes, :unknown_fields => :delete do
97
+ field :wants_email
98
+ field :language_setting, :json => :wants_email
99
+ end
100
+ end
101
+ end
102
+
103
+ e.model_class.should == ::User
104
+ e.column_name.should == :user_attributes
105
+ e.new_field_name.should == :language_setting
106
+ e.existing_field_name.should == :wants_email
107
+ e.json_storage_name.should == :wants_email
108
+
109
+ e.message.should match(/User/i)
110
+ e.message.should match(/user_attributes/i)
111
+ e.message.should match(/language_setting/i)
112
+ e.message.should match(/wants_email/i)
113
+ e.message.should match(/wants_email/i)
114
+ end
115
+
116
+ it "should treat field names present in the JSON hash as unknown fields, and delete them if asked to" do
117
+ define_model_class(:User, 'flexcols_spec_users') do
118
+ flex_column :user_attributes, :unknown_fields => :delete do
119
+ field :wants_email, :json => :we
120
+ field :language_setting, :json => :ls
121
+ end
122
+ end
123
+
124
+ user = ::User.new
125
+ user.name = 'User 1'
126
+ user.save!
127
+
128
+ user_bd = ::UserBackdoor.find(user.id)
129
+ user_bd.user_attributes = { 'wants_email' => 123, 'we' => 456, 'language_setting' => 'bonko' }.to_json
130
+ user_bd.save!
131
+
132
+ user_again = ::User.find(user.id)
133
+ user_again.wants_email.should == 456
134
+ user_again.language_setting.should be_nil
135
+ user_again.user_attributes.touch!
136
+ user_again.save!
137
+
138
+ user_bd = ::UserBackdoor.find(user.id)
139
+ JSON.parse(user_bd.user_attributes).keys.sort.should == %w{we}.sort
140
+ end
141
+
142
+ it "should treat field names present in the JSON hash as unknown fields, and preserve them if asked to" do
143
+ define_model_class(:User, 'flexcols_spec_users') do
144
+ flex_column :user_attributes do
145
+ field :wants_email, :json => :we
146
+ field :language_setting, :json => :ls
147
+ end
148
+ end
149
+
150
+ user = ::User.new
151
+ user.name = 'User 1'
152
+ user.save!
153
+
154
+ user_bd = ::UserBackdoor.find(user.id)
155
+ user_bd.user_attributes = { 'wants_email' => 123, 'we' => 456, 'language_setting' => 'bonko' }.to_json
156
+ user_bd.save!
157
+
158
+ user_again = ::User.find(user.id)
159
+ user_again.wants_email.should == 456
160
+ user_again.wants_email = 567
161
+ user_again.language_setting.should be_nil
162
+ user_again.save!
163
+
164
+ user_bd = ::UserBackdoor.find(user.id)
165
+ parsed = JSON.parse(user_bd.user_attributes)
166
+ parsed.keys.sort.should == %w{wants_email we language_setting}.sort
167
+ parsed['wants_email'].should == 123
168
+ parsed['we'].should == 567
169
+ parsed['language_setting'].should == 'bonko'
170
+ end
171
+ end