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.
- data.tar.gz.sig +0 -0
- data/.yardopts +1 -0
- data/ChangeLog +501 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -7
- data/Manifest.txt +7 -0
- data/README.md +59 -30
- data/Rakefile +1 -0
- data/attribute-filters.gemspec +4 -4
- data/docs/COMMON-FILTERS.md +1067 -0
- data/docs/HISTORY +42 -0
- data/docs/TODO +29 -12
- data/docs/USAGE.md +718 -818
- data/lib/attribute-filters.rb +4 -2
- data/lib/attribute-filters/attribute_set.rb +144 -73
- data/lib/attribute-filters/attribute_set_annotations.rb +110 -77
- data/lib/attribute-filters/attribute_set_attrquery.rb +51 -8
- data/lib/attribute-filters/attribute_set_enum.rb +44 -38
- data/lib/attribute-filters/attribute_set_query.rb +62 -12
- data/lib/attribute-filters/backports.rb +36 -4
- data/lib/attribute-filters/common_filters.rb +83 -37
- data/lib/attribute-filters/common_filters/bare.rb +29 -0
- data/lib/attribute-filters/common_filters/case.rb +34 -19
- data/lib/attribute-filters/common_filters/convert.rb +259 -0
- data/lib/attribute-filters/common_filters/join.rb +37 -30
- data/lib/attribute-filters/common_filters/order.rb +105 -0
- data/lib/attribute-filters/common_filters/pick.rb +90 -0
- data/lib/attribute-filters/common_filters/presence.rb +70 -0
- data/lib/attribute-filters/common_filters/split.rb +37 -21
- data/lib/attribute-filters/common_filters/squeeze.rb +28 -9
- data/lib/attribute-filters/common_filters/strip.rb +7 -3
- data/lib/attribute-filters/dsl_attr_virtual.rb +2 -1
- data/lib/attribute-filters/dsl_filters.rb +14 -61
- data/lib/attribute-filters/dsl_sets.rb +238 -88
- data/lib/attribute-filters/helpers.rb +7 -1
- data/lib/attribute-filters/meta_set.rb +38 -0
- data/lib/attribute-filters/version.rb +1 -1
- data/spec/attribute-filters_spec.rb +178 -16
- data/spec/spec_helper.rb +9 -4
- metadata +129 -69
- 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
|
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
|
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
|
8
|
-
|
9
|
-
|
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
|
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
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
41
|
-
remove elements from it
|
42
|
-
|
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
|
53
|
-
|
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 `
|
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
|
-
|
67
|
-
|
68
|
-
|
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
|
-
* `
|
76
|
-
`
|
77
|
-
`
|
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
|
-
|
94
|
-
|
95
|
-
|
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
|
-
* `
|
103
|
-
`
|
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
|
-
|
118
|
+
has_attributes_that :should_be_stripped, :username, :email, :real_name
|
114
119
|
|
115
|
-
|
116
|
-
|
117
|
-
|
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**.
|
132
|
-
|
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
|
-
* `
|
146
|
-
`
|
147
|
-
`
|
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
|
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 `
|
178
|
-
|
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
|
204
|
-
|
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
|
227
|
-
|
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
|
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
|
-
* `
|
284
|
-
`
|
285
|
-
`
|
286
|
-
|
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
|
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`
|
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
|
-
|
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`
|
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
|
-
|
457
|
+
Instead of `all_protected_attributes` you may also use:
|
369
458
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
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
|
379
|
-
|
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
|
-
|
480
|
+
Instead of `all_semi_real_attributes` you may also use one of the aliases:
|
383
481
|
|
384
|
-
|
482
|
+
* `semi_real_attributes_set`
|
483
|
+
* `treat_as_real` (without arguments)
|
385
484
|
|
386
|
-
|
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
|
412
|
-
|
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
|
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
|
-
|
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
|
-
# =>
|
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
|
-
#####
|
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
|
-
#####
|
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
|
-
#####
|
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
|
-
|
752
|
-
|
753
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
949
|
-
|
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
|
-
|
952
|
-
|
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
|
-
|
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
|
-
|
1349
|
+
attr_virtual :real_name
|
962
1350
|
|
963
1351
|
# define a set
|
964
|
-
|
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
|
1359
|
+
for_attributes_that(:should_be_splitted) do |value|
|
972
1360
|
names = value.split(' ')
|
973
|
-
self.first_name = names[0]
|
974
|
-
self.last_name = names[1]
|
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
|
-
|
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
|
-
|
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
|
-
|
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]
|
1019
|
-
self.last_name = names[1]
|
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
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
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
|
-
|
1402
|
+
# own getter
|
1403
|
+
def real_name
|
1404
|
+
# do somehing specific here (or not)
|
1405
|
+
@real_name
|
1406
|
+
end
|
1038
1407
|
|
1039
|
-
|
1408
|
+
attr_virtual :real_name
|
1040
1409
|
|
1041
|
-
|
1042
|
-
|
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
|
-
|
1413
|
+
#### Unconditional filtering ####
|
1049
1414
|
|
1050
|
-
|
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
|
-
|
1053
|
-
|
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
|
-
|
1056
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1122
|
-
|
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
|
1129
|
-
|
1130
|
-
# declare a virtual attribute
|
1131
|
-
attr_virtual :real_name
|
1469
|
+
class User
|
1132
1470
|
|
1133
1471
|
# mark the attribute as real
|
1134
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1230
|
-
|
1231
|
-
|
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
|
-
* **`
|
1266
|
-
* **`
|
1267
|
-
* **`
|
1268
|
-
* **`
|
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
|
-
|
1625
|
+
has_attributes_that_are cool: [ :some_unannotated, :email ]
|
1275
1626
|
|
1276
1627
|
# That will work
|
1277
|
-
|
1278
|
-
|
1279
|
-
|
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
|
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
|
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
|
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
|
-
|
1303
|
-
|
1304
|
-
|
1305
|
-
|
1306
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
1421
|
-
|
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
|
-
|
1427
|
-
before_validation
|
1794
|
+
squeezes_attributes :email, :name
|
1795
|
+
before_validation :squeeze_attributes
|
1428
1796
|
end
|
1429
1797
|
```
|
1430
1798
|
|
1431
|
-
|
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
|
1435
|
-
|
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
|
-
|
1445
|
-
|
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
|
-
|
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
|
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
|
-
|
1651
|
-
|
1652
|
-
include ActiveModel::AttributeFilters::Common::Case
|
1829
|
+
squeezes_attributes :email, :name
|
1830
|
+
capitalizes_attribute :name
|
1653
1831
|
|
1654
|
-
|
1655
|
-
before_validation :upcase_attributes
|
1832
|
+
before_validation :filter_attributes
|
1656
1833
|
end
|
1657
1834
|
```
|
1658
1835
|
|
1659
|
-
|
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
|
-
|
1672
|
-
|
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
|
1682
|
-
|
1683
|
-
downcase_attributes :name
|
1684
|
-
before_validation :downcase_attributes
|
1685
|
-
end
|
1686
|
-
```
|
1687
|
-
|
1688
|
-
or
|
1844
|
+
include ActiveModel::AttributeFilters::Common
|
1689
1845
|
|
1690
|
-
|
1691
|
-
|
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
|
-
|
1695
|
-
before_validation :downcase_attributes
|
1849
|
+
before_validation :filter_attributes
|
1696
1850
|
end
|
1697
1851
|
```
|
1698
1852
|
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
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
|
-
|
1714
|
-
|
1715
|
-
|
1716
|
-
|
1717
|
-
|
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
|
-
|
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
|
-
|
1744
|
-
|
1745
|
-
> `" Some Name "`
|
1746
|
-
|
1747
|
-
will become:
|
1748
|
-
|
1749
|
-
> `"Some Name"`
|
1869
|
+
### Custom filtering order ###
|
1750
1870
|
|
1751
|
-
|
1752
|
-
|
1753
|
-
|
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
|
1878
|
+
include ActiveModel::AttributeFilters::Common
|
1770
1879
|
|
1771
|
-
|
1772
|
-
|
1773
|
-
end
|
1774
|
-
```
|
1880
|
+
has_attributes_that :should_be_squeezed => [ :email, :name ]
|
1881
|
+
has_attribute_that :should_be_capitalized => :name
|
1775
1882
|
|
1776
|
-
|
1883
|
+
before_validation :prepare_attributes
|
1777
1884
|
|
1778
|
-
|
1779
|
-
|
1780
|
-
|
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
|
-
|
1788
|
-
|
1789
|
-
> `"Some Name"`
|
1892
|
+
Or you can just register multiple callbacks to achieve the same
|
1893
|
+
result.
|
1790
1894
|
|
1791
|
-
|
1895
|
+
### Custom filtering methods ###
|
1792
1896
|
|
1793
|
-
|
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
|
-
|
1903
|
+
Synopsis:
|
1796
1904
|
|
1797
|
-
|
1905
|
+
* `filtering_method(method_name, set_name)`
|
1798
1906
|
|
1799
|
-
|
1800
|
-
|
1801
|
-
|
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
|
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
|
-
|
1961
|
-
|
1962
|
-
include ActiveModel::AttributeFilters::Common::Split
|
1917
|
+
has_attributes_that should_be_happy: [ :username, :real_name ]
|
1918
|
+
before_validation :filter_attributes
|
1963
1919
|
|
1964
|
-
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
2032
|
-
|
2033
|
-
|
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
|
-
|
2036
|
-
|
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
|
-
|
1943
|
+
The common filters are aware of multibyte strings so string
|
1944
|
+
operations should handle diacritics properly.
|
2043
1945
|
|
2044
|
-
|
2045
|
-
join_attributes [ :first_name, :last_name ] => :real_name
|
2046
|
-
```
|
1946
|
+
### List of filters ###
|
2047
1947
|
|
2048
|
-
|
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
|
-
|
2051
|
-
|
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
|
-
|
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
|
2071
|
-
are_attributes_that_are
|
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
|
2076
|
-
attributes_that_are
|
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
|
```
|