attribute-filters 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.yardopts +1 -0
  3. data/ChangeLog +501 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +7 -7
  6. data/Manifest.txt +7 -0
  7. data/README.md +59 -30
  8. data/Rakefile +1 -0
  9. data/attribute-filters.gemspec +4 -4
  10. data/docs/COMMON-FILTERS.md +1067 -0
  11. data/docs/HISTORY +42 -0
  12. data/docs/TODO +29 -12
  13. data/docs/USAGE.md +718 -818
  14. data/lib/attribute-filters.rb +4 -2
  15. data/lib/attribute-filters/attribute_set.rb +144 -73
  16. data/lib/attribute-filters/attribute_set_annotations.rb +110 -77
  17. data/lib/attribute-filters/attribute_set_attrquery.rb +51 -8
  18. data/lib/attribute-filters/attribute_set_enum.rb +44 -38
  19. data/lib/attribute-filters/attribute_set_query.rb +62 -12
  20. data/lib/attribute-filters/backports.rb +36 -4
  21. data/lib/attribute-filters/common_filters.rb +83 -37
  22. data/lib/attribute-filters/common_filters/bare.rb +29 -0
  23. data/lib/attribute-filters/common_filters/case.rb +34 -19
  24. data/lib/attribute-filters/common_filters/convert.rb +259 -0
  25. data/lib/attribute-filters/common_filters/join.rb +37 -30
  26. data/lib/attribute-filters/common_filters/order.rb +105 -0
  27. data/lib/attribute-filters/common_filters/pick.rb +90 -0
  28. data/lib/attribute-filters/common_filters/presence.rb +70 -0
  29. data/lib/attribute-filters/common_filters/split.rb +37 -21
  30. data/lib/attribute-filters/common_filters/squeeze.rb +28 -9
  31. data/lib/attribute-filters/common_filters/strip.rb +7 -3
  32. data/lib/attribute-filters/dsl_attr_virtual.rb +2 -1
  33. data/lib/attribute-filters/dsl_filters.rb +14 -61
  34. data/lib/attribute-filters/dsl_sets.rb +238 -88
  35. data/lib/attribute-filters/helpers.rb +7 -1
  36. data/lib/attribute-filters/meta_set.rb +38 -0
  37. data/lib/attribute-filters/version.rb +1 -1
  38. data/spec/attribute-filters_spec.rb +178 -16
  39. data/spec/spec_helper.rb +9 -4
  40. metadata +129 -69
  41. metadata.gz.sig +0 -0
data/docs/HISTORY CHANGED
@@ -1,3 +1,45 @@
1
+ === 2.0.0 / 2012-10-01
2
+
3
+ * major enhancements
4
+
5
+ * Operating speed increased by 100%
6
+ * API change:
7
+ * AttributeSet is now based on a Hash
8
+ * Added MetaSet for keeping sets of attribute sets
9
+ * Added DSL keyword for registering filtering methods
10
+ * DSL method `filter_attributes` is using filtering methods registered before
11
+ * Creation of duplicates for sets moved to class-level accessors
12
+ * Adding elements to sets improved, `AttributeSet` initializer uses the method `add`
13
+ * Common Filters seriously refactored (easily expandable and parametrizable now)
14
+ * Added common filters: Convert, Order, Pick, Presence
15
+
16
+ * minor enhancements
17
+
18
+ * DSL class methods for models have now aliases named in third person
19
+ * `AttributeSet#annotations` returns a hash containing annotated attributes only
20
+ * Documentation updated, common filters specific documentation moved to `COMMON-FILTERS`
21
+ * Added `AttributeSet::Enumerable#each_name_value` enumerator
22
+ * Added `AttributeSet::Enumerable#select_accessible` enumerator
23
+ * Added DSL for attribute name gethering from next method name in a chain
24
+ * Added `filtered_attribute_simple` DSL method
25
+ * Added `attribute_set_exists?` DSL class method
26
+ * Added `virtual?` and `semi_real?` to `AttributeSet::AttrQuery` and `AttributeSet::Query`
27
+ * Added `changed?` and `unchanged?` to `AttributeSet::AttrQuery` and `AttributeSet::Query`
28
+ * Added `AttributeSet#to_set`
29
+ * Improved DSL methods: `valid?`, `invalid?`, `accessible?`, `protected?`
30
+ * Improved `AttributeSet::Query` initializer (added error checking)
31
+ * Improved `attributes_to_filter` DLS method (removed redundant calls to duplicates of a set)
32
+ * Overriden `inspect` method in `AttributeSet`
33
+
34
+ * major bugfixes
35
+
36
+ * Fixed `AttributeSet::Query` wrapper calls (passing custom calls to next method was broken)
37
+ * Replaced `hash.merge(hash)` expressions with more `HashWithIndifferentAccess` compliant
38
+
39
+ * minor bugfixes
40
+
41
+ * Enumerators wrapped to return `AttributeSet::Enumerator` objects
42
+
1
43
  === 1.4.0 / 2012-08-24
2
44
 
3
45
  * major enhancements
data/docs/TODO CHANGED
@@ -1,20 +1,37 @@
1
- * add more rspec examples (+ nil and boolean attributes)
2
1
 
3
- * add parameter support to squeeze common filter
2
+ * attr_virtual - wrap method_defined? and add some gotchas into it that will wait for accessor to be added
3
+ there already is a hash for attr_virtual, all we need to do is to check any added accessor against that hash
4
+ that will only apply when there weren't accessors while using attr_virtual
4
5
 
5
- * add: changed? all.changed? any.changed? one.changed? none.changed? to attrquery and query proxies
6
- - they can use methods that internally are checking it
7
6
 
8
- * add stringify to common filters
9
- * add numerize to common filters
10
- * add booleanize to common filters
11
- * add nullify to common filters
12
- * add clear to common filters
13
- * add reverse to common filters
14
- * add sanitize to common filters (that calls needed)
15
- -- sanitize_email
7
+ * add sanitize to common filters
16
8
  -- sanitize_url
17
9
  -- sanitize_string
18
10
  -- sanitize_name
19
11
  -- sanitize_country
20
12
  -- sanitize_iban (future, in a gem attribute-filters-common-iban)
13
+
14
+ sanitize_the_attribute :name
15
+ sanitize_the_attribute :name => { :as => :name }
16
+ sanitize :name
17
+ the_attribute :name, :should_be_sanitized
18
+ the_attribute :name, :should_be_sanitized => { :as => :name }
19
+
20
+ before_save :sanitize_attributes
21
+
22
+ * one module for sanitizing - AttributeFilters::Sanitize
23
+ * possible submodules that enable certain/additional sanitizing methods
24
+
25
+
26
+ class User
27
+ sanitize_attributes :name, :email, :home => { :as => :address }
28
+ end
29
+
30
+ -> uses :should_be_sanitized same as filters do.
31
+ -> sanitization often uses each_element helper
32
+ -> sanitization methods taken from: Sanitize.instance_methods(false) [and aliases]
33
+ -> uses one main method for sanitization called sanitize_attributes
34
+ - for each attribute from a set the method checks the :as annotation (sanitize_attributes should add it)
35
+ - if there is no annotation it tries to guess it by the name
36
+ (uses global dictionary against name and its parts from the last one: e.g real_name takes 'name' first)
37
+
data/docs/USAGE.md CHANGED
@@ -4,58 +4,62 @@ Usage of Attribute Filters
4
4
  Attribute sets
5
5
  --------------
6
6
 
7
- Attribute set is a set of attribute names. It's like an array
8
- or, to be exact, like a set (a hash that can only have true values
9
- assigned to elements in order to just know whether the key exists or not).
7
+ Attribute set is a set of attribute names and optional annotations.
8
+ It's like a hash and internally it is a kind of Hash, but differs in
9
+ many places.
10
10
 
11
11
  Attribute sets have simple function; **they group attribute names**. What can
12
12
  you do with that? For example you can use it to perform some tasks
13
- on all attributes that are listed in a set.
13
+ on all attribute names that are stored in a set (or their values). You can also
14
+ combine sets, intersect, exclusively disjuct them, merge with other data, query them,
15
+ iterate through them, and so on.
14
16
 
15
17
  ### Data structures ###
16
18
 
17
19
  Attribute sets are instances of
18
20
  [`ActiveModel::AttributeSet`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeSet)
19
- class. You can create and update sets freely and store them wherever you want,
20
- but when it comes to models then (at the class-level) you can (and you should) rely
21
- on a storage that is already prepared to handle sets.
21
+ class. You can create and update sets freely and store in them wherever you want.
22
22
 
23
- The attribute sets assigned to models are stored as [class instance variable](http://blog.codegram.com/2011/4/understanding-class-instance-variables-in-ruby).
24
- **You cannot and should not interact with that storage directly**
25
- but by using dedicated class methods that are available in your models.
23
+ For your convenience Attribute Filters also provides globally created attribute sets that are bound to your models
24
+ (at the class-level). The binding is realized simply by using the hash of all these sets. The attribute sets
25
+ assigned to models are stored as
26
+ [class instance variable](http://blog.codegram.com/2011/4/understanding-class-instance-variables-in-ruby).
27
+
28
+ **You should interact with the global sets using dedicated DSL keywords** that are available in your models.
26
29
  These methods will allow you to read or write some data from/to global attribute sets.
27
30
 
28
- Attribute Filters are using `AttributeSet` instances
29
- not just to store internal data but also to interact
30
- with other parts of a program. So whenever there is
31
- a method that returns a set of attributes or even
32
- a set of set names, the returned value will probably
33
- be an instance of the class `AttributeSet`.
31
+ Attribute Filters are using `AttributeSet` instances not just to store internal data
32
+ but also to interact with other parts of a program. So whenever there is a method
33
+ that returns a set of attributes or even a set of set names, the returned value
34
+ will probably be an instance of the `AttributeSet` class.
34
35
 
35
36
  Note that when sets are returned the convention is that:
36
37
 
37
38
  * **attribute names are strings**
38
39
  * **set names are symbols**
40
+ * **annotations are hashes** attached to attribute names (strings)
41
+ * **annotation keys are symbols**
39
42
 
40
- Also note that once you create a set that is bound to your model you cannot
41
- remove elements from it and any query returning its contents will give you
42
- a copy. That's because **model-bound attribute sets should be considered
43
- a part of the interface**.
43
+ Once you create some set that is bound to your model (there is a class method `attribute_set` for it)
44
+ you cannot remove elements from it. Moreover, any query returning its contents will always
45
+ give you the deep copy. Such a separation is a consequence of the idea that **the model-bound attribute
46
+ sets should be a part of the interface**, which shouldn't change at runtime. This approach
47
+ enforces the declarative design (which is very concise and clear as you read the code).
44
48
 
45
- ### Defining the attribute sets ###
49
+ ### Defining the model-level attribute sets ###
46
50
 
47
51
  First thing that should be done when using the Attribute Filters
48
52
  is defining the sets of attributes in models.
49
53
 
50
54
  You can also create local sets (using `ActiveModel::AttributeSet.new`)
51
- or a local sets with extra syntactic sugar (using `ActiveModel::AttributeSet.new`)
52
- but in real-life scenarios you should first create some model-bound sets that
53
- can be later used by filters and by your own methods.
55
+ or a local sets with extra syntactic sugar (using `ActiveModel::AttributeSet::Query.new`)
56
+ but in real-life scenarios you will use model-bound sets that can be later used
57
+ by common filters and by your own methods.
54
58
 
55
59
  #### `attribute_set(set_name => attr_names)` ####
56
60
 
57
61
  The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_set)
58
- (a.k.a `attributes_that`) class method allows to **create or update a set** that will be
62
+ (a.k.a `has_attributes_that`) class method allows to **create or update a set** that will be
59
63
  tied to a model.
60
64
 
61
65
  Example:
@@ -63,18 +67,18 @@ Example:
63
67
  ```ruby
64
68
  class User < ActiveRecord::Base
65
69
 
66
- attributes_that should_be_stripped: [ :username, :email, :real_name ]
67
- attributes_that should_be_downcased: [ :username, :email ]
68
- attributes_that should_be_capitalized: [ :real_name ]
70
+ has_attributes_that should_be_stripped: [ :username, :email, :real_name ]
71
+ has_attributes_that should_be_downcased: [ :username, :email ]
72
+ has_attributes_that should_be_capitalized: [ :real_name ]
69
73
 
70
74
  end
71
75
  ```
72
76
 
73
77
  Instead of `attribute_set` you may also use one of the aliases:
74
78
 
75
- * `attributes_that`, `attributes_that_are`, `are_attributes_that_are`, `from_attributes_that_are`,
76
- `within_attributes_that_are`, `attributes_that`, `attributes_are`, `attributes_for`,
77
- `are_attributes`, `are_attributes_for`, `attributes_set`, `properties_that`
79
+ * `attributes_set`, `attributes_that_are`, `attributes_that`, `properties_that`,
80
+ `has_attribute_set`, `has_attribute_that`, `has_attribute_that_is`, `has_attributes`,
81
+ `has_attributes_set`, `has_attributes_that_are`, `has_attributes_that`, `has_properties_that`
78
82
 
79
83
  #### `filter_attribute(attr => set_names)` ####
80
84
 
@@ -90,17 +94,18 @@ Example:
90
94
  ```ruby
91
95
  class User < ActiveRecord::Base
92
96
 
93
- the_attribute real_name: [ :should_be_stripped, :should_be_capitalized ]
94
- the_attribute username: [ :should_be_stripped, :should_be_downcased ]
95
- the_attribute email: [ :should_be_stripped, :should_be_downcased ]
97
+ its_attribute real_name: [ :should_be_stripped, :should_be_capitalized ]
98
+ its_attribute username: [ :should_be_stripped, :should_be_downcased ]
99
+ its_attribute email: [ :should_be_stripped, :should_be_downcased ]
96
100
 
97
101
  end
98
102
  ```
99
103
 
100
104
  Instead of `filter_attribute` you may also use one of the aliases:
101
105
 
102
- * `filtered_attribute`, `the_attribute`, `add_attribute_to_set`,
103
- `add_attribute_to_sets`, `attribute_to_set`, `filtered_attributes`
106
+ * `the_attribute`, `attribute_to_set`, `filtered_attribute`, `filtered_attributes`,
107
+ `filters_attribute`, `filters_attributes`, `its_attribute`, `has_attribute`,
108
+ `has_the_attribute`, `has_filtered_attribute`, `has_filtered_attributes`
104
109
 
105
110
  #### Mixing the syntaxes ####
106
111
 
@@ -110,11 +115,11 @@ You can also use regular arguments instead of hashes to create sets:
110
115
  ```ruby
111
116
  class User < ActiveRecord::Base
112
117
 
113
- attributes_that :should_be_stripped, :username, :email, :real_name
118
+ has_attributes_that :should_be_stripped, :username, :email, :real_name
114
119
 
115
- the_attribute :real_name, :should_be_capitalized
116
- the_attribute :username, :should_be_downcased
117
- the_attribute :email, :should_be_downcased
120
+ its_attribute :real_name, :should_be_capitalized
121
+ its_attribute :username, :should_be_downcased
122
+ its_attribute :email, :should_be_downcased
118
123
 
119
124
  end
120
125
  ```
@@ -128,8 +133,12 @@ of class methods to query them.
128
133
 
129
134
  The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_set)
