flex_columns 1.0.0
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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +38 -0
- data/Gemfile +17 -0
- data/LICENSE.txt +22 -0
- data/README.md +124 -0
- data/Rakefile +6 -0
- data/flex_columns.gemspec +72 -0
- data/lib/flex_columns.rb +15 -0
- data/lib/flex_columns/active_record/base.rb +57 -0
- data/lib/flex_columns/contents/column_data.rb +376 -0
- data/lib/flex_columns/contents/flex_column_contents_base.rb +188 -0
- data/lib/flex_columns/definition/field_definition.rb +316 -0
- data/lib/flex_columns/definition/field_set.rb +89 -0
- data/lib/flex_columns/definition/flex_column_contents_class.rb +327 -0
- data/lib/flex_columns/errors.rb +236 -0
- data/lib/flex_columns/has_flex_columns.rb +187 -0
- data/lib/flex_columns/including/include_flex_columns.rb +179 -0
- data/lib/flex_columns/util/dynamic_methods_module.rb +86 -0
- data/lib/flex_columns/util/string_utils.rb +31 -0
- data/lib/flex_columns/version.rb +4 -0
- data/spec/flex_columns/helpers/database_helper.rb +174 -0
- data/spec/flex_columns/helpers/exception_helpers.rb +20 -0
- data/spec/flex_columns/helpers/system_helpers.rb +47 -0
- data/spec/flex_columns/system/basic_system_spec.rb +245 -0
- data/spec/flex_columns/system/bulk_system_spec.rb +153 -0
- data/spec/flex_columns/system/compression_system_spec.rb +218 -0
- data/spec/flex_columns/system/custom_methods_system_spec.rb +120 -0
- data/spec/flex_columns/system/delegation_system_spec.rb +175 -0
- data/spec/flex_columns/system/dynamism_system_spec.rb +158 -0
- data/spec/flex_columns/system/error_handling_system_spec.rb +117 -0
- data/spec/flex_columns/system/including_system_spec.rb +285 -0
- data/spec/flex_columns/system/json_alias_system_spec.rb +171 -0
- data/spec/flex_columns/system/performance_system_spec.rb +218 -0
- data/spec/flex_columns/system/postgres_json_column_type_system_spec.rb +85 -0
- data/spec/flex_columns/system/types_system_spec.rb +93 -0
- data/spec/flex_columns/system/unknown_fields_system_spec.rb +126 -0
- data/spec/flex_columns/system/validations_system_spec.rb +111 -0
- data/spec/flex_columns/unit/active_record/base_spec.rb +32 -0
- data/spec/flex_columns/unit/contents/column_data_spec.rb +520 -0
- data/spec/flex_columns/unit/contents/flex_column_contents_base_spec.rb +253 -0
- data/spec/flex_columns/unit/definition/field_definition_spec.rb +617 -0
- data/spec/flex_columns/unit/definition/field_set_spec.rb +142 -0
- data/spec/flex_columns/unit/definition/flex_column_contents_class_spec.rb +733 -0
- data/spec/flex_columns/unit/errors_spec.rb +297 -0
- data/spec/flex_columns/unit/has_flex_columns_spec.rb +365 -0
- data/spec/flex_columns/unit/including/include_flex_columns_spec.rb +144 -0
- data/spec/flex_columns/unit/util/dynamic_methods_module_spec.rb +105 -0
- data/spec/flex_columns/unit/util/string_utils_spec.rb +23 -0
- metadata +286 -0
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'flex_columns'
|
2
|
+
require 'flex_columns/helpers/exception_helpers'
|
3
|
+
require 'flex_columns/helpers/system_helpers'
|
4
|
+
|
5
|
+
describe "FlexColumns delegation" 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
|
+
end
|
15
|
+
|
16
|
+
after :each do
|
17
|
+
drop_standard_system_spec_tables!
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should delegate methods by default" do
|
21
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
22
|
+
flex_column :user_attributes do
|
23
|
+
field :wants_email
|
24
|
+
field :something
|
25
|
+
field :something_else
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
user = ::User.new
|
30
|
+
|
31
|
+
%w{wants_email something something_else}.each do |method_name|
|
32
|
+
user.respond_to?(method_name).should be
|
33
|
+
user.respond_to?("#{method_name}=").should be
|
34
|
+
|
35
|
+
user.send(method_name).should be_nil
|
36
|
+
value = "abc123#{rand(1_000)}"
|
37
|
+
user.send("#{method_name}=", value).should == value
|
38
|
+
user.send(method_name).should == value
|
39
|
+
|
40
|
+
user.user_attributes.send(method_name).should == value
|
41
|
+
user.user_attributes.send("#{method_name}=", value + "new").should == value + "new"
|
42
|
+
user.send(method_name).should == value + "new"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not override columns on the model object" do
|
47
|
+
migrate do
|
48
|
+
drop_table :flexcols_spec_users
|
49
|
+
create_table :flexcols_spec_users do |t|
|
50
|
+
t.string :name, :null => false
|
51
|
+
t.string :foo
|
52
|
+
t.string :baz
|
53
|
+
t.text :user_attributes
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
58
|
+
flex_column :user_attributes do
|
59
|
+
field :foo
|
60
|
+
field :bar
|
61
|
+
|
62
|
+
def baz
|
63
|
+
foo + "!!"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
::User.reset_column_information
|
69
|
+
|
70
|
+
define_model_class(:UserBackdoor, 'flexcols_spec_users') { }
|
71
|
+
|
72
|
+
user = ::User.new
|
73
|
+
user.name = 'User 1'
|
74
|
+
user.foo = "outer_foo"
|
75
|
+
user.baz = "outer_baz"
|
76
|
+
user.user_attributes.foo = "inner_foo"
|
77
|
+
user.save!
|
78
|
+
|
79
|
+
user_again = ::User.find(user.id)
|
80
|
+
user_again.foo.should == "outer_foo"
|
81
|
+
user_again.user_attributes.foo.should == "inner_foo"
|
82
|
+
user_again.baz.should == "outer_baz"
|
83
|
+
user_again.user_attributes.baz.should == "inner_foo!!"
|
84
|
+
|
85
|
+
user_bd = ::UserBackdoor.find(user.id)
|
86
|
+
user_bd.foo.should == "outer_foo"
|
87
|
+
user_bd.baz.should == "outer_baz"
|
88
|
+
parsed = JSON.parse(user_bd.user_attributes)
|
89
|
+
parsed.keys.should == [ 'foo' ]
|
90
|
+
parsed['foo'].should == "inner_foo"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should let you turn off delegation for a column" do
|
94
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
95
|
+
flex_column :user_attributes, :delegate => false do
|
96
|
+
field :wants_email
|
97
|
+
field :something
|
98
|
+
field :something_else
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
user = ::User.new
|
103
|
+
|
104
|
+
%w{wants_email something something_else}.each do |method_name|
|
105
|
+
user.respond_to?(method_name).should_not be
|
106
|
+
user.respond_to?("#{method_name}=").should_not be
|
107
|
+
|
108
|
+
lambda { user.send(method_name) }.should raise_error(NoMethodError.superclass)
|
109
|
+
lambda { user.send("#{method_name}=", 1234) }.should raise_error(NoMethodError.superclass)
|
110
|
+
|
111
|
+
user.user_attributes.send(method_name).should be_nil
|
112
|
+
value = "abc123#{rand(1_000)}"
|
113
|
+
user.user_attributes.send("#{method_name}=", value).should == value
|
114
|
+
user.user_attributes.send(method_name).should == value
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should let you use private delegation for a column" do
|
119
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
120
|
+
flex_column :user_attributes, :delegate => :private do
|
121
|
+
field :wants_email
|
122
|
+
field :something
|
123
|
+
field :something_else
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
user = ::User.new
|
128
|
+
|
129
|
+
%w{wants_email something something_else}.each do |method_name|
|
130
|
+
user.respond_to?(method_name).should_not be
|
131
|
+
user.respond_to?("#{method_name}=").should_not be
|
132
|
+
|
133
|
+
lambda { eval("user.#{method_name}") }.should raise_error(NoMethodError)
|
134
|
+
lambda { eval("user.#{method_name} = 123") }.should raise_error(NoMethodError)
|
135
|
+
|
136
|
+
user.send(method_name).should be_nil
|
137
|
+
value = "abc123#{rand(1_000)}"
|
138
|
+
user.send("#{method_name}=", value).should == value
|
139
|
+
user.send(method_name).should == value
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should let you add a prefix to methods" do
|
144
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
145
|
+
flex_column :user_attributes, :prefix => 'bar' do
|
146
|
+
field :wants_email
|
147
|
+
field :something
|
148
|
+
field :something_else
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
user = ::User.new
|
153
|
+
|
154
|
+
%w{wants_email something something_else}.each do |method_name|
|
155
|
+
user.respond_to?(method_name).should_not be
|
156
|
+
user.respond_to?("#{method_name}=").should_not be
|
157
|
+
|
158
|
+
lambda { user.send(method_name) }.should raise_error(NoMethodError.superclass)
|
159
|
+
lambda { user.send("#{method_name}=", 1234) }.should raise_error(NoMethodError.superclass)
|
160
|
+
|
161
|
+
correct_method_name = "bar_#{method_name}"
|
162
|
+
user.respond_to?(correct_method_name).should be
|
163
|
+
user.respond_to?("#{correct_method_name}=").should be
|
164
|
+
|
165
|
+
user.send(correct_method_name).should be_nil
|
166
|
+
value = "abc123#{rand(1_000)}"
|
167
|
+
user.send("#{correct_method_name}=", value).should == value
|
168
|
+
user.send(correct_method_name).should == value
|
169
|
+
|
170
|
+
user.user_attributes.send(method_name).should == value
|
171
|
+
user.user_attributes.send("#{method_name}=", value + "new").should == value + "new"
|
172
|
+
user.send(correct_method_name).should == value + "new"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'flex_columns'
|
2
|
+
require 'flex_columns/helpers/system_helpers'
|
3
|
+
|
4
|
+
describe "FlexColumns basic 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 let you redefine flex columns, and obey the new settings" do
|
19
|
+
class ::User < ::ActiveRecord::Base
|
20
|
+
self.table_name = 'flexcols_spec_users'
|
21
|
+
|
22
|
+
flex_column :user_attributes do
|
23
|
+
field :att1
|
24
|
+
field :att2
|
25
|
+
|
26
|
+
def abc
|
27
|
+
"abc!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
user = ::User.new
|
33
|
+
user.att1 = "foo"
|
34
|
+
user.att2 = "bar"
|
35
|
+
user.abc.should == 'abc!'
|
36
|
+
|
37
|
+
class ::User < ::ActiveRecord::Base
|
38
|
+
self.table_name = 'flexcols_spec_users'
|
39
|
+
|
40
|
+
flex_column :user_attributes do
|
41
|
+
field :att3
|
42
|
+
field :att2
|
43
|
+
|
44
|
+
def def
|
45
|
+
"def!"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
user2 = ::User.new
|
51
|
+
user2.respond_to?(:att1).should_not be
|
52
|
+
user2.user_attributes.respond_to?(:att1).should_not be
|
53
|
+
user2.respond_to?(:abc).should_not be
|
54
|
+
user2.user_attributes.respond_to?(:abc).should_not be
|
55
|
+
|
56
|
+
# explicitly not testing if user.respond_to?(:att3) or if user.respond_to?)(:att1); we make no guarantees about
|
57
|
+
# what happened on older objects
|
58
|
+
|
59
|
+
class ::User < ::ActiveRecord::Base
|
60
|
+
self.table_name = 'flexcols_spec_users'
|
61
|
+
|
62
|
+
flex_column :user_attributes do
|
63
|
+
field :att1
|
64
|
+
field :att2
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
user3 = ::User.new
|
69
|
+
user3.respond_to?(:att1).should be
|
70
|
+
user3.user_attributes.respond_to?(:att1).should be
|
71
|
+
user3.respond_to?(:att2).should be
|
72
|
+
user3.user_attributes.respond_to?(:att2).should be
|
73
|
+
user3.user_attributes.respond_to?(:abc).should_not be
|
74
|
+
user3.respond_to?(:abc).should_not be
|
75
|
+
user3.user_attributes.respond_to?(:def).should_not be
|
76
|
+
user3.respond_to?(:def).should_not be
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should discard all attributes when #reload is called" do
|
80
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
81
|
+
flex_column :user_attributes do
|
82
|
+
field :wants_email
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
user = ::User.new
|
87
|
+
user.name = 'User 1'
|
88
|
+
user.save!
|
89
|
+
|
90
|
+
user = ::User.find(user.id)
|
91
|
+
user.name = 'User 2'
|
92
|
+
user.wants_email = 'bonko'
|
93
|
+
|
94
|
+
user.reload
|
95
|
+
|
96
|
+
user.name.should == 'User 1'
|
97
|
+
user.wants_email.should be_nil
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should use the most-recently-defined flex-column attribute in delegation, if there's a conflict" do
|
101
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
102
|
+
flex_column :user_attributes do
|
103
|
+
field :att1
|
104
|
+
field :att2
|
105
|
+
end
|
106
|
+
|
107
|
+
flex_column :more_attributes do
|
108
|
+
field :att2
|
109
|
+
field :att3
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
user = ::User.new
|
114
|
+
user.name = 'User 1'
|
115
|
+
user.att1 = "foo"
|
116
|
+
user.att2 = "bar"
|
117
|
+
user.att3 = "baz"
|
118
|
+
|
119
|
+
user.user_attributes.att1.should == "foo"
|
120
|
+
user.user_attributes.att2.should be_nil
|
121
|
+
user.user_attributes.att2 = "quux"
|
122
|
+
user.more_attributes.att2.should == "bar"
|
123
|
+
user.more_attributes.att3.should == "baz"
|
124
|
+
|
125
|
+
user.att2.should == "bar"
|
126
|
+
|
127
|
+
# Now, reverse them
|
128
|
+
|
129
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
130
|
+
flex_column :more_attributes do
|
131
|
+
field :att2
|
132
|
+
field :att3
|
133
|
+
end
|
134
|
+
|
135
|
+
flex_column :user_attributes do
|
136
|
+
field :att1
|
137
|
+
field :att2
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
user = ::User.new
|
142
|
+
user.name = 'User 1'
|
143
|
+
user.att1 = "foo"
|
144
|
+
user.att2 = "bar"
|
145
|
+
user.att3 = "baz"
|
146
|
+
|
147
|
+
user.user_attributes.att1.should == "foo"
|
148
|
+
user.user_attributes.att2.should == "bar"
|
149
|
+
user.more_attributes.att2.should be_nil
|
150
|
+
user.more_attributes.att2 = "quux"
|
151
|
+
user.more_attributes.att3.should == "baz"
|
152
|
+
|
153
|
+
user.user_attributes.att2.should == "bar"
|
154
|
+
user.more_attributes.att2.should == "quux"
|
155
|
+
|
156
|
+
user.att2.should == "bar"
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'flex_columns'
|
2
|
+
require 'flex_columns/helpers/system_helpers'
|
3
|
+
require 'flex_columns/helpers/exception_helpers'
|
4
|
+
|
5
|
+
describe "FlexColumns error handling" 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
|
+
migrate do
|
14
|
+
drop_table :flexcols_spec_users rescue nil
|
15
|
+
create_table :flexcols_spec_users do |t|
|
16
|
+
t.string :name, :null => false
|
17
|
+
t.string :user_attributes, :limit => 100
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
define_model_class(:User, 'flexcols_spec_users') do
|
22
|
+
flex_column :user_attributes do
|
23
|
+
field :wants_email
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
define_model_class(:UserBackdoor, 'flexcols_spec_users') { }
|
28
|
+
end
|
29
|
+
|
30
|
+
after :each do
|
31
|
+
migrate do
|
32
|
+
drop_table :flexcols_spec_users rescue nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return a nice error if JSON parsing fails" do
|
37
|
+
user_bd_1 = ::UserBackdoor.new
|
38
|
+
user_bd_1.name = 'User 1'
|
39
|
+
user_bd_1.user_attributes = "---unparseable json---"
|
40
|
+
user_bd_1.save!
|
41
|
+
|
42
|
+
user = ::User.find(user_bd_1.id)
|
43
|
+
|
44
|
+
e = capture_exception(FlexColumns::Errors::UnparseableJsonInDatabaseError) { user.wants_email }
|
45
|
+
e.message.should match(/user.*id.*#{user.id}/i)
|
46
|
+
e.message.should match(/\-\-\-unparseable json\-\-\-/i)
|
47
|
+
e.message.should match(/JSON::ParserError/i)
|
48
|
+
|
49
|
+
e.data_source.should be(user.user_attributes)
|
50
|
+
e.raw_string.should == "---unparseable json---"
|
51
|
+
e.source_exception.class.should == JSON::ParserError
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should return a nice error if the string isn't even a validly-encoded string" do
|
55
|
+
user = ::UserBackdoor.new
|
56
|
+
user.name = 'User 1'
|
57
|
+
user.save!
|
58
|
+
|
59
|
+
user2 = ::User.find(user.id)
|
60
|
+
|
61
|
+
class << user2
|
62
|
+
def [](x)
|
63
|
+
if x.to_s == "user_attributes"
|
64
|
+
out = "abc\xC3\x28def"
|
65
|
+
out = out.force_encoding("UTF-8") if out.respond_to?(:force_encoding)
|
66
|
+
out
|
67
|
+
else
|
68
|
+
super(x)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
if "foo".respond_to?(:force_encoding) # do we have Ruby >= 1.9?
|
74
|
+
e = capture_exception(FlexColumns::Errors::IncorrectlyEncodedStringInDatabaseError) { user2.wants_email }
|
75
|
+
e.message.should match(/user.*id.*#{user2.id}/i)
|
76
|
+
e.message.should match(/abc.*def/i)
|
77
|
+
e.message.should match(/position 3/i)
|
78
|
+
e.message.should match(/c3/i)
|
79
|
+
|
80
|
+
e.data_source.should be(user2.user_attributes)
|
81
|
+
e.raw_string.should match(/abc.*def/i)
|
82
|
+
|
83
|
+
invalid_char = ["C3"].pack("H*")
|
84
|
+
invalid_char.force_encoding("UTF-8")
|
85
|
+
|
86
|
+
e.invalid_chars_as_array.include?(invalid_char).should be
|
87
|
+
e.raw_data_as_array.include?(invalid_char).should be
|
88
|
+
e.raw_data_as_array.include?("a").should be
|
89
|
+
e.first_bad_position.should == 3
|
90
|
+
else
|
91
|
+
e = capture_exception(FlexColumns::Errors::UnparseableJsonInDatabaseError) { user2.wants_email }
|
92
|
+
e.message.should match(/user.*id.*#{user2.id}/i)
|
93
|
+
e.message.should match(/abc.*def/i)
|
94
|
+
e.message.should match(/JSON::ParserError/i)
|
95
|
+
|
96
|
+
e.data_source.should be(user2.user_attributes)
|
97
|
+
e.raw_string.should match(/abc.*def/i)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should fail before storing if the JSON produced is too long for the column" do
|
102
|
+
user = ::User.new
|
103
|
+
user.name = 'User 1'
|
104
|
+
user.wants_email = 'aaa' * 10000
|
105
|
+
|
106
|
+
e = capture_exception(FlexColumns::Errors::JsonTooLongError) { user.save! }
|
107
|
+
e.message.should match(/user_attributes/i)
|
108
|
+
e.message.should match(/100/i)
|
109
|
+
e.message.should match(/aaa/i)
|
110
|
+
e.message.should match(/30[0-9][0-9][0-9]/i)
|
111
|
+
e.message.length.should < 1000
|
112
|
+
|
113
|
+
e.data_source.should be(user.user_attributes)
|
114
|
+
e.limit.should == 100
|
115
|
+
e.json_string.length.should > 30000
|
116
|
+
end
|
117
|
+
end
|