attribute-filters 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +388 -0
- data/Gemfile.lock +14 -14
- data/Manifest.txt +8 -0
- data/README.md +45 -31
- data/attribute-filters.gemspec +3 -3
- data/docs/HISTORY +63 -27
- data/docs/TODO +17 -2
- data/docs/USAGE.md +1161 -76
- data/lib/attribute-filters.rb +4 -0
- data/lib/attribute-filters/attribute_set.rb +92 -1
- data/lib/attribute-filters/attribute_set_annotations.rb +259 -0
- data/lib/attribute-filters/attribute_set_attrquery.rb +9 -8
- data/lib/attribute-filters/attribute_set_enum.rb +76 -45
- data/lib/attribute-filters/attribute_set_query.rb +65 -7
- data/lib/attribute-filters/backports.rb +37 -0
- data/lib/attribute-filters/common_filters.rb +46 -128
- data/lib/attribute-filters/common_filters/case.rb +159 -0
- data/lib/attribute-filters/common_filters/join.rb +98 -0
- data/lib/attribute-filters/common_filters/split.rb +130 -0
- data/lib/attribute-filters/common_filters/squeeze.rb +75 -0
- data/lib/attribute-filters/common_filters/strip.rb +46 -0
- data/lib/attribute-filters/dsl_attr_virtual.rb +50 -0
- data/lib/attribute-filters/dsl_filters.rb +40 -29
- data/lib/attribute-filters/dsl_sets.rb +122 -32
- data/lib/attribute-filters/helpers.rb +14 -0
- data/lib/attribute-filters/version.rb +1 -1
- data/spec/attribute-filters_spec.rb +334 -13
- data/spec/spec_helper.rb +9 -20
- metadata +37 -29
- metadata.gz.sig +0 -0
@@ -22,18 +22,29 @@ module ActiveModel
|
|
22
22
|
# Returns the attribute set of the given name or the set containing
|
23
23
|
# all attributes (if the argument is not given).
|
24
24
|
#
|
25
|
+
# If the given +set_name+ is a kind of String or Symbol then the method
|
26
|
+
# returns a copy of a set that is stored within a model class. The copy
|
27
|
+
# is wrapped in a `AttributeSet::AttrQuery` instance.
|
28
|
+
#
|
29
|
+
# If the argument is a kind of {AttributeSet} then the local set
|
30
|
+
# is taken and wrapped in a {AttributeSet::AttrQuery} instance.
|
31
|
+
#
|
32
|
+
# If the argument is other kind than the specified above then
|
33
|
+
# the method tries to initialize new, local set object and wraps
|
34
|
+
# it in a `AttributeSet::AttrQuery` instance.
|
35
|
+
#
|
25
36
|
# @note The returned value is a duplicate. Adding or removing
|
26
|
-
# elements to it will have no effect
|
27
|
-
# is possible on a class-level only, since attribute sets
|
37
|
+
# elements to it will have no effect on class-level set.
|
38
|
+
# Altering attribute sets is possible on a class-level only, since attribute sets
|
28
39
|
# are part of models' interfaces.
|
29
40
|
#
|
30
|
-
# @param set_name [Symbol] name of attribute set
|
41
|
+
# @param set_name [Symbol] name of attribute set, attribute object or any object that can initialize a set
|
31
42
|
# @return [AttributeSet] attribute set
|
32
43
|
def attribute_set(set_name=nil)
|
33
44
|
if set_name.nil?
|
34
45
|
all_attributes
|
35
46
|
else
|
36
|
-
ActiveModel::AttributeSet::Query.new(
|
47
|
+
ActiveModel::AttributeSet::Query.new(set_name, self)
|
37
48
|
end
|
38
49
|
end
|
39
50
|
alias_method :attributes_that_are, :attribute_set
|
@@ -49,42 +60,83 @@ module ActiveModel
|
|
49
60
|
alias_method :attributes_set, :attribute_set
|
50
61
|
alias_method :properties_that, :attribute_set
|
51
62
|
|
52
|
-
# Returns
|
63
|
+
# Returns the attribute set of the given name without wrapping
|
64
|
+
# the result in proxy methods.
|
65
|
+
#
|
66
|
+
# @note The returned value is a duplicate. Adding or removing
|
67
|
+
# elements to it will have no effect. Altering attribute sets
|
68
|
+
# is possible on a class-level only, since attribute sets
|
69
|
+
# are part of models' interfaces.
|
70
|
+
#
|
71
|
+
# @param set_name [Symbol] name of attribute set
|
72
|
+
# @return [AttributeSet] attribute set
|
73
|
+
def attribute_set_simple(set_name)
|
74
|
+
self.class.attribute_set(set_name).dup
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns a set containing all known attributes
|
78
|
+
# without wrapping it in a proxy.
|
79
|
+
#
|
80
|
+
# @return [AttributeSet] attribute set
|
81
|
+
def all_attributes_simple(no_presence_check = true)
|
82
|
+
(ActiveModel::AttributeSet.new(attributes.keys) +
|
83
|
+
all_accessible_attributes(true) +
|
84
|
+
all_protected_attributes(true) +
|
85
|
+
__vatrf(no_presence_check)).delete("")
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a set containing all known attributes.
|
89
|
+
#
|
53
90
|
# @return [AttributeSet] attribute set
|
54
91
|
def all_attributes
|
55
|
-
ActiveModel::AttributeSet::Query.new(
|
92
|
+
ActiveModel::AttributeSet::Query.new(all_attributes_simple, self)
|
56
93
|
end
|
57
94
|
alias_method :all_attributes_set, :all_attributes
|
58
95
|
|
59
96
|
# Returns a set containing all accessible attributes.
|
97
|
+
#
|
98
|
+
# @param simple [Boolean] optional parameter that disables
|
99
|
+
# wrapping a resulting set in a proxy (defaults to +false+)
|
60
100
|
# @return [AttributeSet] attribute set
|
61
|
-
def all_accessible_attributes
|
62
|
-
|
101
|
+
def all_accessible_attributes(simple = false)
|
102
|
+
c = self.class.class_eval { respond_to?(:accessible_attributes) ? accessible_attributes : [] }
|
103
|
+
simple ? AttributeSet.new(c) : AttributeSet::Query.new(c)
|
63
104
|
end
|
64
105
|
alias_method :accessible_attributes_set, :all_accessible_attributes
|
65
106
|
|
66
107
|
# Returns a set containing all protected attributes.
|
108
|
+
#
|
109
|
+
# @param simple [Boolean] optional parameter that disables
|
110
|
+
# wrapping a resulting set in a proxy (defaults to +false+)
|
67
111
|
# @return [AttributeSet] attribute set
|
68
|
-
def all_protected_attributes
|
69
|
-
|
112
|
+
def all_protected_attributes(simple = false)
|
113
|
+
c = self.class.class_eval { respond_to?(:protected_attributes) ? protected_attributes : [] }
|
114
|
+
simple ? AttributeSet.new(c) : AttributeSet::Query.new(c)
|
70
115
|
end
|
71
116
|
alias_method :protected_attributes_set, :all_protected_attributes
|
72
117
|
|
73
118
|
# Returns a set containing all attributes that are not accessible attributes.
|
74
119
|
# @return [AttributeSet] attribute set
|
75
120
|
def all_inaccessible_attributes
|
76
|
-
all_attributes -
|
121
|
+
all_attributes - all_accessible_attributes(true)
|
77
122
|
end
|
78
123
|
alias_method :all_non_accessible_attributes, :all_inaccessible_attributes
|
79
124
|
alias_method :inaccessible_attributes_set, :all_inaccessible_attributes
|
80
125
|
|
81
126
|
# Gets all the defined attribute sets.
|
127
|
+
#
|
82
128
|
# @note Use +key+ method explicitly to check if the given set exists. The hash returned by this method
|
83
129
|
# will always return {AttributeSet} object. If there is no such set defined then the returned,
|
84
130
|
# matching set will be empty.
|
131
|
+
#
|
85
132
|
# @return [Hash{Symbol => AttributeSet<String>}] the collection of attribute sets indexed by their names
|
86
133
|
def attribute_sets
|
87
|
-
self.class.attribute_sets
|
134
|
+
s = self.class.attribute_sets
|
135
|
+
s.merge!(s) do |set_name, set_object|
|
136
|
+
ActiveModel::AttributeSet::Query.new(set_object, self)
|
137
|
+
end
|
138
|
+
s.default = ActiveModel::AttributeSet::Query.new(ActiveModel::AttributeSet.new.freeze, self)
|
139
|
+
s
|
88
140
|
end
|
89
141
|
alias_method :attributes_sets, :attribute_sets
|
90
142
|
alias_method :properties_sets, :attribute_sets
|
@@ -108,9 +160,11 @@ module ActiveModel
|
|
108
160
|
alias_method :are_the_attributes, :filtered_attribute
|
109
161
|
|
110
162
|
# Gets all the defined attribute set names hashed by attribute names.
|
163
|
+
#
|
111
164
|
# @note Use +key+ method explicitly to check if the given attribute is assigned to any set. The hash
|
112
165
|
# returned by this method will always return {AttributeSet} object. If the attribute is not assigned
|
113
166
|
# to any set then the returned, matching set will be empty.
|
167
|
+
#
|
114
168
|
# @return [Hash{String => AttributeSet<Symbol>}] the collection of attribute set names indexed by attribute names
|
115
169
|
def attributes_to_sets
|
116
170
|
self.class.attributes_to_sets
|
@@ -121,13 +175,13 @@ module ActiveModel
|
|
121
175
|
# that create DSL for managing attribute sets.
|
122
176
|
module ClassMethods
|
123
177
|
# @overload attribute_set()
|
124
|
-
# Gets all the defined attribute sets
|
178
|
+
# Gets all the defined attribute sets by calling +attribute_sets+.
|
125
179
|
# @return [Hash{Symbol => AttributeSet<String>}] the collection of attribute sets indexed by their names
|
126
180
|
#
|
127
181
|
# @overload attribute_set(set_name)
|
128
|
-
# Gets the
|
182
|
+
# Gets the attribute set of the given name from internal storage.
|
129
183
|
# @param set_name [Symbol,String] name of a set
|
130
|
-
# @return [AttributeSet<String>] the
|
184
|
+
# @return [AttributeSet<String>] the attribute set
|
131
185
|
#
|
132
186
|
# @overload attribute_set(set_name, *attribute_names)
|
133
187
|
# Adds new attributes to a set of attributes.
|
@@ -148,13 +202,12 @@ module ActiveModel
|
|
148
202
|
# @param attribute_names [Array<Symbol,String>] names of additional attributes to be stored in set
|
149
203
|
# @return [nil]
|
150
204
|
def attribute_set(*args)
|
151
|
-
AttributeFiltersHelpers.check_wanted_methods(self)
|
152
205
|
case args.size
|
153
206
|
when 0
|
154
207
|
attribute_sets
|
155
208
|
when 1
|
156
209
|
first_arg = args.first
|
157
|
-
if first_arg.is_a?(Hash)
|
210
|
+
if first_arg.is_a?(Hash) # multiple sets defined
|
158
211
|
first_arg.each_pair { |k, v| attribute_set(k, v) }
|
159
212
|
nil
|
160
213
|
else
|
@@ -162,19 +215,13 @@ module ActiveModel
|
|
162
215
|
end
|
163
216
|
else
|
164
217
|
first_arg = args.shift
|
165
|
-
if first_arg.is_a?(Hash)
|
218
|
+
if first_arg.is_a?(Hash) # multiple sets defined
|
166
219
|
first_arg.each_pair do |k, v|
|
167
220
|
attribute_set(k, v, args)
|
168
221
|
end
|
169
222
|
else
|
170
|
-
|
171
|
-
|
172
|
-
atrs.each do |atr_name|
|
173
|
-
__attributes_to_sets_map[atr_name] ||= ActiveModel::AttributeSet.new
|
174
|
-
__attributes_to_sets_map[atr_name] << set_name
|
175
|
-
end
|
176
|
-
__attribute_sets[set_name] ||= ActiveModel::AttributeSet.new
|
177
|
-
__attribute_sets[set_name] << atrs
|
223
|
+
AttributeFiltersHelpers.check_wanted_methods(self)
|
224
|
+
add_atrs_to_set(first_arg.to_sym, *args)
|
178
225
|
end
|
179
226
|
nil
|
180
227
|
end
|
@@ -236,7 +283,13 @@ module ActiveModel
|
|
236
283
|
else
|
237
284
|
first_arg = first_arg.to_s
|
238
285
|
args.flatten.compact.each do |set_name|
|
239
|
-
|
286
|
+
if set_name.is_a?(Hash) # annotation
|
287
|
+
set_name.each_pair do |set_name_b, a_defs|
|
288
|
+
attribute_set(set_name_b, first_arg => a_defs)
|
289
|
+
end
|
290
|
+
else
|
291
|
+
attribute_set(set_name, first_arg)
|
292
|
+
end
|
240
293
|
end
|
241
294
|
end
|
242
295
|
nil
|
@@ -250,26 +303,34 @@ module ActiveModel
|
|
250
303
|
alias_method :filtered_attributes, :filter_attribute
|
251
304
|
|
252
305
|
# Gets all the defined attribute sets.
|
306
|
+
#
|
253
307
|
# @note Use +key+ method explicitly to check if the given set exists. The hash returned by this method
|
254
308
|
# will always return {AttributeSet} object. If there is no such set defined then the returned,
|
255
|
-
# matching set will be empty.
|
309
|
+
# matching set will be empty. All set objects are duplicates of the defined sets.
|
310
|
+
#
|
256
311
|
# @return [Hash{Symbol => AttributeSet<String>}] the collection of attribute sets indexed by their names
|
257
312
|
def attribute_sets
|
258
|
-
d =
|
259
|
-
|
313
|
+
d = Hash.new(ActiveModel::AttributeSet.new.freeze)
|
314
|
+
__attribute_sets.each_pair do |set_name, set_object|
|
315
|
+
d[set_name] = set_object.dup
|
316
|
+
end
|
260
317
|
d
|
261
318
|
end
|
262
319
|
alias_method :attributes_sets, :attribute_sets
|
263
320
|
alias_method :properties_sets, :attribute_sets
|
264
321
|
|
265
322
|
# Gets all the defined attribute set names hashed by attribute names.
|
323
|
+
#
|
266
324
|
# @note Use +key+ method explicitly to check if the given attribute is assigned to any set. The hash
|
267
325
|
# returned by this method will always return {AttributeSet} object. If the attribute is not assigned
|
268
326
|
# to any set then the returned, matching set will be empty.
|
327
|
+
#
|
269
328
|
# @return [Hash{String => AttributeSet<Symbol>}] the collection of attribute set names indexed by attribute names
|
270
329
|
def attributes_to_sets
|
271
|
-
d =
|
272
|
-
|
330
|
+
d = Hash.new(ActiveModel::AttributeSet.new.freeze)
|
331
|
+
__attributes_to_sets_map.each_pair do |set_name, set_object|
|
332
|
+
d[set_name] = set_object.dup
|
333
|
+
end
|
273
334
|
d
|
274
335
|
end
|
275
336
|
alias_method :attribute_sets_map, :attributes_to_sets
|
@@ -284,6 +345,35 @@ module ActiveModel
|
|
284
345
|
@__attribute_sets ||= Hash.new
|
285
346
|
end
|
286
347
|
|
348
|
+
def add_atrs_to_set(set_name, *atrs)
|
349
|
+
atrs = atrs.flatten.compact
|
350
|
+
atrs.each do |atr_name|
|
351
|
+
if atr_name.is_a?(Hash) # annotation
|
352
|
+
atr_name.each_pair do |atr_name_b, a_defs|
|
353
|
+
add_atrs_to_set(set_name, atr_name_b)
|
354
|
+
s = __attribute_sets[set_name] and a_defs.nil? or a_defs.each_pair { |n, v| s.annotate(atr_name_b, n, v) }
|
355
|
+
end
|
356
|
+
return
|
357
|
+
else
|
358
|
+
atr_name = atr_name.to_s
|
359
|
+
__attributes_to_sets_map[atr_name] ||= ActiveModel::AttributeSet.new
|
360
|
+
__attributes_to_sets_map[atr_name] << set_name
|
361
|
+
end
|
362
|
+
end
|
363
|
+
__attribute_sets[set_name] ||= ActiveModel::AttributeSet.new
|
364
|
+
__attribute_sets[set_name] << atrs.map{ |a| a.to_s }.freeze
|
365
|
+
end
|
287
366
|
end # module ClassMethods
|
367
|
+
|
368
|
+
private
|
369
|
+
|
370
|
+
# Helper that collects untracked virtual attributes that
|
371
|
+
# have setters and getters.
|
372
|
+
def __vatrf(no_presence_check = false)
|
373
|
+
tar = self.class.send(:__treat_as_real)
|
374
|
+
return tar if no_presence_check || tar.empty?
|
375
|
+
tar.select { |a| respond_to?(a) && respond_to?("#{a}=") }
|
376
|
+
end
|
377
|
+
|
288
378
|
end # module AttributeMethods
|
289
379
|
end # module ActiveModel
|
@@ -35,6 +35,20 @@ module ActiveModel
|
|
35
35
|
end
|
36
36
|
module_function :check_wanted_methods
|
37
37
|
|
38
|
+
# @private
|
39
|
+
def each_element(value, must_be = nil, &block)
|
40
|
+
if value.is_a?(Array)
|
41
|
+
value.map { |v| each_element(v, must_be, &block) }
|
42
|
+
elsif value.is_a?(Hash)
|
43
|
+
value.merge(value) { |k, ov| each_element(ov, must_be, &block) }
|
44
|
+
elsif must_be.nil? || value.is_a?(must_be)
|
45
|
+
yield(value)
|
46
|
+
else
|
47
|
+
value
|
48
|
+
end
|
49
|
+
end
|
50
|
+
module_function :each_element
|
51
|
+
|
38
52
|
end # module AttributeFiltersHelpers
|
39
53
|
end # module AttributeFilters
|
40
54
|
end # module ActiveModel
|
@@ -6,9 +6,16 @@ describe ActiveModel::AttributeFilters do
|
|
6
6
|
|
7
7
|
describe TestModel do
|
8
8
|
before do
|
9
|
+
TestModel.attributes_that(:should_be_stripped, :email, :real_name)
|
10
|
+
TestModel.attributes_that(:should_be_titleized, :real_name)
|
9
11
|
@tm = TestModel.new
|
10
12
|
end
|
11
13
|
|
14
|
+
it "should return list of sets attribute belongs to" do
|
15
|
+
@tm.the_attribute(:email).should include :should_be_stripped
|
16
|
+
@tm.the_attribute('email').should include :should_be_stripped
|
17
|
+
end
|
18
|
+
|
12
19
|
it "should be able to filter model attributes properly" do
|
13
20
|
@tm.username = " UPCASEĄĘŚĆ "
|
14
21
|
@tm.email = " Some@EXAMPLE.com "
|
@@ -19,6 +26,16 @@ describe ActiveModel::AttributeFilters do
|
|
19
26
|
@tm.real_name.should == "Sir Rails"
|
20
27
|
end
|
21
28
|
|
29
|
+
it "should filter model attributes that are arrays" do
|
30
|
+
@tm.username = [" UPCASEĄĘŚĆ "]
|
31
|
+
@tm.email = [" Some@EXAMPLE.com ", " x "]
|
32
|
+
@tm.real_name = [" sir rails ", nil]
|
33
|
+
@tm.save
|
34
|
+
@tm.username.should == ["upcaseąęść"]
|
35
|
+
@tm.email.should == ["Some@EXAMPLE.com", "x"]
|
36
|
+
@tm.real_name.should == ["Sir Rails", nil]
|
37
|
+
end
|
38
|
+
|
22
39
|
it "should not filter model attributes that are blank" do
|
23
40
|
@tm.username = nil
|
24
41
|
@tm.save
|
@@ -45,19 +62,323 @@ describe ActiveModel::AttributeFilters do
|
|
45
62
|
@tm.test_attribute.should == 'unchanged'
|
46
63
|
end
|
47
64
|
|
65
|
+
it "should operate on annotations" do
|
66
|
+
s = @tm.attributes_that(:should_be_stripped)
|
67
|
+
c = @tm.attributes_that(:should_be_titleized)
|
68
|
+
s.annotate(:real_name, :operation, :first_value)
|
69
|
+
s.annotate(:email, :operation, :e_value)
|
70
|
+
c.annotate(:real_name, :operation, :some_value)
|
71
|
+
c.annotate(:real_name, :operation, :other_value)
|
72
|
+
s.instance_eval{annotations}.should == { 'real_name' => { :operation => :first_value }, 'email' => { :operation => :e_value } }
|
73
|
+
c.instance_eval{annotations}.should == { 'real_name' => { :operation => :other_value } }
|
74
|
+
-> {TestModel.class_eval do
|
75
|
+
attributes_that should_be_sth: [ :abc, :atr_one => {:ak => "av"}, :atr_two => {:sk => "sv"} ]
|
76
|
+
attributes_that should_be_sth: [:atr_three, :atr_two, :yy]
|
77
|
+
attributes_that should_be_sth: {:atr_three => {:fak => "fav"}, :atr_two => {:flala => "flala2"}, :fyy => nil}
|
78
|
+
annotate_attribute_set should_be_sth: {:atr_three => {:ak => "av"}, :atr_two => {:lala => "lala2"}, :yy => nil}
|
79
|
+
annotate_attribute_set should_be_sth: [:atr_three, :oo => "oe"]
|
80
|
+
annotate_attribute_set should_be_sth: [:atr_three, :aa, "bb"]
|
81
|
+
annotate_attribute_set should_be_sth: [:atr_three, :hh, "hh"]
|
82
|
+
annotate_attributes_that :should_be_sth, :atr_three, :six => 6
|
83
|
+
annotate_attribute_set :should_be_sth => [:atr_three, :cc, "dd"]
|
84
|
+
annotate_attribute_set :should_be_sth => [:atr_three, :ccc, "ddd"]
|
85
|
+
delete_annotation_from_set :should_be_sth, :atr_three, :ccc
|
86
|
+
delete_annotation_from_set :should_be_sth => { :atr_three => [ :hh ] }
|
87
|
+
end}.should_not raise_error
|
88
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_one).should == {:ak => "av"}
|
89
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_two, :lala).should == "lala2"
|
90
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_x, :lalax).should == nil
|
91
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :oo).should == "oe"
|
92
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :aa).should == "bb"
|
93
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :cc).should == "dd"
|
94
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :ccc).should == nil
|
95
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :six).should == 6
|
96
|
+
@tm.attributes_that(:should_be_sth).has_annotations?.should == true
|
97
|
+
@tm.attributes_that(:should_be_sth).has_annotation?(:atr_three).should == true
|
98
|
+
@tm.attributes_that(:should_be_sth).has_annotation?(:atr_three, :ak).should == true
|
99
|
+
@tm.attributes_that(:should_be_sth).has_annotation?(:atr_nope).should == false
|
100
|
+
@tm.attributes_that(:should_be_sth).has_annotation?(:atr_three, :nope).should == false
|
101
|
+
@tm.attributes_that(:should_be_sth).delete_annotation(:atr_three, :cc)
|
102
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :cc).should == "dd"
|
103
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :hh).should == nil
|
104
|
+
dupx = TestModel.attributes_that(:should_be_sth)
|
105
|
+
dupy = @tm.attributes_that(:should_be_sth)
|
106
|
+
dupx.send(:annotations).should == dupy.send(:annotations)
|
107
|
+
dupx.object_id.should_not == dupy.object_id
|
108
|
+
-> {TestModel.class_eval do
|
109
|
+
annotate_attributes_that :should_be_sth => { :atr_three => { :cc => "ee" } }
|
110
|
+
annotate_attribute_set should_be_sth: [:atr_three, :oo => "of"]
|
111
|
+
attributes_that should_be_sth: { :atr_one => {:ak => "ax"} }
|
112
|
+
end}.should_not raise_error
|
113
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :cc).should == "ee"
|
114
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_three, :oo).should == "of"
|
115
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_one).should == {:ak => "ax"}
|
116
|
+
@tm.attributes_that(:should_be_sth).annotation(:atr_two, :lala).should == "lala2"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "AttributeSet set operations" do
|
121
|
+
before do
|
122
|
+
TestModel.attributes_that(:should_be_stripped, :email, :real_name)
|
123
|
+
TestModel.attributes_that(:should_be_titleized, :real_name)
|
124
|
+
@tm = TestModel.new
|
125
|
+
@s = @tm.attributes_that(:should_be_stripped)
|
126
|
+
@c = @tm.attributes_that(:should_be_titleized)
|
127
|
+
@s.annotate(:real_name, :operation, :first_value)
|
128
|
+
@s.annotate(:email, :operation, :e_value)
|
129
|
+
@c.annotate(:real_name, :operation, :some_value)
|
130
|
+
@c.annotate(:real_name, :operation, :other_value)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should be able to relatively complement sets" do
|
134
|
+
r = @s - @c
|
135
|
+
r.to_a.sort.should == [ "email", "username" ]
|
136
|
+
r.instance_eval{annotations}.should == { 'email' => { :operation => :e_value } }
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should be able to join sets (union)" do
|
140
|
+
r = @s + @c
|
141
|
+
r.to_a.sort.should == [ "email", "real_name", "username" ]
|
142
|
+
r.instance_eval{annotations}.should == { 'email' => { :operation => :e_value }, 'real_name' => { :operation => :first_value } }
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should be able to intersect sets" do
|
146
|
+
r = @s & @c
|
147
|
+
r.to_a.sort.should == [ "real_name" ]
|
148
|
+
r.instance_eval{annotations}.should == { 'real_name' => { :operation => :first_value } }
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should be able to exclusively disjunct sets" do
|
152
|
+
r = @s ^ @c
|
153
|
+
r.to_a.sort.should == [ "email", "username" ]
|
154
|
+
r.instance_eval{annotations}.should == { 'email' => { :operation => :e_value } }
|
155
|
+
sp = @s.dup
|
156
|
+
sp.annotate(:username, 'k', 'v')
|
157
|
+
r = sp ^ @c
|
158
|
+
r.to_a.sort.should == [ "email", "username" ]
|
159
|
+
r.instance_eval{annotations}.should == { 'email' => { :operation => :e_value }, 'username' => { :k => "v" } }
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should be able to delete elements from a set" do
|
163
|
+
@s.annotate(:username, :some_key, 'string_val')
|
164
|
+
@s.instance_eval{annotations}.should == { 'email' => { :operation => :e_value }, 'real_name' => { :operation => :first_value },
|
165
|
+
'username' => { :some_key => 'string_val' } }
|
166
|
+
@s.delete_if { |o| o == 'username' }
|
167
|
+
@s.include?('username').should == false
|
168
|
+
@s.instance_eval{annotations}.should == { 'email' => { :operation => :e_value }, 'real_name' => { :operation => :first_value } }
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should be able to keep elements in a set using keep_if" do
|
172
|
+
@s.keep_if { |o| o == 'email' }
|
173
|
+
@s.include?('email').should == true
|
174
|
+
@s.instance_eval{annotations}.should == { 'email' => { :operation => :e_value } }
|
175
|
+
end
|
48
176
|
end
|
49
177
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
178
|
+
describe ActiveModel::AttributeFilters::Common do
|
179
|
+
|
180
|
+
before do
|
181
|
+
TestModel.class_eval do
|
182
|
+
include ActiveModel::AttributeFilters::Common
|
183
|
+
@__attribute_sets = nil
|
184
|
+
end
|
185
|
+
@tm = TestModel.new
|
186
|
+
end
|
187
|
+
|
188
|
+
after do
|
189
|
+
TestModel.class_eval{@__attribute_sets = nil}
|
190
|
+
@tm.attributes_that(:should_be_splitted).should be_empty
|
191
|
+
@tm.attributes_that(:should_be_joined).should be_empty
|
192
|
+
@tm.attributes_that(:should_be_splitted).annotation(:real_name).should == nil
|
193
|
+
@tm.attributes_that(:should_be_joined).annotation(:real_name).should == nil
|
194
|
+
end
|
195
|
+
|
196
|
+
shared_examples "splitting" do |ev|
|
197
|
+
before { TestModel.class_eval{before_save :split_attributes} }
|
198
|
+
it "should split attributes using syntax: #{ev}" do
|
199
|
+
TestModel.class_eval(ev)
|
200
|
+
@tm.real_name = "Paweł Wilk Trzy"
|
201
|
+
@tm.first_name = nil
|
202
|
+
@tm.last_name = nil
|
203
|
+
#-> { @tm.save }.should_not raise_error
|
204
|
+
@tm.save
|
205
|
+
@tm.first_name.should == 'Paweł'
|
206
|
+
@tm.last_name.should == 'Wilk'
|
207
|
+
@tm.first_name = nil
|
208
|
+
@tm.last_name = nil
|
209
|
+
@tm.real_name = "Paweł"
|
210
|
+
-> { @tm.save }.should_not raise_error
|
211
|
+
@tm.first_name.should == 'Paweł'
|
212
|
+
@tm.last_name.should == nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
shared_examples "splitting_array" do |de, ev, rn|
|
217
|
+
before do
|
218
|
+
TestModel.class_eval{before_save :split_attributes}
|
219
|
+
end
|
220
|
+
it "should split array attribute #{de}" do
|
221
|
+
TestModel.class_eval(ev)
|
222
|
+
@tm.real_name = rn
|
223
|
+
@tm.first_name = nil
|
224
|
+
@tm.last_name = nil
|
225
|
+
-> { @tm.save }.should_not raise_error
|
226
|
+
@tm.first_name.should == 'Paweł'
|
227
|
+
@tm.last_name.should == 'Wilk'
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "with split_attribute" do
|
232
|
+
include_examples "splitting", "split_attribute :real_name => [ :first_name, :last_name ]"
|
233
|
+
include_examples "splitting", "split_attribute :real_name, [ :first_name, :last_name ]"
|
234
|
+
include_examples "splitting", "split_attribute :real_name => { :into => [ :first_name, :last_name ] }"
|
235
|
+
include_examples "splitting", "split_attribute :real_name, :into => [ :first_name, :last_name ]"
|
236
|
+
end
|
237
|
+
|
238
|
+
context "with attributes_that" do
|
239
|
+
include_examples "splitting", "attributes_that :should_be_splitted => { :real_name => { :split_into => [:first_name, :last_name] } }"
|
240
|
+
include_examples "splitting", "attributes_that :should_be_splitted => [ :real_name => { :split_into => [:first_name, :last_name] } ]"
|
241
|
+
end
|
242
|
+
|
243
|
+
context "with the_attribute" do
|
244
|
+
include_examples "splitting", "the_attribute :real_name => { :should_be_splitted => { :split_into => [:first_name, :last_name] } }"
|
245
|
+
include_examples "splitting", "the_attribute :real_name => [ :should_be_splitted => { :split_into => [:first_name, :last_name] } ]"
|
246
|
+
include_examples "splitting", "the_attribute :real_name, [ :should_be_splitted => { :split_into => [:first_name, :last_name] } ]"
|
247
|
+
end
|
62
248
|
|
63
|
-
|
249
|
+
context "with no pattern and no limit" do
|
250
|
+
include_examples "splitting_array", "", "split_attribute :real_name => { :into => [ :first_name, :last_name ], :flatten => true }",
|
251
|
+
["Paweł", "Wilk", "Trzy"]
|
252
|
+
end
|
253
|
+
|
254
|
+
context "with a single space pattern and without a limit" do
|
255
|
+
include_examples "splitting_array", "having 3 elements",
|
256
|
+
"split_attribute :real_name => {:pattern => ' ', :into => [ :first_name, :last_name ], :flatten => true}",
|
257
|
+
["Paweł", "Wilk", "Trzy"]
|
258
|
+
include_examples "splitting_array", "having 2 elements and first containing pattern (space)",
|
259
|
+
"split_attribute :real_name => {:pattern => ' ', :into => [ :first_name, :last_name ], :flatten => true}",
|
260
|
+
["Paweł Wilk", "Trzy"]
|
261
|
+
end
|
262
|
+
|
263
|
+
context "with a single space pattern and with a limit" do
|
264
|
+
include_examples "splitting_array", "having 3 elements",
|
265
|
+
"split_attribute :real_name => {:pattern => ' ', :limit => 2, :into => [ :first_name, :last_name ], :flatten => true}",
|
266
|
+
["Paweł", "Wilk", "Trzy"]
|
267
|
+
include_examples "splitting_array", "having 2 elements and first containing pattern (space)",
|
268
|
+
"split_attribute :real_name => {:pattern => ' ', :limit => 2, :into => [ :first_name, :last_name ], :flatten => true}",
|
269
|
+
["Paweł Wilk", "Trzy"]
|
270
|
+
include_examples "splitting_array", "having 2 elements and first containing pattern (space)",
|
271
|
+
"split_attribute :real_name => {:pattern => ' ', :limit => 10, :into => [ :first_name, :last_name ], :flatten => true}",
|
272
|
+
["Paweł", "Wilk"]
|
273
|
+
include_examples "splitting_array", "having 1 element and first containing pattern (space)",
|
274
|
+
"split_attribute :real_name => {:pattern => ' ', :limit => 2, :into => [ :first_name, :last_name ], :flatten => true}",
|
275
|
+
["Paweł Wilk"]
|
276
|
+
it "should split array attribute having 2 elements and second containing pattern (space)" do
|
277
|
+
TestModel.class_eval{split_attribute :real_name => {:pattern => ' ', :limit => 2, :into => [ :first_name, :last_name ], :flatten => true}}
|
278
|
+
@tm.real_name = ["Paweł", "Wilk Trzy", "Cztery"]
|
279
|
+
@tm.first_name = nil
|
280
|
+
@tm.last_name = nil
|
281
|
+
-> { @tm.save }.should_not raise_error
|
282
|
+
@tm.first_name.should == 'Paweł'
|
283
|
+
@tm.last_name.should == 'Wilk'
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "without a pattern and with a limit" do
|
288
|
+
include_examples "splitting_array", "having 3 elements",
|
289
|
+
"split_attribute :real_name => {:limit => 2, :into => [ :first_name, :last_name ], :flatten => true}",
|
290
|
+
["Paweł", "Wilk", "Trzy"]
|
291
|
+
include_examples "splitting_array", "having 2 elements",
|
292
|
+
"split_attribute :real_name => {:limit => 2, :into => [ :first_name, :last_name ], :flatten => true}",
|
293
|
+
["Paweł", "Wilk"]
|
294
|
+
end
|
295
|
+
|
296
|
+
it "should split array attribute with the destination in the same place" do
|
297
|
+
TestModel.class_eval{split_attribute :real_name => { :flatten => true } }
|
298
|
+
TestModel.class_eval{before_save :split_attributes}
|
299
|
+
@tm.real_name = ["Paweł", "Wilk Trzy", "Cztery"]
|
300
|
+
@tm.first_name = nil
|
301
|
+
@tm.last_name = nil
|
302
|
+
-> { @tm.save }.should_not raise_error
|
303
|
+
@tm.first_name.should == nil
|
304
|
+
@tm.last_name.should == nil
|
305
|
+
@tm.real_name.should == ["Paweł", "Wilk", "Trzy", "Cztery"]
|
306
|
+
|
307
|
+
TestModel.class_eval{split_attribute :real_name => {:limit => 2}}
|
308
|
+
@tm.real_name = ["Paweł", "Wilk Trzy Osiem Dziewiec", "Cztery"]
|
309
|
+
-> { @tm.save }.should_not raise_error
|
310
|
+
@tm.real_name.should == [["Paweł"], ["Wilk", "Trzy Osiem Dziewiec"], ["Cztery"]]
|
311
|
+
|
312
|
+
TestModel.class_eval{split_attribute :real_name => {:limit => 2, :pattern => ' '}}
|
313
|
+
@tm.real_name = ["Paweł", "Wilk Trzy", "Cztery"]
|
314
|
+
-> { @tm.save }.should_not raise_error
|
315
|
+
@tm.real_name.should == [["Paweł"], ["Wilk", "Trzy"], ["Cztery"]]
|
316
|
+
|
317
|
+
TestModel.class_eval{split_attribute :real_name => {:pattern => ' '}}
|
318
|
+
@tm.real_name = ["Paweł", "Wilk Trzy", "Cztery"]
|
319
|
+
-> { @tm.save }.should_not raise_error
|
320
|
+
@tm.real_name.should == [["Paweł"], ["Wilk", "Trzy"], ["Cztery"]]
|
321
|
+
end
|
322
|
+
|
323
|
+
shared_examples "joining" do |ev,rn,rns,rnt|
|
324
|
+
before do
|
325
|
+
TestModel.class_eval do
|
326
|
+
before_save :join_attributes
|
327
|
+
end
|
328
|
+
end
|
329
|
+
it "should join attributes using syntax: #{ev}" do
|
330
|
+
TestModel.class_eval(ev)
|
331
|
+
# source attributes are strings:
|
332
|
+
@tm.real_name = rn
|
333
|
+
@tm.first_name = "Paweł"
|
334
|
+
@tm.last_name = "Wilk"
|
335
|
+
-> { @tm.save }.should_not raise_error
|
336
|
+
@tm.first_name.should == 'Paweł'
|
337
|
+
@tm.last_name.should == 'Wilk'
|
338
|
+
@tm.real_name.should == 'Paweł Wilk'
|
339
|
+
# source attributes are strings and nils:
|
340
|
+
@tm.first_name = "Paweł"
|
341
|
+
@tm.last_name = nil
|
342
|
+
@tm.real_name = rns
|
343
|
+
@tm.class.annotate_attributes_that(:should_be_joined, :real_name, :join_compact, true)
|
344
|
+
-> { @tm.save }.should_not raise_error
|
345
|
+
@tm.first_name.should == 'Paweł'
|
346
|
+
@tm.last_name.should == nil
|
347
|
+
@tm.real_name.should == 'Paweł'
|
348
|
+
# source attributes are arrays and strings:
|
349
|
+
@tm.first_name = ["Paweł", "Wilk"]
|
350
|
+
@tm.last_name = "Trzeci"
|
351
|
+
@tm.real_name = rnt
|
352
|
+
-> { @tm.save }.should_not raise_error
|
353
|
+
@tm.real_name.should == 'Paweł Wilk Trzeci'
|
354
|
+
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
context "with join_attributes" do
|
359
|
+
include_examples "joining", "join_attributes :real_name", ["Paweł", "Wilk"], ["Paweł"], ["Paweł", "Wilk", "Trzeci"]
|
360
|
+
include_examples "joining", "join_attributes :real_name", ["Paweł Wilk"], ["Paweł"], ["Paweł Wilk", "Trzeci"]
|
361
|
+
include_examples "joining", "join_attributes :real_name", "Paweł Wilk", "Paweł", "Paweł Wilk Trzeci"
|
362
|
+
include_examples "joining", "join_attributes :real_name => [ :first_name, :last_name ]"
|
363
|
+
include_examples "joining", "join_attributes :real_name, [ :first_name, :last_name ]"
|
364
|
+
include_examples "joining", "join_attributes :real_name => { :from => [ :first_name, :last_name ] }"
|
365
|
+
include_examples "joining", "join_attributes :real_name, :from => [ :first_name, :last_name ]"
|
366
|
+
include_examples "joining", "join_attributes [ :first_name, :last_name ] => :real_name"
|
367
|
+
include_examples "joining", "join_attributes [ :first_name, :last_name ], :real_name"
|
368
|
+
include_examples "joining", "join_attributes [ :first_name, :last_name ] => { :into => :real_name }"
|
369
|
+
end
|
370
|
+
|
371
|
+
context "with attributes_that" do
|
372
|
+
include_examples "joining", "attributes_that :should_be_joined => { :real_name => { :join_from => [:first_name, :last_name] } }"
|
373
|
+
include_examples "joining", "attributes_that :should_be_joined => [ :real_name => { :join_from => [:first_name, :last_name] } ]"
|
374
|
+
end
|
375
|
+
|
376
|
+
context "with the_attribute" do
|
377
|
+
include_examples "joining", "the_attribute :real_name => { :should_be_joined => { :join_from => [:first_name, :last_name] } }"
|
378
|
+
include_examples "joining", "the_attribute :real_name => [ :should_be_joined => { :join_from => [:first_name, :last_name] } ]"
|
379
|
+
include_examples "joining", "the_attribute :real_name, [ :should_be_joined => { :join_from => [:first_name, :last_name] } ]"
|
380
|
+
end
|
381
|
+
|
382
|
+
end # describe ActiveModel::AttributeFilters::Common
|
383
|
+
|
384
|
+
end # describe ActiveModel::AttributeFilters
|