130
135
  (a.k.a `attributes_that`) class method called with a single argument **returns the attribute set
131
- of the given name**. It will always return the instance of `AttributeSet` class, even if there
132
- is no set registered under the given name (in that case the resulting set will be empty).
136
+ of the given name**.
137
+
138
+ It will always return the instance of `AttributeSet` class, even if there is no set registered under
139
+ the given name (in that case the resulting set will be empty). To know if the returned set is a stub
140
+ (placed instead of the missing one) you can use `frozen?` on it or you can call the DSL method
141
+ `attribute_set_exists?(set_name)` before querying the global sets for a model.
133
142
 
134
143
  Example:
135
144
 
@@ -140,16 +149,23 @@ Example:
140
149
 
141
150
  Note that the returned set will be a copy of the original set stored within your model.
142
151
 
152
+ Alternative syntax:
153
+
154
+ ```ruby
155
+ User.attributes_that.should_be_stripped - User.attributes_that.should_be_downcased
156
+ # => #<ActiveModel::AttributeSet: {"real_name"}>
157
+ ```
158
+
143
159
  Instead of `attribute_set` you may also use one of the aliases:
144
160
 
145
- * `attributes_that`, `attributes_that_are`, `are_attributes_that_are`, `from_attributes_that_are`,
146
- `within_attributes_that_are`, `attributes_that`, `attributes_are`, `attributes_for`,
147
- `are_attributes`, `are_attributes_for`, `attributes_set`, `properties_that`
161
+ * `attributes_set`, `attributes_that_are`, `attributes_that`, `properties_that`,
162
+ `has_attribute_set`, `has_attribute_that`, `has_attribute_that_is`, `has_attributes`,
163
+ `has_attributes_set`, `has_attributes_that_are`, `has_attributes_that`, `has_properties_that`
148
164
 
149
165
  #### `attribute_sets` ####
150
166
 
151
167
  The [`attribute_sets`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_sets)
152
- class method returns a hash containing **all the defined attribute sets** indexed by their names.
168
+ class method returns a MetaSet kind of value containing **all the defined attribute sets** indexed by their names.
153
169
 
154
170
  Example:
155
171
 
@@ -174,8 +190,15 @@ Note that the returned sets will be copies of the original sets stored within yo
174
190
  #### `attribute_set` ####
175
191
 
176
192
  The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_set)
177
- (a.k.a `attributes_that`) class method called without any arguments is a wrapper that calls `attribute_sets` which
178
- returns a hash containing **all the defined attribute sets**.
193
+ (a.k.a `attributes_that`) class method called without any arguments is a DSL wrapper that calls `attribute_set(set_name)`
194
+ with `set_name` taken from next method call.
195
+
196
+ Example:
197
+
198
+ ```ruby
199
+ User.attributes_that.should_be_downcased
200
+ # => #<ActiveModel::AttributeSet: {"username", "email"}>
201
+ ```
179
202
 
180
203
  #### `filter_attribute(attribute_name)` ####
181
204
 
@@ -200,9 +223,8 @@ Instead of `filter_attribute` you may also use one of the aliases:
200
223
  #### `attributes_to_sets` ####
201
224
 
202
225
  The [`attributes_to_sets`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attributes_to_sets)
203
- class method returns a hash containing **all filtered attributes and arrays of sets**
204
- that the attributes belong to. The hash is indexed by attribute names.
205
- (Filtered means that they belong to some sets.)
226
+ class method returns a MetaSet kind of value containing **all filtered attributes and sets that the attributes
227
+ belong to**. The meta set is indexed by attribute names.
206
228
 
207
229
  Example:
208
230
 
@@ -223,8 +245,8 @@ Instead of `attributes_to_sets` you may also use one of the aliases:
223
245
  The [`filter_attribute`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:filter_attribute)
224
246
  (a.k.a `the_attribute`) class method called without any arguments
225
247
  is a wrapper that calls `attributes_to_sets` which returns
226
- a hash containing **all filtered attributes and arrays of sets**
227
- that the attributes belong to.
248
+ a returns a MetaSet kind of value containing **all filtered attributes and sets that the attributes
249
+ belong to**.
228
250
 
229
251
  ### Querying sets in model objects ###
230
252
 
@@ -234,7 +256,8 @@ To do that you may use instance methods that are designed for that purpose.
234
256
  #### `attribute_set` ####
235
257
 
236
258
  The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:attribute_set)
237
- instance method called withount an argument **returns the attribute set object containing all attributes known in a current object**.
259
+ instance method called withount an argument **returns the attribute set object containing all attributes known
260
+ in a current model**.
238
261
 
239
262
  Example:
240
263
 
@@ -280,15 +303,21 @@ There are also variants of this method that differ in a kind of taken argument:
280
303
 
281
304
  Instead of `attribute_set` you may also use one of the aliases:
282
305
 
283
- * `attributes_that_are`, `from_attributes_that`, `are_attributes_that_are`, `from_attributes_that_are`,
284
- `within_attributes_that_are`, `attributes_that`, `attributes_are`,
285
- `attributes_for`, `are_attributes`, `are_attributes_for`, `attributes_set`,
286
- `properties_that`
306
+ * `attributes_set`, `attributes_that_are`, `attributes_that`, `properties_that`,
307
+ `has_attribute_set`, `has_attribute_that`, `has_attribute_that_is`, `has_attributes`,
308
+ `has_attributes_set`, `has_attributes_that_are`, `has_attributes_that`, `has_properties_that`
309
+
310
+ Instead of `attribute_set` you may also use:
311
+
312
+ * `attribute_set_simple(set_name)`
313
+
314
+ It works same way as `attribute_set(set_name)` but doesn't wrap the result in a transparent proxy
315
+ object that brings some syntactic sugar (explained later).
287
316
 
288
317
  #### `attribute_sets` ####
289
318
 
290
319
  The [`attribute_sets`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:attribute_sets)
291
- method returns a hash containing **all the defined attribute sets** indexed by their names.
320
+ method returns a meta set containing **all the defined attribute sets** indexed by their names.
292
321
  It won't return the exact internal hash but a duplicate and every set within it will also be a duplicate
293
322
  of the original one.
294
323
 
@@ -303,7 +332,7 @@ Example:
303
332
  ```
304
333
 
305
334
  Note that the returned hash will have a default value set to instance of the `AttributeSet`.
306
- If you'll try to get the value of an element that doesn't exist **the empty set will be returned**.
335
+ If you'll try to get the value of an element that doesn't exist **the empty, frozen set will be returned**.
307
336
 
308
337
  Instead of `attribute_sets` you may also use one of the aliases:
309
338
 
@@ -312,7 +341,10 @@ Instead of `attribute_sets` you may also use one of the aliases:
312
341
  #### `all_attributes` ####
313
342
 
314
343
  The [`all_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:all_attributes)
315
- method **returns the attribute set containing all known attributes**.
344
+ method **returns the attribute set containing all known attributes**. It combines many other sets,
345
+ including the sets containing attributes marked as semi-real and virtual (explained later)
346
+ and temporary sets obtained by calling various Rails methods. It just tries to collect as many
347
+ known attribute names as possible.
316
348
 
317
349
  Example:
318
350
 
@@ -321,14 +353,25 @@ Example:
321
353
  # => #<ActiveModel::AttributeSet: {"id", "username", "email", "password", "language", "created_at", "updated_at"}>
322
354
  ```
323
355
 
324
- Be aware that this method requires that the used ORM has `attributes` data structure available for any model object.
356
+ Be aware that this method requires that the used ORM has `attributes` method available for a model object.
325
357
 
326
358
  Instead of `all_attributes` you may also use the alias:
327
359
 
328
360
  * `all_attributes_set`
329
-
361
+
330
362
  or call the instance method `attribute_set` without arguments.
331
363
 
364
+ The `all_attributes` synopsis is really:
365
+
366
+ * `all_attributes(simple = false, no_presence_check = true)`
367
+
368
+ First argument (`simple`) causes the method to not wrap the result in a transparent proxy
369
+ object that brings some syntactic sugar (explained later).
370
+
371
+ Second argument (`no_presence_check`) causes the method to not
372
+ check if each attribute exists by verifying presence of its accessor
373
+ in case of semi-real attributes set that is merged into the resulting set.
374
+
332
375
  #### `all_accessible_attributes` ####
333
376
 
334
377
  The [`all_accessible_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:all_accessible_attributes)
@@ -340,12 +383,57 @@ Example:
340
383
  User.first.all_accessible_attributes
341
384
  # => #<ActiveModel::AttributeSet: {"username", "email", "language"}>
342
385
  ```
343
-
344
- Be aware that this method requires that the used ORM has `accessible_attributes` data structure available for any model class.
386
+ Be aware that this method requires that the used ORM has `accessible_attributes` method available for a model
387
+ class. This method works only if your Rails application supoorts accessible attributes (versions up to 3 support it).
345
388
 
346
389
  Instead of `all_accessible_attributes` you may also use the alias:
347
390
 
348
391
  * `accessible_attributes_set`
392
+
393
+ Instead of `all_accessible_attributes` you may also use:
394
+
395
+ * `all_accessible_attributes(true)`
396
+
397
+ It works the same way but doesn't wrap the result in a transparent proxy
398
+ object that brings some syntactic sugar (explained later).
399
+
400
+ #### `all_inaccessible_attributes` ####
401
+
402
+ The [`all_inaccessible_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:all_inaccessible_attributes)
403
+ method **returns the attribute set containing all inaccessible attributes**.
404
+
405
+ Example:
406
+
407
+ ```ruby
408
+ User.first.all_inaccessible_attributes
409
+ # => #<ActiveModel::AttributeSet: {"id", "confirmation_token", "encrypted_password", "deleted_at"}>
410
+ ```
411
+
412
+ Be aware that this method requires that the used ORM has `accessible_attributes` and `protected_attributes`
413
+ method available in a model. This method works only if your Rails application supoorts accessible
414
+ attributes (versions up to 3 support it).
415
+
416
+ Instead of `all_inaccessible_attributes` you may also use the alias:
417
+
418
+ * `inaccessible_attributes_set`
419
+
420
+ Instead of `all_inaccessible_attributes` you may also use:
421
+
422
+ * `all_inaccessible_attributes(true)`
423
+
424
+ It works the same way but doesn't wrap the result in a transparent proxy
425
+ object that brings some syntactic sugar (explained later).
426
+
427
+ The `all_inaccessible_attributes` synopsis is really:
428
+
429
+ * `all_inaccessible_attributes(simple = false, no_presence_check = true)`
430
+
431
+ First argument (`simple`) causes the method to not wrap the result in a transparent proxy
432
+ object that brings some syntactic sugar (explained later).
433
+
434
+ Second argument (`no_presence_check`) causes the method to not
435
+ check if each attribute exists by verifying presence of its accessor
436
+ in case of semi-real attributes set that is used to compute the resulting set.
349
437
 
