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.
@@ -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