attribute-filters 1.3.2 → 1.4.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.
- 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
|