350
438
  #### `all_protected_attributes` ####
351
439
 
@@ -356,34 +444,80 @@ Example:
356
444
 
357
445
  ```ruby
358
446
  User.first.all_protected_attributes
359
- # => #<ActiveModel::AttributeSet: {"id"}>
447
+ # => #<ActiveModel::AttributeSet: {"id", "type"}>
360
448
  ```
361
449
 
362
- Be aware that this method requires that the used ORM has `protected_attributes` data structure available for any model class.
450
+ Be aware that this method requires that the used ORM has `protected_attributes` method available for a model
451
+ class. This method works only if your Rails application supoorts accessible attributes (versions up to 3 support it).
363
452
 
364
453
  Instead of `all_protected_attributes` you may also use the alias:
365
454
 
366
455
  * `protected_attributes_set`
367
456
 
368
- #### `all_inaccessible_attributes` ####
457
+ Instead of `all_protected_attributes` you may also use:
369
458
 
370
- The [`all_inaccessible_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:all_inaccessible_attributes)
371
- method **returns the attribute set containing all inaccessible attributes**. Inaccessible attributes are attributes
372
- that aren't listed as accessible, which includes protected attributes and attributes for which the `attr_accessible` clause
373
- wasn't used.
459
+ * `all_protected_attributes(true)`
460
+
461
+ It works the same way but doesn't wrap the result in a transparent proxy
462
+ object that brings some syntactic sugar (explained later).
463
+
464
+ #### `all_semi_real_attributes` ####
465
+
466
+ The [`all_semi_real_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:all_semi_real_attributes)
467
+ method **returns the attribute set containing all attributes marked as semi-real**. This is the concept
468
+ of Attribute Filters used to handle attributes that aren't stored in a database (a.k.a virtual attributes, explained later).
374
469
 
375
470
  Example:
376
471
 
377
472
  ```ruby
378
- User.first.all_inaccessible_attributes
379
- # => #<ActiveModel::AttributeSet: {"id", "password", "created_at", "updated_at"}>
473
+ class User
474
+ treats_as_real :trololo
475
+ end
476
+ User.first.all_semi_real_attributes
477
+ # => #<ActiveModel::AttributeSet: {"trololo"}>
380
478
  ```
381
479
 
382
- Be aware that this method requires that the used ORM has `accessible_attributes` data structure available for any model class.
480
+ Instead of `all_semi_real_attributes` you may also use one of the aliases:
383
481
 
384
- Instead of `all_inaccessible_attributes` you may also use the alias:
482
+ * `semi_real_attributes_set`
483
+ * `treat_as_real` (without arguments)
385
484
 
