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.
@@ -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. Altering attribute sets
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(self.class.attribute_set(set_name), self)
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 a set containing all attributes.
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(AttributeSet.new(attributes.keys), self)
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
- all_attributes & self.class.accessible_attributes
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
- all_attributes & self.class.protected_attributes
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 - self.class.accessible_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 contents of an attribute set of the given name.
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 collection of attribute sets
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
- set_name = first_arg.to_sym
171
- atrs = args.flatten.compact.map{|a|a.to_s}.freeze
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
- attribute_set(set_name, first_arg)
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 = __attribute_sets.dup
259
- d.default = ActiveModel::AttributeSet.new
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 = __attributes_to_sets_map.dup
272
- d.default = ActiveModel::AttributeSet.new
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
@@ -14,7 +14,7 @@ module ActiveModel
14
14
  # @private
15
15
  EMAIL = 'pw@gnu.org'
16
16
  # @private
17
- VERSION = '1.3.2'
17
+ VERSION = '1.4.0'
18
18
  # @private
19
19
  NAME = 'attribute-filters'
20
20
  # @private
@@ -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
- # do the above with ActiveRecord -- look in heisepath for testing examples
51
- #it "is able to filter model attributes with Active Record as ORM" do
52
- # @tm = TestModelAR.new
53
- # @tm.username = " UPCASEĄĘŚĆ "
54
- # @tm.email = " Some@EXAMPLE.com "
55
- # @tm.real_name = " sir rails "
56
- # -> { @tm.save }.should_not raise_error
57
- # @tm.username.should == "upcaseąęść"
58
- # @tm.email.should == "Some@EXAMPLE.com"
59
- # @tm.real_name.should == "Sir Rails"
60
- #end
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
- end
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