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,153 @@
1
+ require 'flex_columns'
2
+ require 'flex_columns/helpers/system_helpers'
3
+
4
+ describe "FlexColumns bulk operations" do
5
+ include FlexColumns::Helpers::SystemHelpers
6
+
7
+ before :each do
8
+ @dh = FlexColumns::Helpers::DatabaseHelper.new
9
+ @dh.setup_activerecord!
10
+
11
+ create_standard_system_spec_tables!
12
+ end
13
+
14
+ after :each do
15
+ drop_standard_system_spec_tables!
16
+ end
17
+
18
+ before :each do
19
+ define_model_class(:User, 'flexcols_spec_users') do
20
+ flex_column :user_attributes do
21
+ field :aaa, :string
22
+ field :bbb, :integer
23
+ end
24
+ end
25
+ end
26
+
27
+ it "should return #to_stored_data correctly on a text column, and return the exact same thing for #to_json" do
28
+ user = ::User.new
29
+ user.name = "User 1"
30
+ user.aaa = "aaa#{rand(1_000_000)}"
31
+ user.bbb = rand(1_000_000)
32
+
33
+ json = user.user_attributes.to_stored_data
34
+ json.class.should be(String)
35
+
36
+ parsed = JSON.parse(json)
37
+ parsed.keys.sort.should == %w{aaa bbb}.sort
38
+ parsed['aaa'].should == user.aaa
39
+
40
+ user.user_attributes.to_json.should == json
41
+ end
42
+
43
+ context "with a binary column" do
44
+ before :each do
45
+ migrate do
46
+ drop_table :flexcols_spec_users rescue nil
47
+ create_table :flexcols_spec_users do |t|
48
+ t.string :name, :null => false
49
+ t.binary :user_attributes
50
+ end
51
+ end
52
+
53
+ ::User.reset_column_information
54
+
55
+ define_model_class(:User, 'flexcols_spec_users') do
56
+ flex_column :user_attributes do
57
+ field :aaa, :string
58
+ field :bbb, :integer
59
+ end
60
+ end
61
+ end
62
+
63
+ it "should return #to_stored_data correctly on a binary column, uncompressed, but return JSON separately with #to_json" do
64
+ user = ::User.new
65
+ user.name = "User 1"
66
+ user.aaa = "aaa#{rand(1_000_000)}"
67
+ user.bbb = rand(1_000_000)
68
+
69
+ stored_data = user.user_attributes.to_stored_data
70
+ stored_data.class.should be(String)
71
+ stored_data.should match(/^FC:01,0,/)
72
+
73
+ stored_data =~ /^FC:01,0,(.*)$/i
74
+ json = $1
75
+ parsed = JSON.parse(json)
76
+ parsed.keys.sort.should == %w{aaa bbb}.sort
77
+ parsed['aaa'].should == user.aaa
78
+
79
+ user.user_attributes.to_json.should == json
80
+ end
81
+
82
+ it "should return #to_stored_data correctly on a binary column, compressed, but return JSON separately with #to_json" do
83
+ user = ::User.new
84
+ user.name = "User 1"
85
+ user.aaa = "aaa#{rand(1_000_000)}" * 1_000
86
+ user.bbb = rand(1_000_000)
87
+
88
+ stored_data = user.user_attributes.to_stored_data
89
+ stored_data.class.should be(String)
90
+ stored_data.should match(/^FC:01,1,/)
91
+
92
+ compressed = stored_data[8..-1]
93
+
94
+ require 'stringio'
95
+ stream = StringIO.new(compressed, "r")
96
+ reader = Zlib::GzipReader.new(stream)
97
+ json = reader.read
98
+
99
+ parsed = JSON.parse(json)
100
+ parsed.keys.sort.should == %w{aaa bbb}.sort
101
+ parsed['aaa'].should == user.aaa
102
+
103
+ user.user_attributes.to_json.should == json
104
+ end
105
+ end
106
+
107
+ it "should be able to instantiate fields without an ActiveRecord model, and then serialize them again" do
108
+ users = [ ]
109
+ 10.times do |i|
110
+ user = ::User.new
111
+ user.name = "User #{i}"
112
+ user.aaa = "aaa#{rand(1_000_000)}"
113
+ user.bbb = rand(1_000_000)
114
+ user.save!
115
+
116
+ users << user
117
+ end
118
+
119
+ json_blobs = [ ]
120
+ ::User.connection.select_all("SELECT id, user_attributes FROM flexcols_spec_users ORDER BY id ASC").each do |row|
121
+ json_blobs << row['user_attributes']
122
+ end
123
+
124
+ as_objects = ::User.create_flex_objects_from(:user_attributes, json_blobs)
125
+ as_objects.each_with_index do |object, i|
126
+ user = users[i]
127
+
128
+ object.aaa.should == user.aaa
129
+ object.bbb.should == user.bbb
130
+
131
+ object.bbb = "cannot-validate"
132
+ object.valid?.should_not be
133
+ object.errors.keys.should == [ :bbb ]
134
+ object.errors[:bbb].length.should == 1
135
+ object.errors[:bbb][0].should match(/is not a number/i)
136
+ end
137
+
138
+ json_blobs.each_with_index do |json_blob, i|
139
+ object = ::User.create_flex_object_from(:user_attributes, json_blob)
140
+
141
+ user = users[i]
142
+
143
+ object.aaa.should == user.aaa
144
+ object.bbb.should == user.bbb
145
+
146
+ object.bbb = "cannot-validate"
147
+ object.valid?.should_not be
148
+ object.errors.keys.should == [ :bbb ]
149
+ object.errors[:bbb].length.should == 1
150
+ object.errors[:bbb][0].should match(/is not a number/i)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,218 @@
1
+ require 'flex_columns'
2
+ require 'flex_columns/helpers/system_helpers'
3
+
4
+ describe "FlexColumns compression operations" do
5
+ include FlexColumns::Helpers::SystemHelpers
6
+
7
+ before :each do
8
+ @dh = FlexColumns::Helpers::DatabaseHelper.new
9
+ @dh.setup_activerecord!
10
+
11
+ migrate do
12
+ drop_table :flexcols_spec_users rescue nil
13
+ create_table :flexcols_spec_users do |t|
14
+ t.string :name, :null => false
15
+ t.binary :user_attributes
16
+ end
17
+ end
18
+
19
+ define_model_class(:User, 'flexcols_spec_users') do
20
+ flex_column :user_attributes do
21
+ field :foo
22
+ field :bar
23
+ end
24
+ end
25
+
26
+ define_model_class(:UserBackdoor, 'flexcols_spec_users') { }
27
+ end
28
+
29
+ after :each do
30
+ migrate do
31
+ drop_table :flexcols_spec_users rescue nil
32
+ end
33
+ end
34
+
35
+ it "should not add a header, and should not compress data, if passed :header => false" do
36
+ define_model_class(:User, 'flexcols_spec_users') do
37
+ flex_column :user_attributes, :header => false do
38
+ field :foo
39
+ field :bar
40
+ end
41
+ end
42
+
43
+ user = ::User.new
44
+ user.name = 'User 1'
45
+ user.foo = "foo1"
46
+ user.bar = "bar1"
47
+ user.save!
48
+
49
+ user_bd = ::UserBackdoor.find(user.id)
50
+ data = user_bd.user_attributes
51
+ parsed = JSON.parse(data)
52
+ parsed.keys.sort.should == %w{foo bar}.sort
53
+
54
+ user = ::User.new
55
+ user.name = 'User 1'
56
+ user.foo = "foo" * 10_000
57
+ user.bar = "bar1"
58
+ user.save!
59
+
60
+ user_bd = ::UserBackdoor.find(user.id)
61
+ data = user_bd.user_attributes
62
+ data.length.should > 30_000
63
+ parsed = JSON.parse(data)
64
+ parsed.keys.sort.should == %w{foo bar}.sort
65
+ end
66
+
67
+ it "should not compress short data" do
68
+ user = ::User.new
69
+ user.name = 'User 1'
70
+ user.foo = 'foo1'
71
+ user.bar = 'bar1'
72
+ user.save!
73
+
74
+ user_again = ::User.find(user.id)
75
+ user_again.foo.should == 'foo1'
76
+ user_again.bar.should == 'bar1'
77
+
78
+ user_bd = ::UserBackdoor.find(user.id)
79
+ data = user_bd.user_attributes
80
+ data.should match(/foo1/)
81
+ data.should match(/bar1/)
82
+ end
83
+
84
+ it "should compress long data" do
85
+ user = ::User.new
86
+ user.name = 'User 1'
87
+ user.foo = 'foo' * 1000
88
+ user.bar = 'bar1'
89
+ user.save!
90
+
91
+ user_again = ::User.find(user.id)
92
+ user_again.foo.should == 'foo' * 1000
93
+ user_again.bar.should == 'bar1'
94
+
95
+ user_bd = ::UserBackdoor.find(user.id)
96
+ data = user_bd.user_attributes
97
+ data.length.should < 1000
98
+ data.should_not match(/foo/)
99
+ data.should_not match(/bar/)
100
+ end
101
+
102
+ it "should read compressed data fine, even if told not to compress new data" do
103
+ user = ::User.new
104
+ user.name = 'User 1'
105
+ user.foo = 'foo' * 1000
106
+ user.bar = 'bar1'
107
+ user.save!
108
+
109
+ user_again = ::User.find(user.id)
110
+ user_again.foo.should == 'foo' * 1000
111
+ user_again.bar.should == 'bar1'
112
+
113
+ user_bd = ::UserBackdoor.find(user.id)
114
+ data = user_bd.user_attributes
115
+ data.length.should < 1000
116
+ data.should_not match(/foo/)
117
+ data.should_not match(/bar/)
118
+
119
+ define_model_class(:User2, 'flexcols_spec_users') do
120
+ flex_column :user_attributes, :compress => false do
121
+ field :foo
122
+ field :bar
123
+ end
124
+ end
125
+
126
+ user2 = ::User2.find(user.id)
127
+ user.foo.should == 'foo' * 1000
128
+ user.bar.should == 'bar1'
129
+ end
130
+
131
+ it "should not compress long data, if asked not to" do
132
+ define_model_class(:User, 'flexcols_spec_users') do
133
+ flex_column :user_attributes, :compress => false do
134
+ field :foo
135
+ field :bar
136
+ end
137
+ end
138
+
139
+ user = ::User.new
140
+ user.name = 'User 1'
141
+ user.foo = 'foo' * 1000
142
+ user.bar = 'bar1'
143
+ user.save!
144
+
145
+ user_again = ::User.find(user.id)
146
+ user_again.foo.should == 'foo' * 1000
147
+ user_again.bar.should == 'bar1'
148
+
149
+ user_bd = ::UserBackdoor.find(user.id)
150
+ data = user_bd.user_attributes
151
+ data.length.should >= 3000
152
+ data.should match(/foofoofoofoo/)
153
+ data.should match(/bar1/)
154
+ end
155
+
156
+ it "should not compress data if the compressed version is bigger" do
157
+ define_model_class(:User, 'flexcols_spec_users') do
158
+ flex_column :user_attributes, :compress => 1 do
159
+ field :foo
160
+ field :bar
161
+ end
162
+ end
163
+
164
+ user = ::User.new
165
+ user.name = 'User 1'
166
+ user.foo = 'f'
167
+ user.save!
168
+
169
+ user_again = ::User.find(user.id)
170
+ user_again.foo.should == 'f'
171
+ user_again.bar.should be_nil
172
+
173
+ user_bd = ::UserBackdoor.find(user.id)
174
+ data = user_bd.user_attributes
175
+ data.should match(/^FC:01,0,\{/i)
176
+ end
177
+
178
+ it "should not compress data under a certain limit, if asked to" do
179
+ define_model_class(:User, 'flexcols_spec_users') do
180
+ flex_column :user_attributes, :compress => 10_000 do
181
+ field :foo
182
+ field :bar
183
+ end
184
+ end
185
+
186
+ user = ::User.new
187
+ user.name = 'User 1'
188
+ user.foo = 'foo' * 1000
189
+ user.bar = 'bar1'
190
+ user.save!
191
+
192
+ user_again = ::User.find(user.id)
193
+ user_again.foo.should == 'foo' * 1000
194
+ user_again.bar.should == 'bar1'
195
+
196
+ user_bd = ::UserBackdoor.find(user.id)
197
+ data = user_bd.user_attributes
198
+ data.length.should >= 3000
199
+ data.should match(/foofoofoofoo/)
200
+ data.should match(/bar1/)
201
+
202
+ user2 = ::User.new
203
+ user2.name = 'User 1'
204
+ user2.foo = 'foo' * 10_000
205
+ user2.bar = 'bar1'
206
+ user2.save!
207
+
208
+ user2_again = ::User.find(user2.id)
209
+ user2_again.foo.should == 'foo' * 10_000
210
+ user2_again.bar.should == 'bar1'
211
+
212
+ user2_bd = ::UserBackdoor.find(user2.id)
213
+ data = user2_bd.user_attributes
214
+ data.length.should < 10_000
215
+ data.should_not match(/foofoofoofoo/)
216
+ data.should_not match(/bar1/)
217
+ end
218
+ end
@@ -0,0 +1,120 @@
1
+ require 'flex_columns'
2
+ require 'flex_columns/helpers/system_helpers'
3
+
4
+ describe "FlexColumns custom-methods operations" do
5
+ include FlexColumns::Helpers::SystemHelpers
6
+
7
+ before :each do
8
+ @dh = FlexColumns::Helpers::DatabaseHelper.new
9
+ @dh.setup_activerecord!
10
+
11
+ create_standard_system_spec_tables!
12
+ end
13
+
14
+ after :each do
15
+ drop_standard_system_spec_tables!
16
+ end
17
+
18
+ it "should allow defining methods that are present on both the attributes class and the enclosing class" do
19
+ define_model_class(:User, 'flexcols_spec_users') do
20
+ flex_column :user_attributes do
21
+ field :number_of_emails_sent
22
+
23
+ def increment_number_of_emails_sent
24
+ self.number_of_emails_sent += 1
25
+ end
26
+
27
+ def change_number_of_emails_sent(return_value)
28
+ self.number_of_emails_sent = yield number_of_emails_sent
29
+ return_value
30
+ end
31
+ end
32
+ end
33
+
34
+ user = ::User.new
35
+ user.name = 'User 1'
36
+
37
+ user.user_attributes.number_of_emails_sent = 15
38
+ user.user_attributes.increment_number_of_emails_sent
39
+ user.user_attributes.number_of_emails_sent.should == 16
40
+ (user.user_attributes.change_number_of_emails_sent('abc') { |x| x - 5 }).should == 'abc'
41
+ user.user_attributes.number_of_emails_sent.should == 11
42
+
43
+ user.number_of_emails_sent.should == 11
44
+ user.increment_number_of_emails_sent
45
+ user.number_of_emails_sent.should == 12
46
+ (user.change_number_of_emails_sent('abc') { |x| x - 5 }).should == 'abc'
47
+ user.number_of_emails_sent.should == 7
48
+ end
49
+
50
+ it "should not delegate methods if told not to" do
51
+ define_model_class(:User, 'flexcols_spec_users') do
52
+ flex_column :user_attributes, :delegate => false do
53
+ field :number_of_emails_sent
54
+
55
+ def increment_number_of_emails_sent
56
+ self.number_of_emails_sent += 1
57
+ end
58
+
59
+ def change_number_of_emails_sent(return_value)
60
+ self.number_of_emails_sent = yield number_of_emails_sent
61
+ return_value
62
+ end
63
+ end
64
+ end
65
+
66
+ user = ::User.new
67
+ user.name = 'User 1'
68
+
69
+ user.respond_to?(:number_of_emails_sent).should_not be
70
+ user.respond_to?(:increment_number_of_emails_sent).should_not be
71
+ user.respond_to?(:change_number_of_emails_sent).should_not be
72
+ end
73
+
74
+ it "should delegate methods privately if told to" do
75
+ define_model_class(:User, 'flexcols_spec_users') do
76
+ flex_column :user_attributes, :delegate => :private do
77
+ field :number_of_emails_sent
78
+
79
+ def increment_number_of_emails_sent
80
+ self.number_of_emails_sent += 1
81
+ end
82
+
83
+ def change_number_of_emails_sent(return_value)
84
+ self.number_of_emails_sent = yield number_of_emails_sent
85
+ return_value
86
+ end
87
+ end
88
+
89
+ def nes=(x)
90
+ self.number_of_emails_sent = x
91
+ end
92
+
93
+ def nes
94
+ number_of_emails_sent
95
+ end
96
+
97
+ def ies
98
+ increment_number_of_emails_sent
99
+ end
100
+
101
+ def cnes(rv, &block)
102
+ change_number_of_emails_sent(rv, &block)
103
+ end
104
+ end
105
+
106
+ user = ::User.new
107
+ user.name = 'User 1'
108
+
109
+ user.respond_to?(:number_of_emails_sent).should_not be
110
+ user.respond_to?(:increment_number_of_emails_sent).should_not be
111
+ user.respond_to?(:change_number_of_emails_sent).should_not be
112
+
113
+ user.nes = 10
114
+ user.nes.should == 10
115
+ user.ies
116
+ user.nes.should == 11
117
+ (user.cnes('abc') { |x| x - 5 }).should == 'abc'
118
+ user.nes.should == 6
119
+ end
120
+ end