386
- * `inaccessible_attributes_set`
485
+ The `all_semi_real_attributes` synopsis is really:
486
+
487
+ * `all_semi_real_attributes(simple = false, no_presence_check = true)`
488
+
489
+ First argument (`simple`) causes the method to not wrap the result in a transparent proxy
490
+ object that brings some syntactic sugar (explained later).
491
+
492
+ Second argument (`no_presence_check`) causes the method to not
493
+ check if each attribute exists in order to include its name into set.
494
+
495
+
496
+ #### `all_virtual_attributes` ####
497
+
498
+ The [`all_virtual_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:all_virtual_attributes)
499
+ method **returns the attribute set containing all virtual attributes**.
500
+
501
+ Example:
502
+
503
+ ```ruby
504
+ class User
505
+ attr_virtual :lol
506
+ end
507
+ User.first.all_virtual_attributes
508
+ # => #<ActiveModel::AttributeSet: {"lol"}>
509
+ ```
510
+
511
+ Instead of `all_virtual_attributes` you may also use the alias:
512
+
513
+ * `virtual_attributes_set`
514
+
515
+ Instead of `all_virtual_attributes` you may also use:
516
+
517
+ * `all_virtual_attributes(true)`
518
+
519
+ It works the same way but doesn't wrap the result in a transparent proxy
520
+ object that brings some syntactic sugar (explained later).
387
521
 
388
522
  #### `filtered_attribute(attribute_name)` ####
389
523
 
@@ -391,7 +525,10 @@ The [`filtered_attribute`](http://rubydoc.info/gems/attribute-filters/ActiveMode
391
525
  (a.k.a `the_attribute`) method called with a single argument is used for checking
392
526
  **what are the sets that the attribute belongs to**. It won't return the exact set object but a duplicate.
393
527
  It will always return AttributeSet object, even if there is no attribute of the given name
394
- (in that case the resulting set will be empty).
528
+ (in that case the resulting set will be empty and frozen).
529
+
530
+ If the attribute name is not given or it's +nil+ then the name of an attribute is taken from the
531
+ name of a next method in the chain call.
395
532
 
396
533
  Example:
397
534
 
@@ -405,11 +542,16 @@ Instead of `filtered_attribute` you may also use one of the aliases:
405
542
  * `the_attribute`, `is_the_attribute`, `are_attributes`,
406
543
  `are_the_attributes`
407
544
 
545
+ #### `filtered_attribute_simple(attribute_name)` ####
546
+
547
+ The `filtered_attribute_simple` method is a version of ``filtered_attribute` method that doesn't wrap the resulting
548
+ object in a proxy object. The attribute name must be given.
549
+
408
550
  #### `attributes_to_sets` ####
409
551
 
410
552
  The [`attributes_to_sets`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:attributes_to_sets)
411
- method returns a hash containing **all filtered attributes and arrays of sets**
412
- that the attributes belong to. The hash is indexed by attribute names.
553
+ method returns a meta set containing **all filtered attributes and sets that the attributes belong to**.
554
+ The meta set is indexed by attribute names.
413
555
 
414
556
  Example:
415
557
 
@@ -448,6 +590,61 @@ Example:
448
590
  # => false
449
591
  ```
450
592
 
593
+ You can also use one of the aliases:
594
+
595
+ * `invalid?`, `is_not_valid?`, `any_invalid?`, `are_not_valid?`, `not_valid?`, `is_any_invalid?`
596
+ * `valid?`, `is_valid?`, `are_valid?`, `all_valid?`, `are_all_valid?`
597
+
598
+ #### `changed?` and `unchanged?` ####
599
+
600
+ The `changed?` and `unchanged?` helpers allow you to test if **any** attribute listed in the set
601
+ has changed its value (`changed?`) or if **all** attributes from a set remain unchanged (`unchanged?`).
602
+
603
+
604
+ ```ruby
605
+ u = User.first
606
+ u.username = ' '
607
+ # => " "
608
+
609
+ u.all_attributes.any_changed?
610
+ # => true
611
+
612
+ u.all_attributes.unchanged?
613
+ # => false
614
+ ```
615
+
616
+ You can also use one of the aliases:
617
+
618
+ * `changed?`, `any_changed?`, `have_changed?`, `have_any_changed?`, `is_any_changed?`, `has_any_changed?`, `has_changed?`
619
+ * `unchanged?`, `none_changed?`, `nothing_changed?`, `is_unchanged?`, `are_all_unchanged?`, `all_unchanged?`,
620
+ `havent_changed?`, `arent_changed?`, `are_not_changed?`, `none_changed?`, `not_changed?`
621
+
622
+ #### `values` ####
623
+
624
+ This method simply returns the values of all attributes in a set as an array.
625
+
626
+ #### `to_set` ####
627
+
628
+ This method simply produces an object that is a kind of Set and contains all attribute names.
629
+
630
+ ### `AttributeSet` enumerators ###
631
+
632
+ The `AttributeSet` class is derived from `Hash` class. The keys are attributes,
633
+ the values are usually `true` (`TrueClass`) or hashes (`Hash`) if there are any annotations attached
634
+ to attribute name. All overriden enumerators are kind of `AttributeSet::Enumerator`.
635
+
636
+ Let's see what are the differences from standard enumerators that can be found in the Hash class.
637
+
638
+ * `each` – works on keys and values (behaves like `each_pair`) but usable with just one argument in a block (key)
639
+ * `each_pair` – iterates throught key => value pairs
640
+ * `each_name_value(active_model_object, no_presence_check = false)` – iterates throught key => attribute value pairs
641
+ * `collect` (`map`) – produces array containg keys that may be altered by the given block
642
+ * `select` – selects new hash with elements for which the given block evaluates to +true+ (yields: keys, values)
643
+ * `select_accessible(active_model_object)` – same as `select` but picks the attributes which have accessors
644
+ * `reject` – reversed `select`
645
+ * `sort` – sorts AttributeSet object by keys
646
+ * `sort_by` – sorts AttributeSet object using the comparison method from a block
647
+
451
648
  ### Syntactic sugar for queries ###
452
649
 
453
650
  Querying attribute sets may be even more sweet when you add some syntactic sugar
@@ -493,6 +690,25 @@ in order to help you in creating nice looking method call chains like:
493
690
  User.first.attributes_that(:should_be_stripped).sort.list.present?
494
691
  ```
495
692
 
693
+ ##### Set & attribute names as methods #####
694
+
695
+ It's possible to enter a set name or an attribute name as a name
696
+ of next method in a call chain instead of passing them as a symbolic argument.
697
+ It works with DSL instance methods `attribute_set(set_name)`
698
+ and `filtered_attribute(attribute_name)`.
699
+
700
+ Examples:
701
+
702
+ ```ruby
703
+ User.first.attributes_that.should_be_stripped # same as User.first.attributes_that(:should_be_stripped)
704
+ User.first.attributes_that_are.required_to_use_app.present?
705
+ User.first.attributes_that.should_be_stripped.sort.list.present?
706
+
707
+ User.first.the_attribute.username # same as User.first.the_attribute(:username)
708
+ User.first.the_attribute.username.list.sets
709
+ User.first.the_attribute.username.valid?
710
+ ```
711
+
496
712
  ##### Neutral methods #####
497
713
 
498
714
  * **`are`**
@@ -557,7 +773,7 @@ Just imagine that:
557
773
  becomes:
558
774
 
559
775
  ```ruby
560
- attributes_that(:should_be_stripped).all? { |attribute| attribute METHOD(ARGUMENTS) }
776
+ attributes_that(:should_be_stripped).all? { |attribute| attribute.METHOD(ARGUMENTS) }
561
777
  ```
562
778
 
563
779
  Another example, but with `any`:
@@ -567,6 +783,13 @@ Another example, but with `any`:
567
783
  # => true
568
784
  ```
569
785
 
786
+ You also do:
787
+
788
+ ```ruby
789
+ User.first.attributes_that(:should_be_stripped).any.changed?
790
+ # => false
791
+ ```
792
+
570
793
  ##### Elements selectors #####
571
794
 
572
795
  * **`list`**
@@ -612,12 +835,72 @@ Examples:
612
835
 
613
836
  u.attributes_that(:should_be_stripped).list.valid?
614
837
  # => #<ActiveModel::AttributeSet: {"username", "email"}>
838
+
839
+ User.first.attributes_that(:should_be_stripped).is.any.valid?
840
+ # => true
841
+
842
+ User.first.are_attributes_that(:should_be_stripped).all.valid?
843
+ # => true
615
844
  ```
616
845
 
617
846
  Be aware that calling these methods causes model object to be validated. The required condition
618
847
  to use these methods is the ORM that has `errors` hash (Active Record has it).
619
848
 
620
- #### Querying attributes for set names ####
849
+ ##### Changes tracking helpers #####
850
+
851
+ * **`changed?`**
852
+ * **`unchanged?`**
853
+
854
+ Syntactic sugar for changes tracking is used when calls to these methods
855
+ are combined with selectors described above (presence selectors and elements selectors).
856
+
857
+ Examples:
858
+
859
+ ```ruby
860
+ u = User.first
861
+ u.username = " "
862
+
863
+ u.attributes_that(:should_be_stripped).all.changed?
864
+ # => false
865
+
866
+ u.attributes_that(:should_be_stripped).any.changed?
867
+ # => true
868
+
869
+ u.attributes_that(:should_be_stripped).list.changed?
870
+ # => #<ActiveModel::AttributeSet: {"username"}>
871
+
872
+ User.first.all_attributes.is.any.unchanged?
873
+ # => true
874
+
875
+ User.first.all_attributes.are.none.unchanged?
876
+ # => false
877
+ ```
878
+
879
+ Be aware that the required condition to use these methods is the ORM that
880
+ has `changes` hash (Active Record has it).
881
+
882
+ ##### Calling custom methods #####
883
+
884
+ It's possible to call any other methods using selectors.
885
+ Note that te method next to the given selector will be called **on a value** of each tested attribute.
886
+
887
+ Examples:
888
+
889
+ ```ruby
890
+ u = User.first
891
+
892
+ u.attributes_that(:should_be_stripped).all.is_a?(String)
893
+ # => false
894
+
895
+ u.attributes_that(:should_be_stripped).any.is_a?(String)
896
+ # => false
897
+
898
+ u.attributes_that(:should_be_stripped).list.is_a?(String)
899
+ # => #<ActiveModel::AttributeSet: {"username", "email"}>
900
+ ```
901
+
902
+
903
+ #### Querying attributes ####
621
904
 
622
905
  Querying attributes to know sets they belong to uses one
623
906
  instance method available in your models:
@@ -655,10 +938,14 @@ Example:
655
938
 
656
939
  ```ruby
657
940
  User.first.the_attribute(:username).list.sets
658
- # => #<ActiveModel::AttributeSet: {:should_be_downcased, :should_be_stripped}>
941
+ # => {:should_be_downcased => true, :should_be_stripped => true}
942
+
943
+ User.first.the_attribute(:username).list.sets.to_a
944
+ # => [:should_be_downcased, :should_be_stripped]
945
+
659
946
  ```
660
947
 
661
- ##### Set membership testing #####
948
+ ##### Attribute membership querying #####
662
949
 
663
950
  * **`belongs_to?`**
664
951
  * **`in?`**
@@ -686,7 +973,7 @@ Example:
686
973
  # => true
687
974
  ```
688
975
 
689
- ##### Set membership querying #####
976
+ ##### Attribute membership testing #####
690
977
 
691
978
  Membership querying has different syntax from simple testing.
692
979
  It uses set name with the question mark attached to it.
@@ -704,7 +991,7 @@ with question mark. Otherwise you may get false positives
704
991
  or a strange errors when trying to test if attribute belongs
705
992
  to a set. The real method call will override your check.
706
993
 
707
- ##### Set accessibility querying #####
994
+ ##### Attribute accessibility testing #####
708
995
 
709
996
  * **`accessible?`**
710
997
  * **`inaccessible?`**
@@ -727,6 +1014,91 @@ Examples:
727
1014
  # => true
728
1015
  ```
729
1016
 
1017
+ Be aware that this method requires that the used ORM has `accessible_attributes` and `protected_attributes` methods
1018
+ available in a model. This method works only if your Rails application supoorts accessible attributes (versions up to 3 support it).
1019
+
1020
+ ##### Attribute validity testing #####
1021
+
1022
+ * **`valid?`**
1023
+ * **`invalid?`**
1024
+ * **`is_valid?`**
1025
+ * **`is_invalid?`**
1026
+
1027
+ The methods above allow you to test if certain attribute that changed is valid or invalid.
1028
+
1029
+ Examples:
1030
+
1031
+ ```ruby
1032
+ u = User.first
1033
+ u.username = ' '
1034
+ u.the_attribute(:username).is.valid?
1035
+ # => false
1036
+ u.the_attribute(:username).is_valid?
1037
+ # => false
1038
+ u.the_attribute(:username).invalid?
1039
+ # => true
1040
+ ```
1041
+
1042
+ Be aware that this method requires that the used ORM has `valid?` and `errors` methods that allow to validate
1043
+ model on demand. Also note that validations will run when using that methods.
1044
+
1045
+ ##### Attribute change testing #####
1046
+
1047
+ * **`changed?`**
1048
+ * **`unchanged?`**
1049
+ * **`is_changed?`**
1050
+ * **`is_unchanged?`**
1051
+ * **`has_changed?`**
1052
+ * **`hasnt_changed?`**
1053
+ * **`not_changed?`**
1054
+
1055
+ The methods above allow you to test if certain attribute changed recently or not.
1056
+
1057
+ Examples:
1058
+
1059
+ ```ruby
1060
+ u = User.first
1061
+ u.username = ' '
1062
+ u.the_attribute(:username).has_changed?
1063
+ # => true
1064
+ u.the_attribute(:id).unchanged?
1065
+ # => true
1066
+ ```
1067
+
1068
+ Be aware that this method requires that the used ORM has `changes` method that allows to query a model for
1069
+ all changed attributes.
1070
+
1071
+ ##### Attribute virtuality testing #####
1072
+
1073
+ * **`semi_real?`**
1074
+ * **`virtual?`**
1075
+ * **`is_semi_real?`**
1076
+ * **`is_virtual?`**
1077
+
1078
+ The methods above allow to you to test if certain attribute is virtual or semi-real (explained later).
1079
+
1080
+ ```ruby
1081
+ u = User.first
1082
+ u.the_attribute(:id).is.virtual?
1083
+ # => false
1084
+ u.the_attribute(:id).is.semi_real?
1085
+ # => false
1086
+ ```
1087
+
1088
+ ##### Attribute value querying #####
1089
+
1090
+ * `value`
1091
+
1092
+ Using the `value` method you can get the current value of an attribute.
1093
+
1094
+ Example:
1095
+
1096
+ ```ruby
1097
+ u = User.first
1098
+ u.the_attribute.username.value
1099
+ # => "admin"
1100
+ ```
1101
+
730
1102
  Attribute filters
731
1103
  -----------------
732
1104
 
@@ -748,9 +1120,9 @@ class User < ActiveRecord::Base
748
1120
 
749
1121
  # Defining attribute sets
750
1122
 
751
- the_attribute real_name: [ :should_be_stripped, :should_be_capitalized ]
752
- the_attribute username: [ :should_be_stripped, :should_be_downcased ]
753
- the_attribute email: [ :should_be_stripped, :should_be_downcased ]
1123
+ its_attribute real_name: [ :should_be_stripped, :should_be_capitalized ]
1124
+ its_attribute username: [ :should_be_stripped, :should_be_downcased ]
1125
+ its_attribute email: [ :should_be_stripped, :should_be_downcased ]
754
1126
 
755
1127
  # Registering filtering callback methods
756
1128
 
@@ -760,6 +1132,10 @@ class User < ActiveRecord::Base
760
1132
 
761
1133
  # Defining filtering methods
762
1134
 
1135
+ has_filtering_method :downcase_names, :should_be_downcased
1136
+ has_filtering_method :capitalize_names, :should_be_capitalized
1137
+ has_filtering_method :strip_names, :should_be_stripped
1138
+
763
1139
  def downcase_names
764
1140
  filter_attributes_that :should_be_downcased do |atr|
765
1141
  atr.mb_chars.downcase.to_s
@@ -805,7 +1181,7 @@ The evaluated block can make use of the following arguments that are passed to i
805
1181
  * `set_name` [Symbol] - a name of the processed attribute set
806
1182
  * `args` [Array] - an optional arguments passed to the method
807
1183
 
808
- By default only existing, changed and non-blank attributes are processed.
1184
+ By default only **existing, changed and non-blank** attributes are processed.
809
1185
  You can change that behavior by adding a flags as the first arguments:
810
1186
 
811
1187
  * `:process_blank` – tells to also process attributes that are blank (empty or `nil`)
@@ -813,11 +1189,15 @@ You can change that behavior by adding a flags as the first arguments:
813
1189
  * `:no_presence_check` – tells not to check for existence of each processed attribute when processing
814
1190
  all attributes; increases performance but you must care about putting only the existing attributes into sets
815
1191
 
1192
+ The checking mentioned in `:no_presence_check` flag description is done by querying internal Rails hashes
1193
+ containing known attributes and internal sets containing virtual attributes added by `attr_virtual` or
1194
+ `treats_as_real` keywords (described later).
1195
+
816
1196
  Example:
817
1197
 
818
1198
  ```ruby
819
1199
  class User
820
- attributes_that should_be_lolized: [ :username ]
1200
+ has_attribute_that should_be_lolized: :username
821
1201
  before_validation :lolize
822
1202
 
823
1203
  def lolize
@@ -825,6 +1205,7 @@ Example:
825
1205
  [attribute_value, set_name.to_s, attribute_name, *args.flatten].join('-')
826
1206
  end
827
1207
  end
1208
+ filtering_method :lolize, :should_be_lolized
828
1209
  end
829
1210
 
830
1211
  u = User.new
@@ -869,12 +1250,18 @@ You can change that behavior by adding a flags as the first arguments:
869
1250
  * `:process_all` - tells to process all attributes, not just the ones that has changed
870
1251
  * `:no_presence_check` – tells not to check for existence of each processed attribute when processing
871
1252
  all attributes; increases performance but you must care about putting only the existing attributes into sets
1253
+ * `:include_missing` – includes attributes that does not exist in a resulting iteration (their values are
1254
+ always `nil`); has the effect only when `:process_blank` and `:no_presence_check` are present
1255
+
1256
+ The checking mentioned in `:no_presence_check` flag description is done by querying internal Rails hashes
1257
+ containing known attributes and internal sets containing virtual attributes added by `attr_virtual` or
1258
+ `treats_as_real` keywords (described later).
872
1259
 
873
1260
  Example:
874
1261
 
875
1262
  ```ruby
876
1263
  class User
877
- attributes_that should_be_lolized: [ :username ]
1264
+ has_attribute_that should_be_lolized: :username
878
1265
  before_validation :lolize
879
1266
 
880
1267
  def lolize
@@ -882,6 +1269,7 @@ Example:
882
1269
  attribute_object << '-' << [set_name.to_s, attribute_name, *args.flatten].join('-')
883
1270
  end
884
1271
  end
1272
+ filtering_method :lolize, :should_be_lolized
885
1273
  end
886
1274
 
887
1275
  u = User.new
@@ -936,226 +1324,177 @@ if the filtering operation occured.
936
1324
 
937
1325
  There are cases that virtual attributes (the ones that does not really exist in a database)
938
1326
  are to be filtered. By default it won't happen since all the filtering methods assume
939
- the presence of any processed attribute as 'a real' attribute. There are three ways
940
- to overcome that problem.
941
-
942
- #### Unconditional filtering ####
1327
+ the presence of any processed attribute as 'a real' attribute. There are different ways
1328
+ to overcome that problem. First two methods described below are recommended.
943
1329
 
944
- The first way is to pass the `:no_presence_check` and `:process_all` flags to the filtering method.
945
- That causes filter to be executed for each attribute from a set without any conditions (no checking
946
- for presence of setter/getter method and no checking for changes).
1330
+ #### Marking as virtual ####
947
1331
 
948
- Be aware that by doing that you take full responsibility for attribute set names added to your set.
949
- If you put a name of nonexistent attribute then you may get ugly error later.
1332
+ Since version 1.4.0 of Attribute Filters the **recommended way of dealing with
1333
+ virtual attributes** is to make use of Active Model's changes tracking.
1334
+ To do that look at your ORM's documentation and see
1335
+ [`ActiveModel::Dirty`](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html)
1336
+ and a method `attribute_will_change` that it contains. Just call that method from within
1337
+ your setter and you're done.
950
1338
 
951
- With that approach you can filter virtual attributes that are inaccessible to controllers
952
- and don't show up when model is queried for known attributes. Using it may also cause some filters
953
- to be executed more often than needed, since the changes tracking is disabled (`process_all`).
1339
+ This approach can be easily used with predefined filters. The benefit of it,
1340
+ contrary to other methods, is that a filter will never be called redundantly.
954
1341
 
955
- Example:
1342
+ You should use the built-in DSL keyword **`attr_virtual`** that will create
1343
+ setter and getter for you.
956
1344
 
957
1345
  ```ruby
958
- class User
1346
+ class User < ActiveRecord::Base
959
1347
 
960
1348
  # declare a virtual attribute
961
- attr_accessor :real_name
1349
+ attr_virtual :real_name
962
1350
 
963
1351
  # define a set
964
- attributes_that :should_be_splitted => [ :real_name ]
1352
+ has_attributes_that :should_be_splitted => [ :real_name ]
965
1353
 
966
1354
  # register a callback method
967
1355
  before_validation :split_attributes
968
1356
 
969
1357
  # create a filtering method
970
1358
  def split_attributes
971
- for_attributes_that(:should_be_splitted, :no_presence_check, :process_all) do |value|
1359
+ for_attributes_that(:should_be_splitted) do |value|
972
1360
  names = value.split(' ')
973
- self.first_name = names[0] # assuming first_name exists in a database
974
- self.last_name = names[1] # assuming last_name exists in a database
1361
+ self.first_name = names[0]
1362
+ self.last_name = names[1]
975
1363
  end
976
1364
  end
1365
+ filtering_method :split_attributes, :should_be_splitted
977
1366
 
978
1367
  end
979
1368
  ```
980
1369
 
981
- #### Marking as semi-real ####
982
-
983
- The second way is to use `treat_attributes_as_real` (or simply `treat_as_real`) clause in your model
984
- (available from version 1.2.0 of Attribute Filters). That approach may be applied to attributes
985
- that aren't (may not or should not be) tracked for changes and aren't (may not or should not be) accessible
986
- (to a controller) nor marked as protected.
987
-
988
- There are two main differences from the unconditional filtering. First is that marking attribute
989
- as real causes it to be added to the list of all known attributes that is returned by `all_attributes_set`
990
- (a.k.a `all_attributes`). The second is that nothing ugly will happen if there will be non-existent
991
- attributes in a set when filtering method kicks in. That's because the filtering method doesn't require
992
- `:no_presence_check` flag to pick up such attributes. Attributes that could not be handled are simply ignored.
993
-
994
- The `:process_all` flag is also not needed since all virtual attributes marked as real are by default
995
- not checked for changes.
1370
+ Note that for Rails version 3 you may need to declare attribute as accessible using `attr_accessible`
1371
+ if you want controllers to be able to update its value through assignment passed to model.
996
1372
 
997
- Example:
1373
+ You may change the setter and getter for vitrtual attribute on you own, but it should be done
1374
+ somewhere in the code **before** the `attr_virtual` clause. That will allow `attr_virtual`
1375
+ to wrap you methods and enable tracking of changes for the attribute.
998
1376
 
999
1377
  ```ruby
1000
- class User
1001
-
1002
- # declare a virtual attribute
1003
- attr_accessor :real_name
1004
-
1005
- # mark the attribute as real
1006
- treat_as_real :real_name
1378
+ class User < ActiveRecord::Base
1007
1379
 
1008
1380
  # define a set
1009
- attributes_that :should_be_splitted => [ :real_name ]
1381
+ has_attributes_that :should_be_splitted => [ :real_name ]
1010
1382
 
1011
1383
  # register a callback method
1012
- before_validation :split_attributes
1384
+ before_validation :split_attributes # you could also use :filter_attributes here
1013
1385
 
1014
1386
  # create a filtering method
1015
1387
  def split_attributes
1016
1388
  for_attributes_that(:should_be_splitted) do |value|
1017
1389
  names = value.split(' ')
1018
- self.first_name = names[0] # assuming first_name exists in a database
1019
- self.last_name = names[1] # assuming last_name exists in a database
1390
+ self.first_name = names[0]
1391
+ self.last_name = names[1]
1020
1392
  end
1021
1393
  end
1394
+ filtering_method :split_attributes, :should_be_splitted
1022
1395
 
1023
- end
1024
- ```
1025
-
1026
- Be aware that the virtual attributes declared that way will always be filtered
1027
- since there is no way to know whether they have changed or not.
1028
- That may lead to strange results under some circumstances, e.g. in case of
1029
- cutting some part of a string stored in a virtual attribute by using a filter
1030
- registered with `before_save`; if such operation will be performed
1031
- more than once then the filtering will be performed more than once too.
1032
-
1033
- The presence of virtual attributes is tested by checking if both, a setter and a getter,
1034
- methods exist, unless the `no_presence_check` flag is passed to a filtering method.
1035
- If both accessors don't exist then the attribute is not processed.
1396
+ # own setter
1397
+ def real_name=(val)
1398
+ # do somehing specific here (or not)
1399
+ @real_name = val
1400
+ end
1036
1401
 
1037
- This approach can be easily used with predefined filters.
1402
+ # own getter
1403
+ def real_name
1404
+ # do somehing specific here (or not)
1405
+ @real_name
1406
+ end
1038
1407
 
1039
- #### Marking as trackable ####
1408
+ attr_virtual :real_name
1040
1409
 
1041
- Since version 1.4.0 of Attribute Filters the **recommended way of dealing with
1042
- virtual attributes** is to make use of changes tracking available in Active Model.
1043
- To do that look at your ORM's documentation and see
1044
- [`ActiveModel::Dirty`](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html)
1045
- and a method `attribute_will_change` that it contains. Just call that method from within
1046
- your setter and you're done.
1410
+ end
1411
+ ```
1047
1412
 
1048
- This approach can be easily used with predefined filters.
1413
+ #### Unconditional filtering ####
1049
1414
 
1050
- Conditions:
1415
+ The next way to filter virtual attribute is to pass the `:no_presence_check` and `:process_all` flags
1416
+ to the filtering method. That causes filter to be executed for each attribute from a set without
1417
+ any conditions (no checking for presence of setter/getter method and no checking for changes).
1051
1418
 
1052
- * Use `attr_accessible` or `attr_protected` to mark the attribute as known
1053
- * Use your own setter for notifying that attribute value has changed or `attr_virtual`
1419
+ Be aware that by doing that you take full responsibility for attribute set names added to your set.
1420
+ If you put a name of nonexistent attribute then you may get ugly error later.
1054
1421
 
1055
- The benefit of that approach is that a filter will never be called redundantly contrary
1056
- to previous methods.
1422
+ With that approach you can filter virtual attributes that are inaccessible to controllers
1423
+ and don't show up when model is queried for known attributes. Using it may also cause some filters
1424
+ to be executed more often than needed, since the changes tracking is disabled (`process_all`).
1057
1425
 
1058
1426
  Example:
1059
1427
 
1060
1428
  ```ruby
1061
- class User < ActiveRecord::Base
1062
-
1063
- # declare a virtual attribute
1064
- attr_reader :real_name
1065
- attr_accessible :real_name
1429
+ class User
1066
1430
 
1067
1431
  # define a set
1068
- attributes_that :should_be_splitted => [ :real_name ]
1432
+ has_attributes_that :should_be_splitted => [ :real_name ]
1069
1433
 
1070
1434
  # register a callback method
1071
- before_validation :split_attributes
1072
-
1073
- # create writer that notifies Active Model about changes
1074
- def real_name=(val)
1075
- attribute_will_change!('real_name') if val != real_name
1076
- @real_name = val
1077
- end
1435
+ before_validation :split_attributes # you could also use :filter_attributes here
1078
1436
 
1079
1437
  # create a filtering method
1080
1438
  def split_attributes
1081
- for_attributes_that(:should_be_splitted) do |value|
1439
+ for_attributes_that(:should_be_splitted, :no_presence_check, :process_all) do |value|
1082
1440
  names = value.split(' ')
1083
1441
  self.first_name = names[0] # assuming first_name exists in a database
1084
1442
  self.last_name = names[1] # assuming last_name exists in a database
1085
1443
  end
1086
1444
  end
1445
+ filtering_method :split_attributes, :should_be_splitted
1087
1446
 
1088
1447
  end
1089
1448
  ```
1090
1449
 
1091
- You can also use built-in DSL keyword **`attr_virtual`** that will create setter and getter
1092
- for you:
1093
-
1094
- ```ruby
1095
- class User < ActiveRecord::Base
1096
-
1097
- # declare a virtual attribute
1098
- attr_virtual :real_name
1099
- attr_accessible :real_name
1100
-
1101
- # define a set
1102
- attributes_that :should_be_splitted => [ :real_name ]
1103
-
1104
- # register a callback method
1105
- before_validation :split_attributes
1106
-
1107
- # create a filtering method
1108
- def split_attributes
1109
- for_attributes_that(:should_be_splitted) do |value|
1110
- names = value.split(' ')
1111
- self.first_name = names[0]
1112
- self.last_name = names[1]
1113
- end
1114
- end
1450
+ #### Marking as semi-real ####
1115
1451
 
1116
- end
1117
- ```
1452
+ There is also a way that bases on using `treats_attributes_as_real` (or simply `treats_as_real`) clause in your model
1453
+ (available from version 1.2.0 of Attribute Filters). That approach may be applied to attributes
1454
+ that aren't (may not or should not be) tracked for changes and aren't (may not or should not be) accessible
1455
+ (to a controller) nor marked as protected (if using Rails version <= 3).
1118
1456
 
1119
- #### Marking as trackable and semi-real ####
1457
+ There are two main differences from the unconditional filtering. First is that marking attribute
1458
+ as real causes it to be added to the list of all known attributes that is returned by `all_attributes_set`
1459
+ (a.k.a `all_attributes`). The second is that nothing ugly will happen if there will be non-existent
1460
+ attributes in a set when filtering method kicks in. That's because the filtering method doesn't require
1461
+ `:no_presence_check` flag to pick up such attributes. Attributes that cannot be handled are simply ignored.
1120
1462
 
1121
- That's a variant of the recommended way of dealing with virtual attributes. It may be useful
1122
- if you don't want to (or cannot) add virtual attributes to access lists using `attr_accessible`
1123
- or `attr_protected`.
1463
+ The `:process_all` flag is also not needed since all attributes marked as semi-real are by default
1464
+ not checked for changes.
1124
1465
 
1125
1466
  Example:
1126
1467
 
1127
1468
  ```ruby
1128
- class User < ActiveRecord::Base
1129
-
1130
- # declare a virtual attribute
1131
- attr_virtual :real_name
1469
+ class User
1132
1470
 
1133
1471
  # mark the attribute as real
1134
- treat_as_real :real_name
1135
-
1136
- # tell the engine that all virtual attributes
1137
- # are tracked for changes and it should pick from changed
1138
- # not from all
1139
- virtual_attributes_are_tracked
1472
+ treats_as_real :real_name
1140
1473
 
1141
1474
  # define a set
1142
- attributes_that :should_be_splitted => [ :real_name ]
1475
+ has_attributes_that :should_be_splitted => [ :real_name ]
1143
1476
 
1144
1477
  # register a callback method
1145
- before_validation :split_attributes
1478
+ before_validation :split_attributes # you could also use :filter_attributes here
1146
1479
 
1147
1480
  # create a filtering method
1148
1481
  def split_attributes
1149
1482
  for_attributes_that(:should_be_splitted) do |value|
1150
1483
  names = value.split(' ')
1151
- self.first_name = names[0]
1152
- self.last_name = names[1]
1484
+ self.first_name = names[0] # assuming first_name exists in a database
1485
+ self.last_name = names[1] # assuming last_name exists in a database
1153
1486
  end
1154
1487
  end
1488
+ filtering_method :split_attributes, :should_be_splitted
1155
1489
 
1156
1490
  end
1157
1491
  ```
1158
1492
 
1493
+ Instead of `treat_as_real` you may also use one of its aliases:
1494
+
1495
+ * `attribute_filters_semi_real`, `treat_attribute_as_real`, `treat_attributes_as_real`,
1496
+ `treats_attribute_as_real`, `treats_attributes_as_real`, `treats_as_real`,
1497
+
1159
1498
  Annotations
1160
1499
  -----------
1161
1500
 
@@ -1189,7 +1528,7 @@ Example:
1189
1528
  # Annotation key: some_key
1190
1529
  # Annotation value: some value
1191
1530
 
1192
- attributes_that_are cool: { :email => { :some_key => "some value" } }
1531
+ has_attributes_that_are cool: { :email => { :some_key => "some value" } }
1193
1532
  end
1194
1533
  ```
1195
1534
 
@@ -1198,7 +1537,7 @@ You can mix annotated attributes with unannotated; just put the last ones in fro
1198
1537
 
1199
1538
  ```ruby
1200
1539
  class User
1201
- attributes_that_are cool: [ :some_unannotated, { :email => { :some_key => "some value" } } ]
1540
+ has_attributes_that_are cool: [ :some_unannotated, { :email => { :some_key => "some value" } } ]
1202
1541
  end
1203
1542
  ```
1204
1543
 
@@ -1206,7 +1545,7 @@ or
1206
1545
 
1207
1546
  ```ruby
1208
1547
  class User
1209
- attributes_that_are :cool => [ :some_unannotated, { :email => { :some_key => "some value" } } ]
1548
+ has_attributes_that_are :cool => [ :some_unannotated, { :email => { :some_key => "some value" } } ]
1210
1549
  end
1211
1550
  ```
1212
1551
 
@@ -1220,15 +1559,23 @@ To create annotations for class-level sets use the [`annotate_attribute_set`](ht
1220
1559
  * **`annotate_attributes_for`**
1221
1560
  * **`annotate_attributes_set`**
1222
1561
  * **`annotate_properties_that`**
1223
- * **`annotate_attributes`**
1562
+ * **`annotate_attributes`**
1563
+ * **`annotates_attributes_that_are`**
1564
+ * **`annotates_attributes_that`**
1565
+ * **`annotates_attributes_are`**
1566
+ * **`annotates_attributes_for`**
1567
+ * **`annotates_attributes_set`**
1568
+ * **`annotates_properties_that`**
1569
+ * **`annotates_attributes`**
1570
+ * **`attribute_set_annotate`**
1224
1571
 
1225
1572
  Example:
1226
1573
 
1227
1574
  ```ruby
1228
1575
  class User
1229
- attributes_that_are cool: [ :some_unannotated, :email, :username ]
1230
- annotate_attributes_that_are cool: [ :email, :some_key, "some value" ]
1231
- annotate_attributes_that_are :cool => { :username => { :some_key => "some value", :other_k => 'other v' } }
1576
+ has_attributes_that_are cool: [ :some_unannotated, :email, :username ]
1577
+ annotates_attributes_that_are cool: [ :email, :some_key, "some value" ]
1578
+ annotates_attributes_that_are :cool => { :username => { :some_key => "some value", :other_k => 'other v' } }
1232
1579
  end
1233
1580
  ```
1234
1581
 
@@ -1262,48 +1609,52 @@ Be aware that using these method to delete annotations from class-level sets won
1262
1609
  That's because you'll always get a copy when querying these sets. However there are methods that
1263
1610
  will work in a model:
1264
1611
 
1265
- * **`delete_annotations_from_set(set_name, attribute, *annotation_keys)`** - to delete annotation keys for the given attribute
1266
- * **`delete_annotations_from_set(set_name, attribute)`** - to delete all annotation keys for the given attribute
1267
- * **`delete_annotations_from_set(set_name => *attributes)`** - to delete all annotation keys for the given attribute
1268
- * **`delete_annotations_from_set(set_name => { attribute => keys})`** - to delete specified annotation keys for the given attributes
1612
+ * **`delete_annotation_from_set(set_name, attribute, *annotation_keys)`** - to delete annotation keys for the given attribute
1613
+ * **`delete_annotation_from_set(set_name, attribute)`** - to delete all annotation keys for the given attribute
1614
+ * **`delete_annotation_from_set(set_name => *attributes)`** - to delete all annotation keys for the given attribute
1615
+ * **`delete_annotation_from_set(set_name => { attribute => keys})`** - to delete specified annotation keys for the given attributes
1616
+
1617
+ Instead of `delete_annotation_from_set` you may use the aliases:
1618
+
1619
+ * `delete_annotations_from_set`, `delete_annotation_from_set`, `deletes_annotations_from_set`
1269
1620
 
1270
1621
  Example:
1271
1622
 
1272
1623
  ```ruby
1273
1624
  class User
1274
- attributes_that_are cool: [ :some_unannotated, :email ]
1625
+ has_attributes_that_are cool: [ :some_unannotated, :email ]
1275
1626
 
1276
1627
  # That will work
1277
- delete_annotations_from_set cool: :email
1278
- delete_annotations_from_set cool: [ :email, :key_one ]
1279
- delete_annotation_from_set cool: { :email => [:key_one, :other_key], :name => :some_key }
1628
+ deletes_annotations_from_set cool: :email
1629
+ deletes_annotations_from_set cool: [ :email, :key_one ]
1630
+ deletes_annotation_from_set cool: { :email => [:key_one, :other_key], :name => :some_key }
1280
1631
 
1281
1632
  # That won't affect the global set called 'cool'
1282
- # since we have its copy here, not the original.
1633
+ # since we have its copy, not the original.
1283
1634
  def some_method
1284
1635
  attributes_that_are(:cool).delete_annotation(:email)
1285
1636
  end
1286
1637
 
1287
1638
  # That won't affect the global set called 'cool'
1288
- # since we have its copy here, not the original.
1639
+ # since the method returns its copy, not the original.
1289
1640
  attributes_that_are(:cool).delete_annotation(:email)
1290
1641
  end
1291
1642
  ```
1292
1643
 
1293
1644
  ### Updating annotations ###
1294
1645
 
1295
- Calling `annotate` method again on a set or redefining set at a class-level allows to add annotations
1296
- or modify their keys.
1646
+ Calling `annotate` method again on a set or redefining set at the class-level allows to add annotations
1647
+ or to modify their keys.
1297
1648
 
1298
1649
  Example:
1299
1650
 
1300
1651
  ```ruby
1301
1652
  class User
1302
- attributes_that_are :cool => { :email => { :some_key => "some value" } }
1303
- attributes_that_are cool: { :email => { :other_key => "other_value" } }
1304
- attributes_that_are cool: { :email => { :some_key => "another_value" } }
1305
- annotate_attributes_that_are :cool, :email, :some_key => "x"
1306
- delete_annotation_from_set :cool => { :email => :other_key }
1653
+ has_attributes_that_are :cool => { :email => { :some_key => "some value" } }
1654
+ has_attributes_that_are cool: { :email => { :other_key => "other_value" } }
1655
+ has_attributes_that_are cool: { :email => { :some_key => "another_value" } }
1656
+ annotates_attributes_that_are :cool, :email, :some_key => "x"
1657
+ deletes_annotation_from_set :cool => { :email => :other_key }
1307
1658
  end
1308
1659
 
1309
1660
  # In the result there will be only one annotation key left;
@@ -1319,7 +1670,7 @@ a copy when querying these sets.
1319
1670
 
1320
1671
  ```ruby
1321
1672
  class User
1322
- attributes_that_are :cool => { :email => { :some_key => "some value" } }
1673
+ has_attributes_that_are :cool => { :email => { :some_key => "some value" } }
1323
1674
 
1324
1675
  # Calling `some_method` won't work on 'cool' global set.
1325
1676
  def some_method
@@ -1339,8 +1690,8 @@ To check if a set has any annotations you can use one of the methods:
1339
1690
 
1340
1691
  To read annotations you can use :
1341
1692
 
1342
- * **`annotation(attribute_name)`** - gets a hash of annotations or returns nil
1343
- * **`annotation(attribute_name, *keys)`** - gets an array annotation values for the given keys (puts nils if key is missing) or returns nil
1693
+ * **`annotation(attribute_name)`** - gets a hash of annotations or returns `nil`
1694
+ * **`annotation(attribute_name, *keys)`** - gets an array annotation values for the given keys (puts `nil`s if key is missing) or returns `nil`
1344
1695
 
1345
1696
  Example:
1346
1697
 
@@ -1362,38 +1713,51 @@ Example:
1362
1713
  end
1363
1714
  ```
1364
1715
 
1716
+ To read **all annotations** use `annotations` method called on an attribute set. For instance:
1717
+
1718
+ ```ruby
1719
+ User.first.attributes_that(:should_be_splitted).annotations
1720
+ # => {"some_name"=>{:split_into=>[:first_part, :second_part]}}
1721
+ ```
1722
+
1723
+ As you can see it returns a hash with attribute names as keys and annotations as values.
1724
+ If some attribute from a set doesn't have any annotations then it's not included in the results.
1725
+
1365
1726
  Predefined filters
1366
1727
  ------------------
1367
1728
 
1368
- Predefined filters are ready-to-use methods
1369
- for filtering attributes. You just have to call them
1729
+ Predefined filters are ready-to-use methods for filtering attributes. You just have to call them
1370
1730
  or register them as [callbacks](http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html).
1371
1731
 
1372
1732
  To use all predefined filters you have to manually
1373
1733
  include the [`ActiveModel::AttributeFilters::Common`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common)
1374
1734
  module. That will include **all available filtering methods** into your model.
1375
1735
 
1736
+ Predefined filters work by utilizing global attribute sets with predetermined names. For example,
1737
+ a common filter that downcases attributes will use the set called `:should_be_downcased`.
1738
+
1376
1739
  Example:
1377
1740
 
1378
1741
  ```ruby
1379
1742
  class User < ActiveRecord::Base
1380
1743
  include ActiveModel::AttributeFilters::Common
1381
1744
 
1382
- the_attribute name: [:should_be_downcased, :should_be_titleized ]
1745
+ its_attribute name: [:should_be_downcased, :should_be_titleized ]
1383
1746
 
1384
1747
  before_validation :downcase_attributes
1385
1748
  before_validation :titleize_attributes
1386
1749
  end
1387
1750
  ```
1388
1751
 
1389
- If you don't want to include portions of code that you'll never use, you can include some filters selectively. To do that include just a submodule containing certain filtering method:
1752
+ If you don't want to include portions of code that you'll never use in your model classes, you may
1753
+ want to include some filters selectively. To do that just include a submodule containing needed filtering method:
1390
1754
 
1391
1755
  ```ruby
1392
1756
  class User < ActiveRecord::Base
1393
1757
  include ActiveModel::AttributeFilters::Common::Downcase
1394
1758
  include ActiveModel::AttributeFilters::Common::Titleize
1395
1759
 
1396
- the_attribute name: [:should_be_downcased, :should_be_titleized ]
1760
+ its_attribute name: [:should_be_downcased, :should_be_titleized ]
1397
1761
 
1398
1762
  before_validation :downcase_attributes
1399
1763
  before_validation :titleize_attributes
@@ -1411,28 +1775,36 @@ For example, to squeeze attributes `name` and `email` you can write:
1411
1775
  ```ruby
1412
1776
  class User < ActiveRecord::Base
1413
1777
  include ActiveModel::AttributeFilters::Common::Squeeze
1414
- attributes_that should_be_sqeezed: [:email, :name]
1778
+ has_attributes_that should_be_squeezed: [:email, :name]
1415
1779
  before_validation :squeeze_attributes
1416
1780
  end
1417
1781
  ```
1418
1782
 
1783
+ ### Filtering keywords ###
1784
+
1419
1785
  The filtering methods usually come with class-level DSL methods
1420
- that are a simple wrappers calling `the_attribute`. So you can
1421
- also write:
1786
+ that are helpful wrappers. They are simply calling `the_attribute`
1787
+ method to add some attributes to a proper set for you.
1788
+
1789
+ So you can also write:
1422
1790
 
1423
1791
  ```ruby
1424
1792
  class User < ActiveRecord::Base
1425
1793
  include ActiveModel::AttributeFilters::Common::Squeeze
1426
- squeeze_attributes :email, :name
1427
- before_validation :squeeze_attributes
1794
+ squeezes_attributes :email, :name
1795
+ before_validation :squeeze_attributes
1428
1796
  end
1429
1797
  ```
1430
1798
 
1431
- ### Calling all at once ###
1799
+ Some of these helpers are doing some extra work to make things syntactically easier
1800
+ than with using pure sets. Check the list if common filters for more information.
1801
+
1802
+ ### Calling all filters ###
1432
1803
 
1433
1804
  There is a special method called
1434
- [`filter_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters.html#filter_attributes-instance_method) that can be registered as a callback. It will call all possible (known) filtering methods
1435
- in a predetermined order.
1805
+ [`filter_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters.html#filter_attributes-instance_method) that can be registered as a callback. It will call all filtering methods which sets (that they're assigned to)
1806
+ are non-empty. In other words it will run specific filtering method for each global attribute set if the set exists
1807
+ and contains at least one element. The calling order is the same as the order of adding sets in your model class.
1436
1808
 
1437
1809
  Example:
1438
1810
 
@@ -1441,615 +1813,143 @@ class User < ActiveRecord::Base
1441
1813
  include ActiveModel::AttributeFilters::Common::Squeeze
1442
1814
  include ActiveModel::AttributeFilters::Common::Capitalize
1443
1815
 
1444
- squeeze_attributes :email, :name
1445
- capitalize_attributes :name
1816
+ squeezes_attributes :email, :name
1817
+ capitalizes_attribute :name
1446
1818
 
1447
1819
  before_validation :filter_attributes
1448
1820
  end
1449
1821
  ```
1450
1822
 
1451
- Use this method if you're really lazy.
1452
- You can also create your own method like that and call all needed filters there:
1453
-
1454
- ```ruby
1455
- class User < ActiveRecord::Base
1456
- include ActiveModel::AttributeFilters::Common::Squeeze
1457
- include ActiveModel::AttributeFilters::Common::Capitalize
1458
-
1459
- squeeze_attributes :email, :name
1460
- capitalize_attributes :name
1461
-
1462
- before_validation :my_total_filtering_method
1463
-
1464
- def my_total_filtering_method
1465
- squeeze_attributes
1466
- capitalize_attributes
1467
- end
1468
- end
1469
- ```
1470
-
1471
- But to increase readability you should go with the old-fashion way and register
1472
- each filtering callback method separately.
1473
-
1474
- ### Data types ###
1475
-
1476
- The common filters are aware and can operate on attributes that
1477
- are arrays or hashes. If an array or a hash is detected then
1478
- the filtering is made **recursively** for each element (or for each value in case
1479
- of a hash) and the produced structure is returned. If the attribute has an
1480
- unknown type then its value is not altered at all and left intact.
1481
-
1482
- Some of the common filters may treat arrays and hashes in a slight different
1483
- way (e.g. joining and splitting filters do that).
1484
-
1485
- The common filters are aware of multibyte strings so string
1486
- operations should handle diacritics properly.
1487
-
1488
- ### List of filters ###
1489
-
1490
- * **`capitalize_attributes`**
1491
- * **`fully_capitalize_attributes`**
1492
- * **`titleize_attributes`**
1493
- * **`downcase_attributes`**
1494
- * **`upcase_attributes`**
1495
- * **`strip_attributes`**
1496
- * **`squeeze_attributes`**
1497
- * **`squish_attributes`**
1498
-
1499
- See the
1500
- [`ActiveModel::AttributeFilters::Common`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common)
1501
- for detailed descriptions.
1502
-
1503
- #### Case ####
1504
-
1505
- * Submodule: [`Case`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Case.html)
1506
-
1507
- ##### `capitalize_attributes` #####
1508
-
1509
- Capitalizes attributes.
1510
-
1511
- * Callback method: `capitalize_attributes`
1512
- * Class-level helper: `capitalize_attributes(*attribute_names)`
1513
- * Uses set: `:should_be_capitalized`
1514
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1515
- * Uses annotations: no
1516
-
1517
- Example:
1518
-
1519
- ```ruby
1520
- class User < ActiveRecord::Base
1521
- include ActiveModel::AttributeFilters::Common::Case
1522
-
1523
- capitalize_attributes :name
1524
- before_validation :capitalize_attributes
1525
- end
1526
- ```
1527
-
1528
- or
1529
-
1530
- ```ruby
1531
- class User < ActiveRecord::Base
1532
- include ActiveModel::AttributeFilters::Common::Case
1533
-
1534
- attributes_that :should_be_capitalized => [ :name ]
1535
- before_validation :capitalize_attributes
1536
- end
1537
- ```
1538
-
1539
- Then:
1540
-
1541
- > `"some name"`
1542
-
1543
- will become:
1544
-
1545
- > `"Some name"`
1546
-
1547
- ##### `fully_capitalize_attributes` #####
1548
-
1549
- Capitalizes attributes and squeezes spaces that separate strings.
1550
-
1551
- * Callback method: `fully_capitalize_attributes`
1552
- * Class-level helper: `fully_capitalize_attributes(*attribute_names)`
1553
- * Uses set: `:should_be_fully_capitalized` and `:should_be_titleized`
1554
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1555
- * Uses annotations: no
1556
-
1557
- Example:
1558
-
1559
- ```ruby
1560
- class User < ActiveRecord::Base
1561
- include ActiveModel::AttributeFilters::Common::Case
1562
-
1563
- fully_capitalize_attributes :name
1564
- before_validation :fully_capitalize_attributes
1565
- end
1566
- ```
1567
-
1568
- or
1569
-
1570
- ```ruby
1571
- class User < ActiveRecord::Base
1572
- include ActiveModel::AttributeFilters::Common::Case
1573
-
1574
- attributes_that :should_be_fully_capitalized => [ :name ]
1575
- before_validation :fully_capitalize_attributes
1576
- end
1577
- ```
1578
-
1579
- Then:
1580
-
1581
- > `"some name"`
1582
-
1583
- will become:
1584
-
1585
- > `"Some Name"`
1586
-
1587
- ##### `titleize_attributes` #####
1588
-
1589
- Titleizes attributes.
1590
-
1591
- * Callback method: `titleize_attributes`
1592
- * Class-level helper: `titleize_attributes(*attribute_names)`
1593
- * Uses set: `:should_be_titleized`
1594
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1595
- * Uses annotations: no
1596
-
1597
- Example:
1598
-
1599
- ```ruby
1600
- class User < ActiveRecord::Base
1601
- include ActiveModel::AttributeFilters::Common::Case
1602
-
1603
- titleize_attributes :name
1604
- before_validation :titleize_attributes
1605
- end
1606
- ```
1607
-
1608
- or
1823
+ Or even more simpler (but it will include all common filters):
1609
1824
 
1610
1825
  ```ruby
1611
1826
  class User < ActiveRecord::Base
1612
- include ActiveModel::AttributeFilters::Common::Case
1613
-
1614
- attributes_that :should_be_titleized => [ :name ]
1615
- before_validation :titleize_attributes
1616
- end
1617
- ```
1618
-
1619
- Then:
1620
-
1621
- > `"some name"`
1622
-
1623
- will become:
1624
-
1625
- > `"Some Name"`
1626
-
1627
- ##### `upcase_attributes` #####
1628
-
1629
- Upcases attributes.
1630
-
1631
- * Callback method: `upcase_attributes`
1632
- * Class-level helper: `upcase_attributes(*attribute_names)`
1633
- * Uses set: `:should_be_upcased`
1634
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1635
- * Uses annotations: no
1636
-
1637
- Example:
1638
-
1639
- ```ruby
1640
- class User < ActiveRecord::Base
1641
- include ActiveModel::AttributeFilters::Common::Case
1642
-
1643
- upcase_attributes :name
1644
- before_validation :upcase_attributes
1645
- end
1646
- ```
1647
-
1648
- or
1827
+ include ActiveModel::AttributeFilters::Common
1649
1828
 
1650
- ```ruby
1651
- class User < ActiveRecord::Base
1652
- include ActiveModel::AttributeFilters::Common::Case
1829
+ squeezes_attributes :email, :name
1830
+ capitalizes_attribute :name
1653
1831
 
1654
- attributes_that :should_be_upcased => [ :name ]
1655
- before_validation :upcase_attributes
1832
+ before_validation :filter_attributes
1656
1833
  end
1657
1834
  ```
1658
1835
 
1659
- Then:
1660
-
1661
- > `"some name"`
1662
-
1663
- will become:
1664
-
1665
- > `"SOME NAME"`
1666
-
1667
- ##### `downcase_attributes` #####
1668
-
1669
- Downcases attributes.
1836
+ The method of using common filters presented in he example above
1837
+ is the easiest and the most consistent one.
1670
1838
 
1671
- * Callback method: `downcase_attributes`
1672
- * Class-level helper: `downcase_attributes(*attribute_names)`
1673
- * Uses set: `:should_be_downcased`
1674
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1675
- * Uses annotations: no
1676
-
1677
- Example:
1839
+ Of course the `filter_attributes` will also work if the sets are
1840
+ specified explicitly:
1678
1841
 
1679
1842
  ```ruby
1680
1843
  class User < ActiveRecord::Base
1681
- include ActiveModel::AttributeFilters::Common::Case
1682
-
1683
- downcase_attributes :name
1684
- before_validation :downcase_attributes
1685
- end
1686
- ```
1687
-
1688
- or
1844
+ include ActiveModel::AttributeFilters::Common
1689
1845
 
1690
- ```ruby
1691
- class User < ActiveRecord::Base
1692
- include ActiveModel::AttributeFilters::Common::Case
1846
+ has_attributes_that :should_be_squeezed => [ :email, :name ]
1847
+ has_attributes_that :should_be_capitalized => :name
1693
1848
 
1694
- attributes_that :should_be_downcased => [ :name ]
1695
- before_validation :downcase_attributes
1849
+ before_validation :filter_attributes
1696
1850
  end
1697
1851
  ```
1698
1852
 
1699
- Then:
1700
-
1701
- > `"SOME NAME"`
1702
-
1703
- will become:
1704
-
1705
- > `"some name"`
1706
-
1707
- #### Strip ####
1708
-
1709
- * Submodule: [`Strip`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Strip.html)
1710
-
1711
- ##### `strip_attributes` #####
1853
+ To know which methods are known to `filter_attributes`
1854
+ use the `filtering_methods` instance method
1855
+ available in your model. It returns a meta set containing
1856
+ attributes set names as keys and the methods assigned
1857
+ to them as values (both are symbols).
1712
1858
 
1713
- Strips attributes of leading and trailing spaces.
1714
-
1715
- * Callback method: `strip_attributes`
1716
- * Class-level helper: `strip_attributes(*attribute_names)`
1717
- * Uses set: `:should_be_stripped`
1718
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1719
- * Uses annotations: no
1720
-
1721
- Example:
1722
-
1723
- ```ruby
1724
- class User < ActiveRecord::Base
1725
- include ActiveModel::AttributeFilters::Common::Strip
1726
-
1727
- strip_attributes :name
1728
- before_validation :strip_attributes
1729
- end
1730
- ```
1731
-
1732
- or
1859
+ Note that the method above returns **all** known
1860
+ methods marked as filtering methods, including those
1861
+ that might not be called until a proper set will
1862
+ be defined. To check what methods will actually be
1863
+ called (at a given time) use:
1733
1864
 
1734
1865
  ```ruby
1735
- class User < ActiveRecord::Base
1736
- include ActiveModel::AttributeFilters::Common::Strip
1737
-
1738
- attributes_that :should_be_stripped => [ :name ]
1739
- before_validation :strip_attributes
1740
- end
1866
+ (attribute_sets & filtering_methods).values
1741
1867
  ```
1742
1868
 
1743
- Then:
1744
-
1745
- > `" Some Name "`
1746
-
1747
- will become:
1748
-
1749
- > `"Some Name"`
1869
+ ### Custom filtering order ###
1750
1870
 
1751
- #### Squeeze ####
1752
-
1753
- * Submodule: [`Squeeze`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Squeeze.html)
1754
-
1755
- ##### `squeeze_attributes` #####
1756
-
1757
- Squeezes attributes (squeezes repeating spaces into one).
1758
-
1759
- * Callback method: `squeeze_attributes`
1760
- * Class-level helper: `squeeze_attributes(*attribute_names)`
1761
- * Uses set: `:should_be_squeezed`
1762
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1763
- * Uses annotations: no
1764
-
1765
- Example:
1871
+ Instead of relaying on `filter_attributes` you may create
1872
+ your own callback method containing invocations of your
1873
+ filtering methods in the desired order (independent from
1874
+ the "natural" order, based on adding the sets to model class):
1766
1875
 
1767
1876
  ```ruby
1768
1877
  class User < ActiveRecord::Base
1769
- include ActiveModel::AttributeFilters::Common::Squeeze
1878
+ include ActiveModel::AttributeFilters::Common
1770
1879
 
1771
- squeeze_attributes :name
1772
- before_validation :squeeze_attributes
1773
- end
1774
- ```
1880
+ has_attributes_that :should_be_squeezed => [ :email, :name ]
1881
+ has_attribute_that :should_be_capitalized => :name
1775
1882
 
1776
- or
1883
+ before_validation :prepare_attributes
1777
1884
 
1778
- ```ruby
1779
- class User < ActiveRecord::Base
1780
- include ActiveModel::AttributeFilters::Common::Squeeze
1781
-
1782
- attributes_that :should_be_squeezed => [ :name ]
1783
- before_validation :squeeze_attributes
1885
+ def prepare_attributes
1886
+ capitalize_attributes # common filter that uses the set called :should_be_capitalized
1887
+ squeeze_attributes # common filter that uses the set called :should_be_squeezed
1888
+ end
1784
1889
  end
1785
1890
  ```
1786
1891
 
1787
- Then:
1788
-
1789
- > `"Some Name"`
1892
+ Or you can just register multiple callbacks to achieve the same
1893
+ result.
1790
1894
 
1791
- will become:
1895
+ ### Custom filtering methods ###
1792
1896
 
1793
- > `"Some Name"`
1897
+ You can create your own filtering methods and they will be called
1898
+ among others by `filter_attributes`. You just have to use special
1899
+ DSL class method called `filtering_method` to mark your method
1900
+ and assign its name to some global attribute set (not used by
1901
+ any other filtering method).
1794
1902
 
1795
- ##### `squish_attributes` #####
1903
+ Synopsis:
1796
1904
 
1797
- Squishes attributes (removes all whitespace characters on both ends of the string, and then changes remaining consecutive whitespace groups into one space each).
1905
+ * `filtering_method(method_name, set_name)`
1798
1906
 
1799
- * Callback method: `squish_attributes`
1800
- * Class-level helper: `squish_attributes(*attribute_names)`
1801
- * Uses set: `:should_be_squished`
1802
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1803
- * Uses annotations: no
1907
+ The global set of the given name will be looked up when callback
1908
+ method `filter_attributes` will be called and if non-empty then
1909
+ the registered method will be called (among others).
1804
1910
 
1805
1911
  Example:
1806
1912
 
1807
1913
  ```ruby
1808
1914
  class User < ActiveRecord::Base
1809
- include ActiveModel::AttributeFilters::Common::Squeeze
1810
-
1811
- squish_attributes :name
1812
- before_validation :squish_attributes
1813
- end
1814
- ```
1815
-
1816
- or
1817
-
1818
- ```ruby
1819
- class User < ActiveRecord::Base
1820
- include ActiveModel::AttributeFilters::Common::Squeeze
1821
-
1822
- attributes_that :should_be_squished => [ :name ]
1823
- before_validation :squish_attributes
1824
- end
1825
- ```
1826
-
1827
- Then:
1828
-
1829
- > `" Some Name"`
1830
-
1831
- will become:
1832
-
1833
- > `"Some Name"`
1834
-
1835
- #### Split ####
1836
-
1837
- * Submodule: [`Split`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Split.html)
1838
-
1839
- ##### `split_attributes` #####
1840
-
1841
- Splits attributes into arrays and puts the results into other attributes or into the same attributes.
1842
-
1843
- * Callback methods: `split_attributes`
1844
- * Class-level helpers:
1845
- * `split_attributes(attribute_name, parameters_hash)`
1846
- * `split_attributes(attribute_name)`
1847
- * Uses set: `:should_be_splitted`
1848
- * Operates on: strings, arrays of strings, hashes of strings (as values)
1849
- * Uses annotations: yes
1850
- * `split_pattern` - a pattern passed to [`split`](http://www.ruby-doc.org/core/String.html#method-i-split) method (optional)
1851
- * `split_limit` - a limit passed to `split` method (optional)
1852
- * `split_into` - attribute names used as destinations for parts
1853
- * `split_flatten` - flag that causes resulting array to be flattened
1854
-
1855
- If some source attribute is an array or a hash then the filter will recursively traverse it and
1856
- operate on each element. The filter works the same way as the `split` method from the `String` class
1857
- of Ruby's standard library. If the filter encounters an object which is not a string nor an array or a hash,
1858
- it simply leaves it as is.
1859
-
1860
- You can set `:pattern` (`:split_pattern`) and `:limit` (`:split_limit`) arguments passed to
1861
- `split` method but note that **a limit is applied to each processed string separately**,
1862
- not to the resulting array **(if the processed attribute is an array)**. For instance,
1863
- if there is a string containing 3 words (`'A B C'`) and the limit is set to 2 then the last two words
1864
- will be left intact and placed in a second element of the resulting array (`['A', 'B C']`).
1865
- If the source is an array (`['A', 'B B B B', 'C']`) the result of this operation will be array of arrays
1866
- (`[ ['A'], ['B B'], ['C'] ]`); as you can see the limit will be applied to its second element.
1867
-
1868
- If there are no destination attributes defined (`:into` or `:split_into` option) then
1869
- the resulting array will be written to a current attribute. If there are destination attributes
1870
- given then the resulting array will be written into them (each subsequent element into each next attribute).
1871
- The elements that don't fit in the collection are simply ignored.
1872
-
1873
- There is also `flatten` (or `:split_flatten`) parameter that causes the resulting array to be
1874
- flattened. Note that it doesn't change how the limits work; they still will be applied but to a single
1875
- split results, not to the whole resulting array (in case of array of arrays).
1876
-
1877
- Examples:
1878
-
1879
- ```ruby
1880
- class User < ActiveRecord::Base
1881
- # Including common filter for splitting
1882
- include ActiveModel::AttributeFilters::Common::Split
1883
-
1884
- # Registering virtual attribute
1885
- attr_virtual :real_name
1886
- attr_accessible :real_name
1887
-
1888
- # Adding attribute name to :should_be_splitted set
1889
- split_attributes :real_name
1890
-
1891
- # Registering callback method
1892
- # Warning: it will be executed each time model object is validated
1893
- # (the nice thing is that it allows to validate the results, not the unsplitted data)
1894
- before_validation :split_attributes
1895
- end
1896
- ```
1897
-
1898
- or without a `split_attributes` helper:
1899
-
1900
- ```ruby
1901
- class User < ActiveRecord::Base
1902
- # Including common filter for splitting
1903
- include ActiveModel::AttributeFilters::Common::Split
1904
-
1905
- # Registering virtual attribute
1906
- attr_virtual :real_name
1907
- attr_accessible :real_name
1908
-
1909
- # Adding attribute name to :should_be_splitted set (by hand)
1910
- attributes_that :should_be_splitted => :real_name
1911
-
1912
- # Registering callback method
1913
- before_validation :split_attributes
1914
- end
1915
- ```
1916
-
1917
- The result of executing the filter above will be replacement of a string by an array containing
1918
- words (each one in a separate element). The `real_name` attribute is a virtual attribute in this example
1919
- but it could be real attribute. The result will be written as an array into the same attribute since there
1920
- are no destination attributes given. So `'Paul Wolf'` will become `['Paul', 'Wolf']`.
1921
-
1922
- Using limit:
1923
-
1924
- ```ruby
1925
- class User < ActiveRecord::Base
1926
- include ActiveModel::AttributeFilters::Common::Split
1927
-
1928
- attr_virtual :real_name
1929
- attr_accessible :real_name
1930
- split_attributes :real_name, :limit => 2
1931
- before_validation :split_attributes
1932
- end
1933
- ```
1934
-
1935
- or without a `split_attributes` keyword:
1936
-
1937
- ```ruby
1938
- class User < ActiveRecord::Base
1939
- include ActiveModel::AttributeFilters::Common::Split
1940
-
1941
- attr_virtual :real_name
1942
- attr_accessible :real_name
1943
-
1944
- attributes_that :should_be_splitted => { :real_name => { :split_limit => 2 } }
1945
- before_validation :split_attributes
1946
- end
1947
- ```
1948
-
1949
- The result of the above example will be the same as the previous one with the difference that any
1950
- reduntant elements will be left intact and placed as the last element of an array. So for data:
1951
-
1952
- > `'Paul Thomas Wolf'`
1953
-
1954
- the array will be:
1955
-
1956
- > `[ 'Paul', 'Thomas Wolf' ]`
1957
-
1958
- Another example, let's write results to some attributes:
1915
+ include ActiveModel::AttributeFilters::Common
1959
1916
 
1960
- ```ruby
1961
- class User < ActiveRecord::Base
1962
- include ActiveModel::AttributeFilters::Common::Split
1917
+ has_attributes_that should_be_happy: [ :username, :real_name ]
1918
+ before_validation :filter_attributes
1963
1919
 
1964
- attr_virtual :real_name
1965
- attr_accessible :real_name
1966
- split_attributes :real_name, :limit => 2, :into => [ :first_name, :last_name ], :pattern => ' '
1967
- before_validation :split_attributes
1920
+ def happify
1921
+ filter_attributes_that(:should_be_happy) do |v, o|
1922
+ v + "-happy"
1923
+ end
1924
+ end
1925
+ filtering_method :happify, :should_be_happy
1968
1926
  end
1969
1927
  ```
1970
1928
 
1971
- (The `:pattern` is given here but you may skip it if it's a space.)
1972
-
1973
- This will split a value of the `real_name` attribute and place the results in the attributes
1974
- called `first_name` and `last_name`, so for:
1975
-
1976
- > `'Paul Thomas Wolf'`
1977
-
1978
- the result will be:
1979
-
1980
- ```
1981
- first_name: 'Paul'
1982
- last_name: 'Thomas Wolf'
1983
- ```
1984
-
1985
- If you remove the limit, then it will be quite different:
1986
-
1987
- ```
1988
- first_name: 'Paul'
1989
- last_name: 'Thomas'
1990
- ```
1991
-
1992
- That's because there are more results than attributes they fit into. You just have to keep in mind
1993
- that this filter behaves like the String's split method with the difference when the results are written
1994
- into other attributes. In that case the limit causes redundant data to be placed in the last element (if a limit
1995
- is lower or is the same as the count of destination attributes) and its lack causes some of the resulting data to
1996
- be ignored (if there are more slices than receiving attributes).
1997
-
1998
- The pattern parameter (`:pattern` when using `split_attributes` or `:split_pattern` when directly
1999
- annotating attribute in a set) should be a string.
2000
-
2001
- #### Join ####
2002
-
2003
- * Submodule: [`Join`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Join.html)
2004
-
2005
- ##### `join_attributes` #####
2006
-
2007
- Joins attributes and places the results into other attributes or into the same attributes as strings.
1929
+ Of course, you can set your own method as a callback explicitly
1930
+ instead of using the `filter_attributes`.
2008
1931
 
2009
- * Callback method: `join_attributes`
2010
- * Class-level helpers:
2011
- * `join_attributes(attribute_name, parameters_hash)` (a.k.a `joint_attribute`)
2012
- * `join_attributes(attribute_name)` (a.k.a `joint_attribute`)
2013
- * Uses set: `:should_be_joined`
2014
- * Operates on: strings, arrays of strings
2015
- * Uses annotations: yes
2016
- * `join_separator` - a pattern passed to [`join`](http://www.ruby-doc.org/core/Array.html#method-i-join) method (optional)
2017
- * `join_compact` - compact flag; if true then an array is compacted before it's joined (optional)
2018
- * `join_from` - attribute names used as sources for joins
2019
-
2020
- The join filter uses `join` instance method of the `Array` class to produce single string from multiple strings.
2021
- These strings may be values of other attributes (source attributes), values of an array stored in an attribute
2022
- or mix of it. If the `:compact` (`:join_compact` in case of manually annotating a set) parameter is given
2023
- and it's not `false` nor `nil` then results are compacted during processing. That means any slices equals to `nil` are
2024
- removed.
2025
-
2026
- If the parameter `:from` (or annotation key `:join_from`) was not given then a currently processed attribute
2027
- is treated as a source (it should be an array).
2028
-
2029
- Examples:
1932
+ ### Data types ###
2030
1933
 
2031
- ```ruby
2032
- class User < ActiveRecord::Base
2033
- include ActiveModel::AttributeFilters::Common::Join
1934
+ The common filters are aware of different kinds of data and can operate
1935
+ on attributes that are arrays or hashes. If an array or a hash is detected then
1936
+ the filtering is made **recursively** for each element (or for each value in case
1937
+ of a hash) and the produced value is returned. If the attribute has an
1938
+ unknown type then its value is not altered at all and left intact.
2034
1939
 
2035
- attr_virtual :first_name, :last_name
2036
- attr_accessible :first_name, :last_name
2037
- join_attributes_into :real_name, :from => [ :first_name, :last_name ]
2038
- before_validation :join_attributes
2039
- end
2040
- ```
1940
+ Some of the common filters may treat arrays and hashes in a slight different
1941
+ way (e.g. joining and splitting filters do that).
2041
1942
 
2042
- you can also switch source with destination:
1943
+ The common filters are aware of multibyte strings so string
1944
+ operations should handle diacritics properly.
2043
1945
 
2044
- ```ruby
2045
- join_attributes [ :first_name, :last_name ] => :real_name
2046
- ```
1946
+ ### List of filters ###
2047
1947
 
2048
- or add a descriptive keyword `:into`:
1948
+ See the [COMMON-FILTERS](http://rubydoc.info/gems/attribute-filters/file/docs/COMMON-FILTERS.md) for detailed descriptions and usage examples.
2049
1949
 
2050
- ```ruby
2051
- join_attributes [ :first_name, :last_name ], :into => :real_name
2052
- ```
1950
+ See the
1951
+ [`ActiveModel::AttributeFilters::Common`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common)
1952
+ for descriptions of common filtering modules.
2053
1953
 
2054
1954
  Custom applications
2055
1955
  -------------------
@@ -2064,16 +1964,16 @@ Example:
2064
1964
  ```ruby
2065
1965
  class User < ActiveRecord::Base
2066
1966
 
2067
- attributes_that_are required_to_trade: [ :username, :email, :real_name, :address, :account ]
1967
+ has_attributes_that_are required_to_trade: [ :username, :email, :real_name, :address, :account ]
2068
1968
 
2069
1969
  def is_able_to_trade?
2070
- are_attributes_that_are(:required_to_trade).all.present? and
2071
- are_attributes_that_are(:required_to_trade).all.valid?
1970
+ are_attributes_that_are.required_to_trade.all.present? and
1971
+ are_attributes_that_are.required_to_trade.all.valid?
2072
1972
  end
2073
1973
 
2074
1974
  def attributes_missing_to_trade
2075
- attributes_that_are(:required_to_trade).list.blank? +
2076
- attributes_that_are(:required_to_trade).list.invalid?
1975
+ attributes_that_are.required_to_trade.list.blank? +
1976
+ attributes_that_are.required_to_trade.list.invalid?
2077
1977
  end
2078
1978
  end
2079
1979
  ```