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,253 @@
|
|
1
|
+
require 'flex_columns'
|
2
|
+
require 'flex_columns/helpers/exception_helpers'
|
3
|
+
|
4
|
+
describe FlexColumns::Contents::FlexColumnContentsBase do
|
5
|
+
include FlexColumns::Helpers::ExceptionHelpers
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@klass = Class.new(FlexColumns::Contents::FlexColumnContentsBase)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should include ActiveModel::Validations" do
|
12
|
+
@klass.included_modules.map(&:name).include?("ActiveModel::Validations").should be
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should extend FlexColumnContentsClass" do
|
16
|
+
@klass.respond_to?(:is_flex_column_class?).should be
|
17
|
+
@klass.is_flex_column_class?.should be
|
18
|
+
end
|
19
|
+
|
20
|
+
context "with a set-up class" do
|
21
|
+
before :each do
|
22
|
+
@model_class = Class.new
|
23
|
+
allow(@klass).to receive(:model_class).with().and_return(@model_class)
|
24
|
+
allow(@klass).to receive(:column_name).with().and_return(:fcn)
|
25
|
+
|
26
|
+
@json_string = '{"foo":"bar","bar":"baz"}'
|
27
|
+
|
28
|
+
@model_instance = @model_class.new
|
29
|
+
allow(@model_instance).to receive(:[]).with(:fcn).and_return(@json_string)
|
30
|
+
|
31
|
+
@column_data = double("column_data")
|
32
|
+
end
|
33
|
+
|
34
|
+
def expect_column_data_creation(input)
|
35
|
+
expect(@klass).to receive(:_flex_columns_create_column_data).once do |*args|
|
36
|
+
args.length.should == 2
|
37
|
+
args[0].should == input
|
38
|
+
args[1].class.should be(@klass)
|
39
|
+
|
40
|
+
@column_data
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#initialize" do
|
45
|
+
it "should accept nil" do
|
46
|
+
expect_column_data_creation(nil)
|
47
|
+
@klass.new(nil)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should accept a String" do
|
51
|
+
source_string = " foo "
|
52
|
+
expect_column_data_creation(source_string)
|
53
|
+
@klass.new(source_string)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should accept a model of the right class" do
|
57
|
+
expect_column_data_creation(@json_string)
|
58
|
+
@klass.new(@model_instance)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should not accept a model of the wrong class" do
|
62
|
+
wrong_model_class = Class.new
|
63
|
+
allow(@model_class).to receive(:name).and_return("bongo")
|
64
|
+
wrong_model_instance = wrong_model_class.new
|
65
|
+
class << wrong_model_instance
|
66
|
+
def to_s
|
67
|
+
"whatevs"
|
68
|
+
end
|
69
|
+
def inspect
|
70
|
+
"whatevs"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
e = capture_exception(ArgumentError) { @klass.new(wrong_model_instance) }
|
74
|
+
e.message.should match(/bongo/i)
|
75
|
+
e.message.should match(/whatevs/i)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#describe_flex_column_data_source" do
|
80
|
+
it "should work with a model instance" do
|
81
|
+
expect_column_data_creation(@json_string)
|
82
|
+
@instance = @klass.new(@model_instance)
|
83
|
+
|
84
|
+
allow(@model_class).to receive(:name).with().and_return("mcname")
|
85
|
+
allow(@model_instance).to receive(:id).with().and_return("mcid")
|
86
|
+
s = @instance.describe_flex_column_data_source
|
87
|
+
|
88
|
+
s.should match(/mcname/)
|
89
|
+
s.should match(/mcid/)
|
90
|
+
s.should match(/fcn/)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should work with a raw string" do
|
94
|
+
expect_column_data_creation(@json_string)
|
95
|
+
@instance = @klass.new(@json_string)
|
96
|
+
|
97
|
+
allow(@model_class).to receive(:name).with().and_return("mcname")
|
98
|
+
s = @instance.describe_flex_column_data_source
|
99
|
+
s.should match(/mcname/)
|
100
|
+
s.should match(/fcn/)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#notification_hash_for_flex_column_data_source" do
|
105
|
+
it "should work with a model instance" do
|
106
|
+
expect_column_data_creation(@json_string)
|
107
|
+
@instance = @klass.new(@model_instance)
|
108
|
+
|
109
|
+
h = @instance.notification_hash_for_flex_column_data_source
|
110
|
+
h.keys.sort_by(&:to_s).should == [ :model_class, :column_name, :model ].sort_by(&:to_s)
|
111
|
+
h[:model_class].should be(@model_class)
|
112
|
+
h[:column_name].should == :fcn
|
113
|
+
h[:model].should be(@model_instance)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should work with a raw string" do
|
117
|
+
expect_column_data_creation(@json_string)
|
118
|
+
@instance = @klass.new(@json_string)
|
119
|
+
|
120
|
+
h = @instance.notification_hash_for_flex_column_data_source
|
121
|
+
h.keys.sort_by(&:to_s).should == [ :model_class, :column_name, :source ].sort_by(&:to_s)
|
122
|
+
h[:model_class].should be(@model_class)
|
123
|
+
h[:column_name].should == :fcn
|
124
|
+
h[:source].should == @json_string
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "#before_validation!" do
|
129
|
+
it "should do nothing if created with a raw string" do
|
130
|
+
expect_column_data_creation(@json_string)
|
131
|
+
@instance = @klass.new(@json_string)
|
132
|
+
@instance.before_validation!
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should do nothing if the instance is valid" do
|
136
|
+
expect_column_data_creation(@json_string)
|
137
|
+
@instance = @klass.new(@model_instance)
|
138
|
+
|
139
|
+
expect(@instance).to receive(:valid?).once.with().and_return(true)
|
140
|
+
@instance.before_validation!
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should copy errors over if the instance is invalid" do
|
144
|
+
expect_column_data_creation(@json_string)
|
145
|
+
@instance = @klass.new(@model_instance)
|
146
|
+
|
147
|
+
expect(@instance).to receive(:valid?).once.with().and_return(false)
|
148
|
+
allow(@instance).to receive(:errors).with().and_return({ :e1 => :m1, :e2 => :m2 })
|
149
|
+
|
150
|
+
errors = Object.new
|
151
|
+
class << errors
|
152
|
+
def add(k, v)
|
153
|
+
@_errors ||= [ ]
|
154
|
+
@_errors << [ k, v ]
|
155
|
+
end
|
156
|
+
|
157
|
+
def all_errors
|
158
|
+
@_errors
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
allow(@model_instance).to receive(:errors).with().and_return(errors)
|
163
|
+
|
164
|
+
@instance.before_validation!
|
165
|
+
all_errors = errors.all_errors
|
166
|
+
all_errors.length.should == 2
|
167
|
+
|
168
|
+
e1 = all_errors.detect { |e| e[0] == "fcn.e1" }
|
169
|
+
e1[1].should == :m1
|
170
|
+
|
171
|
+
e2 = all_errors.detect { |e| e[0] == "fcn.e2" }
|
172
|
+
e2[1].should == :m2
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "#before_save!" do
|
177
|
+
it "should do nothing if created with a raw string" do
|
178
|
+
expect_column_data_creation(@json_string)
|
179
|
+
@instance = @klass.new(@json_string)
|
180
|
+
|
181
|
+
@instance.before_save!
|
182
|
+
end
|
183
|
+
|
184
|
+
context "with a valid instance" do
|
185
|
+
before :each do
|
186
|
+
expect_column_data_creation(@json_string)
|
187
|
+
@instance = @klass.new(@model_instance)
|
188
|
+
allow(@model_instance).to receive(:_flex_column_object_for).with(:fcn, false).and_return(@instance)
|
189
|
+
end
|
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
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should save if the class tells it to" do
|
199
|
+
expect(@klass).to receive(:requires_serialization_on_save?).once.with(@model_instance).and_return(true)
|
200
|
+
expect(@column_data).to receive(:to_stored_data).once.with().and_return("somestoreddata")
|
201
|
+
expect(@model_instance).to receive(:[]=).once.with(:fcn, "somestoreddata")
|
202
|
+
@instance.before_save!
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should not save if the class tells it not to" do
|
206
|
+
expect(@klass).to receive(:requires_serialization_on_save?).once.with(@model_instance).and_return(false)
|
207
|
+
@instance.before_save!
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "with a valid instance" do
|
213
|
+
before :each do
|
214
|
+
expect_column_data_creation(@json_string)
|
215
|
+
@instance = @klass.new(@model_instance)
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should return itself for #to_model" do
|
219
|
+
@instance.to_model.should be(@instance)
|
220
|
+
end
|
221
|
+
|
222
|
+
it "should delegate to the column data on []" do
|
223
|
+
expect(@column_data).to receive(:[]).once.with(:xxx).and_return(:yyy)
|
224
|
+
@instance[:xxx].should == :yyy
|
225
|
+
end
|
226
|
+
|
227
|
+
it "should delegate to the column data on []=" do
|
228
|
+
expect(@column_data).to receive(:[]=).once.with(:xxx, :yyy).and_return(:zzz)
|
229
|
+
(@instance[:xxx] = :yyy).should == :yyy
|
230
|
+
end
|
231
|
+
|
232
|
+
it "should delegate to the column data on #touch!" do
|
233
|
+
expect(@column_data).to receive(:touch!).once.with()
|
234
|
+
@instance.touch!
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should delegate to the column data on #to_json" do
|
238
|
+
expect(@column_data).to receive(:to_json).once.with().and_return("somejsondata")
|
239
|
+
@instance.to_json.should == "somejsondata"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "should delegate to the column data on #to_stored_data" do
|
243
|
+
expect(@column_data).to receive(:to_stored_data).once.with().and_return("somestoreddata")
|
244
|
+
@instance.to_stored_data.should == "somestoreddata"
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should delegate to the column data on #keys" do
|
248
|
+
expect(@column_data).to receive(:keys).once.with().and_return([ :a, :z, :q ])
|
249
|
+
@instance.keys.should == [ :a, :z, :q ]
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
@@ -0,0 +1,617 @@
|
|
1
|
+
require 'flex_columns'
|
2
|
+
require 'flex_columns/helpers/exception_helpers'
|
3
|
+
|
4
|
+
describe FlexColumns::Definition::FieldDefinition do
|
5
|
+
include FlexColumns::Helpers::ExceptionHelpers
|
6
|
+
|
7
|
+
def klass
|
8
|
+
FlexColumns::Definition::FieldDefinition
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should normalize names properly" do
|
12
|
+
lambda { klass.normalize_name(nil) }.should raise_error(ArgumentError)
|
13
|
+
lambda { klass.normalize_name(123) }.should raise_error(ArgumentError)
|
14
|
+
klass.normalize_name(" FoO ").should == :foo
|
15
|
+
klass.normalize_name(:foo).should == :foo
|
16
|
+
end
|
17
|
+
|
18
|
+
before :each do
|
19
|
+
@flex_column_class = double("flex_column_class")
|
20
|
+
allow(@flex_column_class).to receive(:is_flex_column_class?).with().and_return(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#initialize" do
|
24
|
+
it "should validate its arguments" do
|
25
|
+
non_fcc = double("flex_column_class")
|
26
|
+
lambda { klass.new(non_fcc, :foo, [ ], { }) }.should raise_error(ArgumentError)
|
27
|
+
|
28
|
+
non_fcc = double("flex_column_class")
|
29
|
+
allow(non_fcc).to receive(:is_flex_column_class?).with().and_return(false)
|
30
|
+
lambda { klass.new(non_fcc, :foo, [ ], { }) }.should raise_error(ArgumentError)
|
31
|
+
|
32
|
+
lambda { klass.new(@flex_column_class, :foo, [ ], { :foo => :bar }) }.should raise_error(ArgumentError)
|
33
|
+
|
34
|
+
lambda { klass.new(@flex_column_class, :foo, [ ], { :visibility => 123 }) }.should raise_error(ArgumentError)
|
35
|
+
lambda { klass.new(@flex_column_class, :foo, [ ], { :visibility => true }) }.should raise_error(ArgumentError)
|
36
|
+
|
37
|
+
lambda { klass.new(@flex_column_class, :foo, [ ], { :json => 123 }) }.should raise_error(ArgumentError)
|
38
|
+
|
39
|
+
lambda { klass.new(@flex_column_class, :foo, [ ], { :null => 123 }) }.should raise_error(ArgumentError)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should raise an error if there are additional arguments" do
|
43
|
+
expect(@flex_column_class).to receive(:validates).once.with(:foo, { :numericality => { :only_integer => true }})
|
44
|
+
lambda { klass.new(@flex_column_class, :foo, [ :integer, :bar ], { }) }.should raise_error(ArgumentError)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "types support" do
|
48
|
+
it "should raise an error if given an invalid type" do
|
49
|
+
lambda { klass.new(@flex_column_class, :foo, [ :bar ], { }) }.should raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should force a value to be present if :null => false" do
|
53
|
+
expect(@flex_column_class).to receive(:validates).once.with(:foo, { :presence => true })
|
54
|
+
klass.new(@flex_column_class, :foo, [ ], { :null => false })
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should force a value to be in an :enum list" do
|
58
|
+
expect(@flex_column_class).to receive(:validates).once.with(:foo, { :inclusion => { :in => %w{a b c} } })
|
59
|
+
klass.new(@flex_column_class, :foo, [ ], { :enum => %w{a b c} })
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should force a value to be of a maximum length" do
|
63
|
+
expect(@flex_column_class).to receive(:validates).once.with(:foo, { :length => { :maximum => 123 } })
|
64
|
+
klass.new(@flex_column_class, :foo, [ ], { :limit => 123 })
|
65
|
+
end
|
66
|
+
|
67
|
+
def expect_validation(type, arguments)
|
68
|
+
expect(@flex_column_class).to receive(:validates).once.with(:foo, arguments)
|
69
|
+
klass.new(@flex_column_class, :foo, [ type ], { })
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should validate integers properly" do
|
73
|
+
expect_validation(:integer, { :numericality => { :only_integer => true } })
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should validate floats properly" do
|
77
|
+
expect_validation(:float, { :numericality => true, :allow_nil => true })
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should validate decimals properly" do
|
81
|
+
expect_validation(:decimal, { :numericality => true, :allow_nil => true })
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should validate booleans properly" do
|
85
|
+
expect_validation(:boolean, { :inclusion => { :in => [ true, false, nil ] }})
|
86
|
+
end
|
87
|
+
|
88
|
+
def check_validation_block(type, input, expected_error)
|
89
|
+
validation_block = nil
|
90
|
+
expect(@flex_column_class).to receive(:validates_each).once.with(:foo) do |&block|
|
91
|
+
validation_block = block
|
92
|
+
end
|
93
|
+
|
94
|
+
klass.new(@flex_column_class, :foo, [ type.to_sym ], { })
|
95
|
+
|
96
|
+
record = double("record")
|
97
|
+
errors = double("errors")
|
98
|
+
|
99
|
+
allow(record).to receive(:errors).with().and_return(errors)
|
100
|
+
|
101
|
+
if expected_error
|
102
|
+
expect(errors).to receive(:add).once.with(:foo, expected_error)
|
103
|
+
end
|
104
|
+
|
105
|
+
validation_block.call(record, :foo, input)
|
106
|
+
end
|
107
|
+
|
108
|
+
%w{string text}.each do |type|
|
109
|
+
it "should validate fields of type #{type} properly" do
|
110
|
+
check_validation_block(type, "aaa", nil)
|
111
|
+
check_validation_block(type, :aaa, nil)
|
112
|
+
check_validation_block(type, nil, nil)
|
113
|
+
check_validation_block(type, 123, "must be a String")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should validate dates correctly" do
|
118
|
+
check_validation_block(:date, nil, nil)
|
119
|
+
check_validation_block(:date, Date.today, nil)
|
120
|
+
check_validation_block(:date, Time.now, "must be a Date")
|
121
|
+
check_validation_block(:date, DateTime.now, nil)
|
122
|
+
check_validation_block(:date, 123, "must be a Date")
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should validate times correctly" do
|
126
|
+
check_validation_block(:time, nil, nil)
|
127
|
+
check_validation_block(:time, Date.today, "must be a Time")
|
128
|
+
check_validation_block(:time, Time.now, nil)
|
129
|
+
check_validation_block(:time, DateTime.now, "must be a Time")
|
130
|
+
check_validation_block(:time, 123, "must be a Time")
|
131
|
+
end
|
132
|
+
|
133
|
+
%w{timestamp datetime}.each do |type|
|
134
|
+
it "should validate fields of type #{type} properly" do
|
135
|
+
check_validation_block(type, nil, nil)
|
136
|
+
check_validation_block(type, Time.now, nil)
|
137
|
+
|
138
|
+
check_validation_block(type, DateTime.now, nil)
|
139
|
+
|
140
|
+
check_validation_block(type, 123, "must be a Time or DateTime")
|
141
|
+
check_validation_block(type, Date.today, "must be a Time or DateTime")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should return the JSON storage name from #json_storage_name" do
|
148
|
+
klass.new(@flex_column_class, :foo, [ ], { }).json_storage_name.should == :foo
|
149
|
+
klass.new(@flex_column_class, :foo, [ ], { :json => :bar }).json_storage_name.should == :bar
|
150
|
+
end
|
151
|
+
|
152
|
+
describe "#add_methods_to_flex_column_class!" do
|
153
|
+
before :each do
|
154
|
+
@dmm = Class.new do
|
155
|
+
class << self; public :define_method, :private; end
|
156
|
+
|
157
|
+
def initialize(h)
|
158
|
+
@h = h
|
159
|
+
end
|
160
|
+
|
161
|
+
def [](x)
|
162
|
+
@h[x]
|
163
|
+
end
|
164
|
+
|
165
|
+
def []=(x, y)
|
166
|
+
@h[x] = y
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should define methods on the flex-column class" do
|
172
|
+
allow(@flex_column_class).to receive(:fields_are_private_by_default?).with().and_return(false)
|
173
|
+
klass.new(@flex_column_class, :foo, [ ], { :json => :bar }).add_methods_to_flex_column_class!(@dmm)
|
174
|
+
|
175
|
+
instance = @dmm.new(:foo => 123)
|
176
|
+
instance.foo.should == 123
|
177
|
+
instance.foo = 234
|
178
|
+
instance.foo.should == 234
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should define methods on the flex-column class privately if the flex-column says to" do
|
182
|
+
allow(@flex_column_class).to receive(:fields_are_private_by_default?).with().and_return(true)
|
183
|
+
klass.new(@flex_column_class, :foo, [ ], { :json => :bar }).add_methods_to_flex_column_class!(@dmm)
|
184
|
+
|
185
|
+
instance = @dmm.new(:foo => 123)
|
186
|
+
|
187
|
+
lambda { instance.foo }.should raise_error(NoMethodError)
|
188
|
+
lambda { instance.foo = 123 }.should raise_error(NoMethodError)
|
189
|
+
|
190
|
+
instance.send(:foo).should == 123
|
191
|
+
instance.send(:foo=, 234)
|
192
|
+
instance.send(:foo).should == 234
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should define methods on the flex-column class privately if the field says to" do
|
196
|
+
allow(@flex_column_class).to receive(:fields_are_private_by_default?).with().and_return(false)
|
197
|
+
klass.new(@flex_column_class, :foo, [ ], { :json => :bar, :visibility => :private }).add_methods_to_flex_column_class!(@dmm)
|
198
|
+
|
199
|
+
instance = @dmm.new(:foo => 123)
|
200
|
+
|
201
|
+
lambda { instance.foo }.should raise_error(NoMethodError)
|
202
|
+
lambda { instance.foo = 123 }.should raise_error(NoMethodError)
|
203
|
+
|
204
|
+
instance.send(:foo).should == 123
|
205
|
+
instance.send(:foo=, 234)
|
206
|
+
instance.send(:foo).should == 234
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should define methods on the flex-column class publicly if the field says to" do
|
210
|
+
allow(@flex_column_class).to receive(:fields_are_private_by_default?).with().and_return(true)
|
211
|
+
klass.new(@flex_column_class, :foo, [ ], { :json => :bar, :visibility => :public }).add_methods_to_flex_column_class!(@dmm)
|
212
|
+
|
213
|
+
instance = @dmm.new(:foo => 123)
|
214
|
+
instance.foo.should == 123
|
215
|
+
instance.foo = 234
|
216
|
+
instance.foo.should == 234
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "#add_methods_to_model_class!" do
|
221
|
+
before :each do
|
222
|
+
@dmm = Class.new do
|
223
|
+
class << self; public :define_method, :private; end
|
224
|
+
end
|
225
|
+
|
226
|
+
@model_class = double("model_class")
|
227
|
+
end
|
228
|
+
|
229
|
+
def check_add_methods_to_model_class(options)
|
230
|
+
create_options = options[:create_options]
|
231
|
+
fields_are_private_by_default = options[:fields_are_private_by_default] || false
|
232
|
+
safe_to_define = if options.has_key?(:safe_to_define) then options[:safe_to_define] else true end
|
233
|
+
delegation_type = options[:delegation_type]
|
234
|
+
delegation_prefix = options[:delegation_prefix]
|
235
|
+
method_name = options[:method_name]
|
236
|
+
should_be_private = options[:should_be_private]
|
237
|
+
method_should_exist = if options.has_key?(:method_should_exist) then options[:method_should_exist] else true end
|
238
|
+
|
239
|
+
flex_instance = { }
|
240
|
+
field = klass.new(@flex_column_class, :foo, [ ], create_options)
|
241
|
+
allow(@flex_column_class).to receive(:fields_are_private_by_default?).with().and_return(fields_are_private_by_default)
|
242
|
+
|
243
|
+
allow(@model_class).to receive(:_flex_columns_safe_to_define_method?).with(method_name.to_sym).and_return(safe_to_define)
|
244
|
+
allow(@model_class).to receive(:_flex_columns_safe_to_define_method?).with("#{method_name}=".to_sym).and_return(safe_to_define)
|
245
|
+
allow(@flex_column_class).to receive(:delegation_type).with().and_return(delegation_type)
|
246
|
+
allow(@flex_column_class).to receive(:delegation_prefix).with().and_return(delegation_prefix)
|
247
|
+
|
248
|
+
field.add_methods_to_model_class!(@dmm, @model_class)
|
249
|
+
|
250
|
+
instance = @dmm.new
|
251
|
+
allow(@flex_column_class).to receive(:object_for).with(instance).and_return(flex_instance)
|
252
|
+
|
253
|
+
if (! method_should_exist)
|
254
|
+
lambda { instance.send(method_name) }.should raise_error(NoMethodError)
|
255
|
+
lambda { instance.send("#{method_name}=", 123) }.should raise_error(NoMethodError)
|
256
|
+
elsif should_be_private
|
257
|
+
lambda { eval("instance.#{method_name}") }.should raise_error(NoMethodError)
|
258
|
+
lambda { eval("instance.#{method_name} = 123") }.should raise_error(NoMethodError)
|
259
|
+
instance.send(method_name).should be_nil
|
260
|
+
instance.send("#{method_name}=", 123).should == 123
|
261
|
+
instance.send(method_name).should == 123
|
262
|
+
else
|
263
|
+
eval("instance.#{method_name}").should be_nil
|
264
|
+
eval("instance.#{method_name} = 123").should == 123
|
265
|
+
eval("instance.#{method_name}").should == 123
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should define a very standard public method just fine" do
|
270
|
+
check_add_methods_to_model_class(
|
271
|
+
:create_options => { },
|
272
|
+
:fields_are_private_by_default => false,
|
273
|
+
:safe_to_define => true,
|
274
|
+
:delegation_type => :public,
|
275
|
+
:delegation_prefix => nil,
|
276
|
+
:method_name => :foo,
|
277
|
+
:should_be_private => false
|
278
|
+
)
|
279
|
+
end
|
280
|
+
|
281
|
+
it "should define a private method if asked to on the field" do
|
282
|
+
check_add_methods_to_model_class(
|
283
|
+
:create_options => { :visibility => :private },
|
284
|
+
:fields_are_private_by_default => false,
|
285
|
+
:safe_to_define => true,
|
286
|
+
:delegation_type => :public,
|
287
|
+
:delegation_prefix => nil,
|
288
|
+
:method_name => :foo,
|
289
|
+
:should_be_private => true
|
290
|
+
)
|
291
|
+
end
|
292
|
+
|
293
|
+
it "should define a private method if fields are private by default" do
|
294
|
+
check_add_methods_to_model_class(
|
295
|
+
:create_options => { },
|
296
|
+
:fields_are_private_by_default => true,
|
297
|
+
:safe_to_define => true,
|
298
|
+
:delegation_type => :public,
|
299
|
+
:delegation_prefix => nil,
|
300
|
+
:method_name => :foo,
|
301
|
+
:should_be_private => true
|
302
|
+
)
|
303
|
+
end
|
304
|
+
|
305
|
+
it "should define a private method if fields are private by default, but the specific field is public" do
|
306
|
+
check_add_methods_to_model_class(
|
307
|
+
:create_options => { :visibility => :public },
|
308
|
+
:fields_are_private_by_default => true,
|
309
|
+
:safe_to_define => true,
|
310
|
+
:delegation_type => :public,
|
311
|
+
:delegation_prefix => nil,
|
312
|
+
:method_name => :foo,
|
313
|
+
:should_be_private => false
|
314
|
+
)
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should define a private method if delegation is private" do
|
318
|
+
check_add_methods_to_model_class(
|
319
|
+
:create_options => { },
|
320
|
+
:fields_are_private_by_default => false,
|
321
|
+
:safe_to_define => true,
|
322
|
+
:delegation_type => :private,
|
323
|
+
:delegation_prefix => nil,
|
324
|
+
:method_name => :foo,
|
325
|
+
:should_be_private => true
|
326
|
+
)
|
327
|
+
end
|
328
|
+
|
329
|
+
it "should not define a method if delegation is off" do
|
330
|
+
check_add_methods_to_model_class(
|
331
|
+
:create_options => { },
|
332
|
+
:fields_are_private_by_default => false,
|
333
|
+
:safe_to_define => true,
|
334
|
+
:delegation_type => nil,
|
335
|
+
:delegation_prefix => nil,
|
336
|
+
:method_name => :foo,
|
337
|
+
:should_be_private => false,
|
338
|
+
:method_should_exist => false
|
339
|
+
)
|
340
|
+
end
|
341
|
+
|
342
|
+
it "should not define a method if it's not safe to do so" do
|
343
|
+
check_add_methods_to_model_class(
|
344
|
+
:create_options => { },
|
345
|
+
:fields_are_private_by_default => false,
|
346
|
+
:safe_to_define => false,
|
347
|
+
:delegation_type => :public,
|
348
|
+
:delegation_prefix => nil,
|
349
|
+
:method_name => :foo,
|
350
|
+
:should_be_private => false,
|
351
|
+
:method_should_exist => false
|
352
|
+
)
|
353
|
+
end
|
354
|
+
|
355
|
+
it "should prefix the method if requested" do
|
356
|
+
check_add_methods_to_model_class(
|
357
|
+
:create_options => { },
|
358
|
+
:fields_are_private_by_default => false,
|
359
|
+
:safe_to_define => true,
|
360
|
+
:delegation_type => :public,
|
361
|
+
:delegation_prefix => :aaa,
|
362
|
+
:method_name => :aaa_foo,
|
363
|
+
:should_be_private => false,
|
364
|
+
:method_should_exist => true
|
365
|
+
)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
describe "#add_methods_to_included_class!" do
|
370
|
+
def check_add_methods_to_included_class(options)
|
371
|
+
delegation_type = options[:delegation_type]
|
372
|
+
delegation_prefix = options[:delegation_prefix]
|
373
|
+
create_options = options[:create_options]
|
374
|
+
fields_are_private_by_default = options[:fields_are_private_by_default] || false
|
375
|
+
method_name = options[:method_name]
|
376
|
+
safe_to_define = options[:safe_to_define]
|
377
|
+
include_options = options[:include_options]
|
378
|
+
method_should_exist = options[:method_should_exist]
|
379
|
+
should_be_private = options[:should_be_private]
|
380
|
+
|
381
|
+
allow(@flex_column_class).to receive(:delegation_type).with().and_return(delegation_type)
|
382
|
+
allow(@flex_column_class).to receive(:delegation_prefix).with().and_return(delegation_prefix)
|
383
|
+
allow(@flex_column_class).to receive(:fields_are_private_by_default?).with().and_return(fields_are_private_by_default)
|
384
|
+
|
385
|
+
@model_class = double("model_class")
|
386
|
+
allow(@model_class).to receive(:name).with().and_return("mcname")
|
387
|
+
allow(@flex_column_class).to receive(:model_class).with().and_return(@model_class)
|
388
|
+
allow(@flex_column_class).to receive(:column_name).with().and_return(:colname)
|
389
|
+
|
390
|
+
target_class = double("target_class")
|
391
|
+
allow(target_class).to receive(:_flex_columns_safe_to_define_method?).with(method_name.to_sym).and_return(safe_to_define)
|
392
|
+
allow(target_class).to receive(:_flex_columns_safe_to_define_method?).with("#{method_name}=".to_sym).and_return(safe_to_define)
|
393
|
+
|
394
|
+
associated_object = double("associated_object")
|
395
|
+
|
396
|
+
@dmm = Class.new do
|
397
|
+
def ao=(x)
|
398
|
+
@associated_object = x
|
399
|
+
end
|
400
|
+
|
401
|
+
def ao
|
402
|
+
@associated_object
|
403
|
+
end
|
404
|
+
|
405
|
+
def bao=(x)
|
406
|
+
@build_associated_object = x
|
407
|
+
end
|
408
|
+
|
409
|
+
def build_ao
|
410
|
+
@build_associated_object
|
411
|
+
end
|
412
|
+
|
413
|
+
class << self; public :define_method, :private; end
|
414
|
+
end
|
415
|
+
|
416
|
+
field = klass.new(@flex_column_class, :foo, [ ], create_options)
|
417
|
+
field.add_methods_to_included_class!(@dmm, :ao, target_class, include_options)
|
418
|
+
|
419
|
+
instance = @dmm.new
|
420
|
+
|
421
|
+
if options[:use_build]
|
422
|
+
instance.bao = associated_object
|
423
|
+
else
|
424
|
+
instance.ao = associated_object
|
425
|
+
end
|
426
|
+
|
427
|
+
flex_instance = Object.new
|
428
|
+
class << flex_instance
|
429
|
+
def foo=(x)
|
430
|
+
@foo = x
|
431
|
+
end
|
432
|
+
|
433
|
+
def foo
|
434
|
+
@foo
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
allow(associated_object).to receive(:colname).and_return(flex_instance)
|
439
|
+
|
440
|
+
if (! method_should_exist)
|
441
|
+
lambda { instance.send(method_name) }.should raise_error(NoMethodError)
|
442
|
+
lambda { instance.send("#{method_name}=", 123) }.should raise_error(NoMethodError)
|
443
|
+
elsif should_be_private
|
444
|
+
lambda { eval("instance.#{method_name}") }.should raise_error(NoMethodError)
|
445
|
+
lambda { eval("instance.#{method_name} = 123") }.should raise_error(NoMethodError)
|
446
|
+
instance.send(method_name).should be_nil
|
447
|
+
instance.send("#{method_name}=", 123).should == 123
|
448
|
+
instance.send(method_name).should == 123
|
449
|
+
else
|
450
|
+
eval("instance.#{method_name}").should be_nil
|
451
|
+
eval("instance.#{method_name} = 123").should == 123
|
452
|
+
eval("instance.#{method_name}").should == 123
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should define a very standard public method just fine" do
|
457
|
+
check_add_methods_to_included_class(
|
458
|
+
:delegation_type => :public,
|
459
|
+
:delegation_prefix => nil,
|
460
|
+
:create_options => { },
|
461
|
+
:fields_are_private_by_default => false,
|
462
|
+
:method_name => :foo,
|
463
|
+
:safe_to_define => true,
|
464
|
+
:include_options => { },
|
465
|
+
:method_should_exist => true,
|
466
|
+
:should_be_private => false
|
467
|
+
)
|
468
|
+
end
|
469
|
+
|
470
|
+
it "should define a private method if the field is private" do
|
471
|
+
check_add_methods_to_included_class(
|
472
|
+
:delegation_type => :public,
|
473
|
+
:delegation_prefix => nil,
|
474
|
+
:create_options => { :visibility => :private },
|
475
|
+
:fields_are_private_by_default => false,
|
476
|
+
:method_name => :foo,
|
477
|
+
:safe_to_define => true,
|
478
|
+
:include_options => { },
|
479
|
+
:method_should_exist => true,
|
480
|
+
:should_be_private => true
|
481
|
+
)
|
482
|
+
end
|
483
|
+
|
484
|
+
it "should define a private method if the flex column is private" do
|
485
|
+
check_add_methods_to_included_class(
|
486
|
+
:delegation_type => :public,
|
487
|
+
:delegation_prefix => nil,
|
488
|
+
:create_options => { },
|
489
|
+
:fields_are_private_by_default => true,
|
490
|
+
:method_name => :foo,
|
491
|
+
:safe_to_define => true,
|
492
|
+
:include_options => { },
|
493
|
+
:method_should_exist => true,
|
494
|
+
:should_be_private => true
|
495
|
+
)
|
496
|
+
end
|
497
|
+
|
498
|
+
it "should define a public method if the flex column is private but the field is public" do
|
499
|
+
check_add_methods_to_included_class(
|
500
|
+
:delegation_type => :public,
|
501
|
+
:delegation_prefix => nil,
|
502
|
+
:create_options => { :visibility => :public },
|
503
|
+
:fields_are_private_by_default => true,
|
504
|
+
:method_name => :foo,
|
505
|
+
:safe_to_define => true,
|
506
|
+
:include_options => { },
|
507
|
+
:method_should_exist => true,
|
508
|
+
:should_be_private => false
|
509
|
+
)
|
510
|
+
end
|
511
|
+
|
512
|
+
it "should default to the flex-column prefix" do
|
513
|
+
check_add_methods_to_included_class(
|
514
|
+
:delegation_type => :public,
|
515
|
+
:delegation_prefix => :aaa,
|
516
|
+
:create_options => { },
|
517
|
+
:fields_are_private_by_default => false,
|
518
|
+
:method_name => :aaa_foo,
|
519
|
+
:safe_to_define => true,
|
520
|
+
:include_options => { },
|
521
|
+
:method_should_exist => true,
|
522
|
+
:should_be_private => false
|
523
|
+
)
|
524
|
+
end
|
525
|
+
|
526
|
+
it "should override to the flex-column prefix if requested" do
|
527
|
+
check_add_methods_to_included_class(
|
528
|
+
:delegation_type => :public,
|
529
|
+
:delegation_prefix => :aaa,
|
530
|
+
:create_options => { },
|
531
|
+
:fields_are_private_by_default => false,
|
532
|
+
:method_name => :bbb_foo,
|
533
|
+
:safe_to_define => true,
|
534
|
+
:include_options => { :prefix => :bbb },
|
535
|
+
:method_should_exist => true,
|
536
|
+
:should_be_private => false
|
537
|
+
)
|
538
|
+
end
|
539
|
+
|
540
|
+
it "should override to the flex-column prefix with no prefix if requested" do
|
541
|
+
check_add_methods_to_included_class(
|
542
|
+
:delegation_type => :public,
|
543
|
+
:delegation_prefix => :aaa,
|
544
|
+
:create_options => { },
|
545
|
+
:fields_are_private_by_default => false,
|
546
|
+
:method_name => :foo,
|
547
|
+
:safe_to_define => true,
|
548
|
+
:include_options => { :prefix => nil },
|
549
|
+
:method_should_exist => true,
|
550
|
+
:should_be_private => false
|
551
|
+
)
|
552
|
+
end
|
553
|
+
|
554
|
+
it "should raise if you ask for public methods from a private field" do
|
555
|
+
e = capture_exception(ArgumentError) do
|
556
|
+
check_add_methods_to_included_class(
|
557
|
+
:delegation_type => :public,
|
558
|
+
:delegation_prefix => nil,
|
559
|
+
:create_options => { },
|
560
|
+
:fields_are_private_by_default => true,
|
561
|
+
:method_name => :foo,
|
562
|
+
:safe_to_define => true,
|
563
|
+
:include_options => { :visibility => :public },
|
564
|
+
:method_should_exist => true,
|
565
|
+
:should_be_private => true
|
566
|
+
)
|
567
|
+
end
|
568
|
+
|
569
|
+
e.message.should match(/ao/i)
|
570
|
+
e.message.should match(/mcname/i)
|
571
|
+
e.message.should match(/colname/i)
|
572
|
+
end
|
573
|
+
|
574
|
+
it "should skip defining methods if there's no delegation" do
|
575
|
+
check_add_methods_to_included_class(
|
576
|
+
:delegation_type => nil,
|
577
|
+
:delegation_prefix => nil,
|
578
|
+
:create_options => { },
|
579
|
+
:fields_are_private_by_default => false,
|
580
|
+
:method_name => :foo,
|
581
|
+
:safe_to_define => true,
|
582
|
+
:include_options => { },
|
583
|
+
:method_should_exist => false,
|
584
|
+
:should_be_private => false
|
585
|
+
)
|
586
|
+
end
|
587
|
+
|
588
|
+
it "should skip defining methods if it's not safe" do
|
589
|
+
check_add_methods_to_included_class(
|
590
|
+
:delegation_type => :public,
|
591
|
+
:delegation_prefix => nil,
|
592
|
+
:create_options => { },
|
593
|
+
:fields_are_private_by_default => false,
|
594
|
+
:method_name => :foo,
|
595
|
+
:safe_to_define => false,
|
596
|
+
:include_options => { },
|
597
|
+
:method_should_exist => false,
|
598
|
+
:should_be_private => false
|
599
|
+
)
|
600
|
+
end
|
601
|
+
|
602
|
+
it "should build the association if needed" do
|
603
|
+
check_add_methods_to_included_class(
|
604
|
+
:delegation_type => :public,
|
605
|
+
:delegation_prefix => nil,
|
606
|
+
:create_options => { },
|
607
|
+
:fields_are_private_by_default => false,
|
608
|
+
:method_name => :foo,
|
609
|
+
:safe_to_define => true,
|
610
|
+
:include_options => { },
|
611
|
+
:method_should_exist => true,
|
612
|
+
:should_be_private => false,
|
613
|
+
:use_build => true
|
614
|
+
)
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|