attribute-filters 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +388 -0
- data/Gemfile.lock +14 -14
- data/Manifest.txt +8 -0
- data/README.md +45 -31
- data/attribute-filters.gemspec +3 -3
- data/docs/HISTORY +63 -27
- data/docs/TODO +17 -2
- data/docs/USAGE.md +1161 -76
- data/lib/attribute-filters.rb +4 -0
- data/lib/attribute-filters/attribute_set.rb +92 -1
- data/lib/attribute-filters/attribute_set_annotations.rb +259 -0
- data/lib/attribute-filters/attribute_set_attrquery.rb +9 -8
- data/lib/attribute-filters/attribute_set_enum.rb +76 -45
- data/lib/attribute-filters/attribute_set_query.rb +65 -7
- data/lib/attribute-filters/backports.rb +37 -0
- data/lib/attribute-filters/common_filters.rb +46 -128
- data/lib/attribute-filters/common_filters/case.rb +159 -0
- data/lib/attribute-filters/common_filters/join.rb +98 -0
- data/lib/attribute-filters/common_filters/split.rb +130 -0
- data/lib/attribute-filters/common_filters/squeeze.rb +75 -0
- data/lib/attribute-filters/common_filters/strip.rb +46 -0
- data/lib/attribute-filters/dsl_attr_virtual.rb +50 -0
- data/lib/attribute-filters/dsl_filters.rb +40 -29
- data/lib/attribute-filters/dsl_sets.rb +122 -32
- data/lib/attribute-filters/helpers.rb +14 -0
- data/lib/attribute-filters/version.rb +1 -1
- data/spec/attribute-filters_spec.rb +334 -13
- data/spec/spec_helper.rb +9 -20
- metadata +37 -29
- metadata.gz.sig +0 -0
data/Manifest.txt
CHANGED
@@ -20,10 +20,18 @@ init.rb
|
|
20
20
|
lib/attribute-filters.rb
|
21
21
|
lib/attribute-filters/active_model_insert.rb
|
22
22
|
lib/attribute-filters/attribute_set.rb
|
23
|
+
lib/attribute-filters/attribute_set_annotations.rb
|
23
24
|
lib/attribute-filters/attribute_set_attrquery.rb
|
24
25
|
lib/attribute-filters/attribute_set_enum.rb
|
25
26
|
lib/attribute-filters/attribute_set_query.rb
|
27
|
+
lib/attribute-filters/backports.rb
|
26
28
|
lib/attribute-filters/common_filters.rb
|
29
|
+
lib/attribute-filters/common_filters/case.rb
|
30
|
+
lib/attribute-filters/common_filters/join.rb
|
31
|
+
lib/attribute-filters/common_filters/split.rb
|
32
|
+
lib/attribute-filters/common_filters/squeeze.rb
|
33
|
+
lib/attribute-filters/common_filters/strip.rb
|
34
|
+
lib/attribute-filters/dsl_attr_virtual.rb
|
27
35
|
lib/attribute-filters/dsl_filters.rb
|
28
36
|
lib/attribute-filters/dsl_sets.rb
|
29
37
|
lib/attribute-filters/helpers.rb
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Attribute Filters for Rails
|
2
2
|
===========================
|
3
3
|
|
4
|
-
**attribute-filters version `1.
|
4
|
+
**attribute-filters version `1.4`** (**`Fried Meerkat`**)
|
5
5
|
|
6
6
|
* https://rubygems.org/gems/attribute-filters
|
7
7
|
* https://github.com/siefca/attribute-filters/tree
|
@@ -23,12 +23,13 @@ You may want to try it when your Rails application often modifies
|
|
23
23
|
attribute values that changed recently and uses callbacks to do that.
|
24
24
|
|
25
25
|
When the number of attributes that are altered in such a way increases,
|
26
|
-
you can observe the same thing happening with your filtering
|
26
|
+
you can observe that the same thing is happening with your filtering
|
27
27
|
methods. That's because each one is tied to some attribute.
|
28
28
|
|
29
29
|
To refine that process you may write more generic methods
|
30
30
|
for altering attributes. They should be designed to handle
|
31
31
|
common operations and not be tied to certain attributes.
|
32
|
+
Attribute Filters helps you do that.
|
32
33
|
|
33
34
|
Let's see that in action.
|
34
35
|
|
@@ -68,6 +69,37 @@ The filtering code is not reusable since it operates on specific attributes.
|
|
68
69
|
|
69
70
|
### After ###
|
70
71
|
|
72
|
+
```ruby
|
73
|
+
class User < ActiveRecord::Base
|
74
|
+
include ActiveModel::AttributeFilters::Common
|
75
|
+
|
76
|
+
strip_attributes :username, :email, :real_name
|
77
|
+
downcase_attributes :username, :email
|
78
|
+
titleize_attribute :real_name
|
79
|
+
|
80
|
+
before_validation :filter_attributes
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
or if you want to have more control:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class User < ActiveRecord::Base
|
88
|
+
include ActiveModel::AttributeFilters::Common::Strip
|
89
|
+
include ActiveModel::AttributeFilters::Common::Downcase
|
90
|
+
include ActiveModel::AttributeFilters::Common::Titleize
|
91
|
+
|
92
|
+
attributes_that should_be_stripped: [ :username, :email, :real_name ]
|
93
|
+
attributes_that should_be_downcased: [ :username, :email ]
|
94
|
+
attributes_that should_be_titleized: [ :real_name ]
|
95
|
+
|
96
|
+
before_validation :strip_attributes
|
97
|
+
before_validation :downcase_attributes
|
98
|
+
before_validation :titleize_attributes
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
or if you would like to create filtering methods on your own:
|
71
103
|
|
72
104
|
```ruby
|
73
105
|
class User < ActiveRecord::Base
|
@@ -97,26 +129,18 @@ class User < ActiveRecord::Base
|
|
97
129
|
end
|
98
130
|
```
|
99
131
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
include ActiveModel::AttributeFilters::Common::Downcase
|
106
|
-
include ActiveModel::AttributeFilters::Common::Titleize
|
107
|
-
|
108
|
-
attributes_that should_be_stripped: [ :username, :email, :real_name ]
|
109
|
-
attributes_that should_be_downcased: [ :username, :email ]
|
110
|
-
attributes_that should_be_titleized: [ :real_name ]
|
132
|
+
Attributes that should be altered may be simply added
|
133
|
+
to the attribute sets that you define and then filtered
|
134
|
+
with generic methods that operate on that sets. You can share
|
135
|
+
these methods across all of your models by putting them into your own
|
136
|
+
handy module that will be included in models that needs it.
|
111
137
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
```
|
138
|
+
Alternatively (as presented at the beginning) you can use predefined filtering methods from `ActiveModel::AttributeFilters::Common` module and its submodules. They contain filters
|
139
|
+
for changing the case, stripping, squishng, squeezing, joining, splitting
|
140
|
+
[and more](http://rubydoc.info/gems/attribute-filters/file/docs/USAGE.md#Predefined_filters).
|
117
141
|
|
118
|
-
If you would rather like to group filters by attribute names
|
119
|
-
the alternative syntax may be helpful:
|
142
|
+
If you would rather like to group filters by attribute names while
|
143
|
+
registering them then the alternative syntax may be helpful:
|
120
144
|
|
121
145
|
```ruby
|
122
146
|
class User < ActiveRecord::Base
|
@@ -126,20 +150,10 @@ class User < ActiveRecord::Base
|
|
126
150
|
end
|
127
151
|
```
|
128
152
|
|
129
|
-
Attributes that should be altered may be simply added
|
130
|
-
to the attribute sets that you define and then filtered
|
131
|
-
with generic methods. You can use these methods in all
|
132
|
-
your models if you wish.
|
133
|
-
|
134
|
-
The last action can be performed by putting the filtering methods into
|
135
|
-
some base class that all your models inherit form or (better) into your own
|
136
|
-
handy module that is included in all your models. Alternatively you can
|
137
|
-
use predefined filters from `ActiveModel::AttributeFilters::Common` module.
|
138
|
-
|
139
153
|
Usage and more examples
|
140
154
|
-----------------------
|
141
155
|
|
142
|
-
You can use
|
156
|
+
You can use Attribute Filters to filter attributes (as presented above) but you can also
|
143
157
|
use it to express some logic
|
144
158
|
[on your own](http://rubydoc.info/gems/attribute-filters/file/docs/USAGE.md#Custom_applications).
|
145
159
|
|
data/attribute-filters.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "attribute-filters"
|
5
|
-
s.version = "1.
|
5
|
+
s.version = "1.4.0.20120824020319"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Pawe\u{142} Wilk"]
|
9
9
|
s.cert_chain = ["/Users/siefca/.gem/gem-public_cert.pem"]
|
10
|
-
s.date = "2012-08-
|
10
|
+
s.date = "2012-08-24"
|
11
11
|
s.description = "Concise way of filtering model attributes in Rails."
|
12
12
|
s.email = ["pw@gnu.org"]
|
13
13
|
s.extra_rdoc_files = ["Manifest.txt"]
|
14
|
-
s.files = [".rspec", ".yardopts", "ChangeLog", "Gemfile", "Gemfile.lock", "LGPL-LICENSE", "Manifest.txt", "README.md", "Rakefile", "attribute-filters.gemspec", "docs/COPYING", "docs/HISTORY", "docs/LEGAL", "docs/LGPL-LICENSE", "docs/TODO", "docs/USAGE.md", "docs/rdoc.css", "docs/yard-tpl/default/fulldoc/html/css/common.css", "init.rb", "lib/attribute-filters.rb", "lib/attribute-filters/active_model_insert.rb", "lib/attribute-filters/attribute_set.rb", "lib/attribute-filters/attribute_set_attrquery.rb", "lib/attribute-filters/attribute_set_enum.rb", "lib/attribute-filters/attribute_set_query.rb", "lib/attribute-filters/common_filters.rb", "lib/attribute-filters/dsl_filters.rb", "lib/attribute-filters/dsl_sets.rb", "lib/attribute-filters/helpers.rb", "lib/attribute-filters/railtie.rb", "lib/attribute-filters/version.rb", "spec/attribute-filters_spec.rb", "spec/spec_helper.rb", ".gemtest"]
|
14
|
+
s.files = [".rspec", ".yardopts", "ChangeLog", "Gemfile", "Gemfile.lock", "LGPL-LICENSE", "Manifest.txt", "README.md", "Rakefile", "attribute-filters.gemspec", "docs/COPYING", "docs/HISTORY", "docs/LEGAL", "docs/LGPL-LICENSE", "docs/TODO", "docs/USAGE.md", "docs/rdoc.css", "docs/yard-tpl/default/fulldoc/html/css/common.css", "init.rb", "lib/attribute-filters.rb", "lib/attribute-filters/active_model_insert.rb", "lib/attribute-filters/attribute_set.rb", "lib/attribute-filters/attribute_set_annotations.rb", "lib/attribute-filters/attribute_set_attrquery.rb", "lib/attribute-filters/attribute_set_enum.rb", "lib/attribute-filters/attribute_set_query.rb", "lib/attribute-filters/backports.rb", "lib/attribute-filters/common_filters.rb", "lib/attribute-filters/common_filters/case.rb", "lib/attribute-filters/common_filters/join.rb", "lib/attribute-filters/common_filters/split.rb", "lib/attribute-filters/common_filters/squeeze.rb", "lib/attribute-filters/common_filters/strip.rb", "lib/attribute-filters/dsl_attr_virtual.rb", "lib/attribute-filters/dsl_filters.rb", "lib/attribute-filters/dsl_sets.rb", "lib/attribute-filters/helpers.rb", "lib/attribute-filters/railtie.rb", "lib/attribute-filters/version.rb", "spec/attribute-filters_spec.rb", "spec/spec_helper.rb", ".gemtest"]
|
15
15
|
s.homepage = "https://rubygems.org/gems/attribute-filters/"
|
16
16
|
s.rdoc_options = ["--title", "Attribute::Filters Documentation", "--quiet"]
|
17
17
|
s.require_paths = ["lib"]
|
data/docs/HISTORY
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
=== 1.4.0 / 2012-08-24
|
2
|
+
|
3
|
+
* major enhancements
|
4
|
+
|
5
|
+
* Added annotations support
|
6
|
+
* Added Split and Join common filters
|
7
|
+
* Improved virtual attributes handling (added attr_virtual keyword)
|
8
|
+
* All common filters can now handle arrays and hashes
|
9
|
+
* API change: additional yeld params changed for DSL instance methods:
|
10
|
+
* `for_each_attr_from_set`
|
11
|
+
* `filter_attrs_from_set`
|
12
|
+
* `operate_on_attrs_from_set`
|
13
|
+
* API change: class methods return copies of sets, not originals
|
14
|
+
|
15
|
+
* minor enhancements
|
16
|
+
|
17
|
+
* Added `values` and `values_hash` methods to `AttributeSet::Query` for reading values
|
18
|
+
* Added `value` method to `AttributeSet::AttrQuery` for reading value
|
19
|
+
* Added `each` iterator to custom enumerators
|
20
|
+
* Added `attribute_set_simple` instance DSL method for bypassing proxies
|
21
|
+
* Added `filter_attributes` instance DSL method for running all predefined filters
|
22
|
+
* Added calls forwarding in proxy classes for `instance_eval` and `instance_exec` methods
|
23
|
+
* `AttributeSetEnumerable` is now `AttributeSet::Enumerable`
|
24
|
+
* `AttirbuteSetEnumerator` is now `AttributeSet::Enumerator`
|
25
|
+
* Modified filtering methods so they return specialized enumerator when called whithout a block
|
26
|
+
* `for_each_attr_from_set`
|
27
|
+
* `filter_attrs_from_set`
|
28
|
+
|
29
|
+
* major bugfixes
|
30
|
+
|
31
|
+
* In `AttributeSet::Query`: separated instance level data from class level by adding `dup` where needed
|
32
|
+
* In `AttributeSet`: separated instance level data from class level by adding `dup` where needed
|
33
|
+
|
34
|
+
* minor bugfixes
|
35
|
+
|
36
|
+
* In `AttributeFilters::Common`: `Squish` submodule included in `Common` module
|
37
|
+
* In `AttributeSet::Query`: replaced calls to `send` by calls to `public_send`
|
38
|
+
* In `AttributeSet::Query`: replaced calls to `method` by calls to `public_method`
|
39
|
+
* In `AttributeSet::AttrQuery`: replaced call to `method` by call to `public_method`
|
40
|
+
* In `AttributeFilters::operate_on_attrs_from_set`: replaced calls to `send` by calls to `public_send`
|
41
|
+
|
1
42
|
=== 1.3.2 / 2012-08-05
|
2
43
|
|
3
44
|
* minor bugfixes
|
@@ -8,8 +49,8 @@
|
|
8
49
|
|
9
50
|
* major enhancements
|
10
51
|
|
11
|
-
* Added accessible
|
12
|
-
* Added all_accessible_attributes
|
52
|
+
* Added `accessible?`, `inaccessible?`, `protected?` attribute checks
|
53
|
+
* Added `all_accessible_attributes`, `all_protected_attributes`, `all_inaccessible_attributes`
|
13
54
|
|
14
55
|
* minor enhancements
|
15
56
|
|
@@ -19,17 +60,12 @@
|
|
19
60
|
|
20
61
|
* major bugfixes
|
21
62
|
|
22
|
-
* In AttributeSet::Query proxy
|
63
|
+
* In `AttributeSet::Query`: proxy now uses `send` to get attribute values
|
23
64
|
|
24
65
|
* major enhancements
|
25
66
|
|
26
|
-
* Added valid
|
27
|
-
* Added all_attributes instance method
|
28
|
-
|
29
|
-
* minor enhancements
|
30
|
-
|
31
|
-
* Added is_a?() overrides in proxy classes
|
32
|
-
* Method attributes_to_filter now also takes attribute set as an argument
|
67
|
+
* Added `valid?` and `invalid?` DSL instance methods
|
68
|
+
* Added `all_attributes` instance method
|
33
69
|
|
34
70
|
=== 1.2.2 / 2012-08-03
|
35
71
|
|
@@ -39,8 +75,8 @@
|
|
39
75
|
|
40
76
|
* minor enhancements
|
41
77
|
|
42
|
-
* Added is_a
|
43
|
-
* Method attributes_to_filter now
|
78
|
+
* Added `is_a?` overrides to proxy classes
|
79
|
+
* Method `attributes_to_filter` can now take attribute set as an argument
|
44
80
|
|
45
81
|
=== 1.2.1 / 2012-07-09
|
46
82
|
|
@@ -52,43 +88,43 @@
|
|
52
88
|
|
53
89
|
* major bugfixes
|
54
90
|
|
55
|
-
* In AttributeSet::Query
|
91
|
+
* In `AttributeSet::Query`: removed typo that caused incorrect processing
|
56
92
|
|
57
93
|
* minor bugfixes
|
58
94
|
|
59
|
-
* Added missing respond_to
|
95
|
+
* Added missing `respond_to?` definitions to proxy classes
|
60
96
|
|
61
97
|
* major enhancements
|
62
98
|
|
63
|
-
* Added enumerators (AttributeSetEnumerable and AttributeSetEnumerator)
|
99
|
+
* Added enumerators (`AttributeSetEnumerable` and `AttributeSetEnumerator`)
|
64
100
|
* Added predefined filtering methods
|
65
101
|
* Added virtual attributes support in filters
|
66
102
|
* Written the usage instructions
|
67
103
|
|
68
104
|
* minor enhancements
|
69
105
|
|
70
|
-
* Removed the show DSL method from AttrQuery proxy class
|
106
|
+
* Removed the show DSL method from `AttrQuery` proxy class
|
71
107
|
* Added checking of model methods that are needed for a proper operation
|
72
|
-
* Proxy class Query is now viral and tries to wrap the results in its own instances
|
73
|
-
* Added
|
74
|
-
* Added from_attributes_that as
|
108
|
+
* Proxy class `AttributeSet::Query` is now viral and tries to wrap the results in its own instances
|
109
|
+
* Added `none` and `one` presence selectors to `AttributeSet::Query` class
|
110
|
+
* Added `from_attributes_that` as an alias for the `attribute_set` instance method
|
75
111
|
|
76
112
|
=== 1.1.2 / 2012-06-30
|
77
113
|
|
78
114
|
* major bugfixes
|
79
115
|
|
80
|
-
* In operate_on_attrs_from_set
|
81
|
-
* In attributes_to_filter
|
116
|
+
* In `operate_on_attrs_from_set`: replaced `self[attr]` and `method(attr)` calls with `send` (to be ORM agnostic)
|
117
|
+
* In `attributes_to_filter`: inefficient `respond_to?` calls replaced by the attributes method call
|
82
118
|
|
83
119
|
* major enhancements
|
84
120
|
|
85
|
-
* AttributeFilters module can be now used without full Rails stack, just with Active Model loaded
|
121
|
+
* `AttributeFilters` module can be now used without full Rails stack, just with Active Model loaded
|
86
122
|
|
87
123
|
* minor enhancements
|
88
124
|
|
89
125
|
* Documentation updated
|
90
|
-
* Added attribute-filters/helpers.rb containing AttributeFiltersHelpers module
|
91
|
-
* Flags parsing method attr_filter_process_flags moved to AttributeFiltersHelpers as process_flags
|
126
|
+
* Added `attribute-filters/helpers.rb` containing `AttributeFiltersHelpers` module
|
127
|
+
* Flags parsing method `attr_filter_process_flags` moved to `AttributeFiltersHelpers` as `process_flags`
|
92
128
|
* Added SuperModel and ActiveRecord dependencies for testing purposes
|
93
129
|
* Added first RSpec example
|
94
130
|
|
@@ -96,12 +132,12 @@
|
|
96
132
|
|
97
133
|
* major enhancement
|
98
134
|
|
99
|
-
* API changed; call_attrs_from_set name changed to for_each_attr_from_set
|
135
|
+
* API changed; `call_attrs_from_set` name changed to `for_each_attr_from_set`, caching removed
|
100
136
|
|
101
137
|
* minor enhancements
|
102
138
|
|
103
139
|
* Optimized code for attributes filtering
|
104
|
-
* Namespace organized (AttributeFilters completely
|
140
|
+
* Namespace organized (`AttributeFilters` moved completely under `ActiveModel` namespace)
|
105
141
|
* Prepared for testing with RSpec
|
106
142
|
* Added custom CSS file for YARD formatter
|
107
143
|
|
@@ -110,7 +146,7 @@
|
|
110
146
|
* minor enhancements
|
111
147
|
|
112
148
|
* Documentation updated
|
113
|
-
* Extended arguments parsing for DSL class method attribute_set
|
149
|
+
* Extended arguments parsing for DSL class method `attribute_set`
|
114
150
|
|
115
151
|
=== 1.0.1 / 2012-06-28
|
116
152
|
|
data/docs/TODO
CHANGED
@@ -1,5 +1,20 @@
|
|
1
|
-
* add more rspec examples
|
1
|
+
* add more rspec examples (+ nil and boolean attributes)
|
2
2
|
|
3
|
-
*
|
3
|
+
* add parameter support to squeeze common filter
|
4
4
|
|
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
|
5
7
|
|
8
|
+
* add stringify to common filters
|
9
|
+
* add numerize to common filters
|
10
|
+
* add booleanize to common filters
|
11
|
+
* add nullify to common filters
|
12
|
+
* add clear to common filters
|
13
|
+
* add reverse to common filters
|
14
|
+
* add sanitize to common filters (that calls needed)
|
15
|
+
-- sanitize_email
|
16
|
+
-- sanitize_url
|
17
|
+
-- sanitize_string
|
18
|
+
-- sanitize_name
|
19
|
+
-- sanitize_country
|
20
|
+
-- sanitize_iban (future, in a gem attribute-filters-common-iban)
|
data/docs/USAGE.md
CHANGED
@@ -16,10 +16,14 @@ on all attributes that are listed in a set.
|
|
16
16
|
|
17
17
|
Attribute sets are instances of
|
18
18
|
[`ActiveModel::AttributeSet`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeSet)
|
19
|
-
class.
|
20
|
-
|
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.
|
22
|
+
|
23
|
+
The attribute sets assigned to models are stored as [class instance variable](http://blog.codegram.com/2011/4/understanding-class-instance-variables-in-ruby).
|
24
|
+
**You cannot and should not interact with that storage directly**
|
21
25
|
but by using dedicated class methods that are available in your models.
|
22
|
-
These methods will allow you to read or write some data from/to attribute sets.
|
26
|
+
These methods will allow you to read or write some data from/to global attribute sets.
|
23
27
|
|
24
28
|
Attribute Filters are using `AttributeSet` instances
|
25
29
|
not just to store internal data but also to interact
|
@@ -33,15 +37,26 @@ Note that when sets are returned the convention is that:
|
|
33
37
|
* **attribute names are strings**
|
34
38
|
* **set names are symbols**
|
35
39
|
|
40
|
+
Also note that once you create a set that is bound to your model you cannot
|
41
|
+
remove elements from it and any query returning its contents will give you
|
42
|
+
a copy. That's because **model-bound attribute sets should be considered
|
43
|
+
a part of the interface**.
|
44
|
+
|
36
45
|
### Defining the attribute sets ###
|
37
46
|
|
38
47
|
First thing that should be done when using the Attribute Filters
|
39
48
|
is defining the sets of attributes in models.
|
40
49
|
|
50
|
+
You can also create local sets (using `ActiveModel::AttributeSet.new`)
|
51
|
+
or a local sets with extra syntactic sugar (using `ActiveModel::AttributeSet.new`)
|
52
|
+
but in real-life scenarios you should first create some model-bound sets that
|
53
|
+
can be later used by filters and by your own methods.
|
54
|
+
|
41
55
|
#### `attribute_set(set_name => attr_names)` ####
|
42
56
|
|
43
57
|
The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_set)
|
44
|
-
(a.k.a `attributes_that`) class method allows to **create or update a set
|
58
|
+
(a.k.a `attributes_that`) class method allows to **create or update a set** that will be
|
59
|
+
tied to a model.
|
45
60
|
|
46
61
|
Example:
|
47
62
|
|
@@ -106,15 +121,15 @@ end
|
|
106
121
|
|
107
122
|
### Querying sets in models ###
|
108
123
|
|
109
|
-
When your attribute sets are defined
|
110
|
-
of class methods to query them
|
124
|
+
When your "global" attribute sets are defined, you can use couple
|
125
|
+
of class methods to query them.
|
111
126
|
|
112
127
|
#### `attribute_set(set_name)` ####
|
113
128
|
|
114
129
|
The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_set)
|
115
130
|
(a.k.a `attributes_that`) class method called with a single argument **returns the attribute set
|
116
|
-
of the given name**. It will always return
|
117
|
-
(in that case the resulting set will be empty).
|
131
|
+
of the given name**. It will always return the instance of `AttributeSet` class, even if there
|
132
|
+
is no set registered under the given name (in that case the resulting set will be empty).
|
118
133
|
|
119
134
|
Example:
|
120
135
|
|
@@ -123,6 +138,8 @@ Example:
|
|
123
138
|
# => #<ActiveModel::AttributeSet: {"real_name"}>
|
124
139
|
```
|
125
140
|
|
141
|
+
Note that the returned set will be a copy of the original set stored within your model.
|
142
|
+
|
126
143
|
Instead of `attribute_set` you may also use one of the aliases:
|
127
144
|
|
128
145
|
* `attributes_that`, `attributes_that_are`, `are_attributes_that_are`, `from_attributes_that_are`,
|
@@ -152,6 +169,8 @@ Instead of `attribute_sets` you may also use one of the aliases:
|
|
152
169
|
|
153
170
|
* `attributes_sets`, `properties_sets`
|
154
171
|
|
172
|
+
Note that the returned sets will be copies of the original sets stored within your model.
|
173
|
+
|
155
174
|
#### `attribute_set` ####
|
156
175
|
|
157
176
|
The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods:attribute_set)
|
@@ -207,7 +226,7 @@ is a wrapper that calls `attributes_to_sets` which returns
|
|
207
226
|
a hash containing **all filtered attributes and arrays of sets**
|
208
227
|
that the attributes belong to.
|
209
228
|
|
210
|
-
### Querying sets in objects ###
|
229
|
+
### Querying sets in model objects ###
|
211
230
|
|
212
231
|
It is possible to access attribute sets from within the ActiveModel (or ORM, like ActiveRecord) objects.
|
213
232
|
To do that you may use instance methods that are designed for that purpose.
|
@@ -215,7 +234,7 @@ To do that you may use instance methods that are designed for that purpose.
|
|
215
234
|
#### `attribute_set` ####
|
216
235
|
|
217
236
|
The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:attribute_set)
|
218
|
-
method called withount an argument **returns the attribute set containing all attributes
|
237
|
+
instance method called withount an argument **returns the attribute set object containing all attributes known in a current object**.
|
219
238
|
|
220
239
|
Example:
|
221
240
|
|
@@ -229,9 +248,9 @@ It works the same as the `all_attributes` method.
|
|
229
248
|
#### `attribute_set(set_name)` ####
|
230
249
|
|
231
250
|
The [`attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:attribute_set)
|
232
|
-
(a.k.a `attributes_that`) method called with a single argument **returns the attribute set
|
251
|
+
(a.k.a `attributes_that`) instance method called with a single argument **returns a copy of the attribute set
|
233
252
|
of the given name**. It won't return the exact set object but a duplicate.
|
234
|
-
It will always return an `AttributeSet` instance, even if there is
|
253
|
+
It will always return an `AttributeSet` instance, even if there is no set of the given name defined for a model
|
235
254
|
(in that case the resulting set will be empty).
|
236
255
|
|
237
256
|
Example:
|
@@ -241,6 +260,24 @@ Example:
|
|
241
260
|
# => #<ActiveModel::AttributeSet: {"real_name", "username", "email"}>
|
242
261
|
```
|
243
262
|
|
263
|
+
The returned object in transparently wrapped in a proxy class instance that allows you
|
264
|
+
to apply additional methods and keywords to it. See the section
|
265
|
+
['Syntactic sugar for queries'](http://rubydoc.info/gems/attribute-filters/file/docs/USAGE.md#Syntactic_sugar_for_queries)
|
266
|
+
for more info.
|
267
|
+
|
268
|
+
There are also variants of this method that differ in a kind of taken argument:
|
269
|
+
|
270
|
+
* `attribute_set(set_object)`
|
271
|
+
|
272
|
+
> Allows to wrap an existing attribute set instance (e.g. created locally) with transparent proxy instance.
|
273
|
+
> The resulting object will look like the same set but will be decorated with additional syntactic sugar.
|
274
|
+
|
275
|
+
* `attribute_set(any_object)`
|
276
|
+
|
277
|
+
> Allows to create local set that will be initialized with the given object (usually an array) that may not
|
278
|
+
> be a `String`, a `Symbol` or an `AttributeSet` (these are reserved for the variants above). The resulting
|
279
|
+
> object (a new `AttributeSet` instance) is also wrapped in a proxy.
|
280
|
+
|
244
281
|
Instead of `attribute_set` you may also use one of the aliases:
|
245
282
|
|
246
283
|
* `attributes_that_are`, `from_attributes_that`, `are_attributes_that_are`, `from_attributes_that_are`,
|
@@ -252,7 +289,8 @@ Instead of `attribute_set` you may also use one of the aliases:
|
|
252
289
|
|
253
290
|
The [`attribute_sets`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters:attribute_sets)
|
254
291
|
method returns a hash containing **all the defined attribute sets** indexed by their names.
|
255
|
-
It won't return the exact internal hash but a duplicate
|
292
|
+
It won't return the exact internal hash but a duplicate and every set within it will also be a duplicate
|
293
|
+
of the original one.
|
256
294
|
|
257
295
|
Example:
|
258
296
|
|
@@ -382,6 +420,7 @@ Example:
|
|
382
420
|
|
383
421
|
Note that the returned hash will have a default value set to instance of the `AttributeSet`.
|
384
422
|
It won't return the exact internal hash but a duplicate.
|
423
|
+
|
385
424
|
If you'll try to get the value of an element that doesn't exist **the empty set will be returned**.
|
386
425
|
|
387
426
|
Instead of `attributes_to_sets` you may also use one of the aliases:
|
@@ -423,8 +462,9 @@ practice there are two groups of methods with two sets of DSL features.
|
|
423
462
|
|
424
463
|
First group (querying attribute sets):
|
425
464
|
|
426
|
-
* [`attribute_set`](#
|
427
|
-
* [`
|
465
|
+
* [`attribute_set`](#attribute_set0) and aliases
|
466
|
+
* [`attribute_set(set_name)`](#attribute_set_set_name_1) and aliases
|
467
|
+
* [`attributes_to_filter`](#attributes_to_filter_set_____)
|
428
468
|
|
429
469
|
Second group (querying attributes for sets they belong to):
|
430
470
|
|
@@ -435,8 +475,9 @@ Second group (querying attributes for sets they belong to):
|
|
435
475
|
Querying attribute sets uses two instance methods available
|
436
476
|
in your models:
|
437
477
|
|
438
|
-
* [`attribute_set`](#
|
439
|
-
* [`
|
478
|
+
* [`attribute_set`](#attribute_set0) and aliases
|
479
|
+
* [`attribute_set(set_name)`](#attribute_set_set_name_1) and aliases
|
480
|
+
* [`attributes_to_filter`](#attributes_to_filter_set_____)
|
440
481
|
|
441
482
|
The output of calling these methods is an `AttributeSet` instance
|
442
483
|
wrapped within a transparent proxy class instance
|
@@ -489,7 +530,7 @@ Let's do it without any fancy DSL methods:
|
|
489
530
|
```ruby
|
490
531
|
u = User.first
|
491
532
|
u.attributes_that(:should_be_stripped).all? do |attribute_name|
|
492
|
-
u.
|
533
|
+
u.public_send(attribute_name).present?
|
493
534
|
end
|
494
535
|
# => false
|
495
536
|
```
|
@@ -745,9 +786,9 @@ for altering attribute values: `for_attributes_that` and
|
|
745
786
|
|
746
787
|
Filtering attributes basically means calling a proper
|
747
788
|
method that will collect the attributes (matching certain
|
748
|
-
criteria and belonging to the given set) and
|
749
|
-
|
750
|
-
|
789
|
+
criteria and belonging to the given set) and calling some code block
|
790
|
+
that will either produce new values or just call some method on each
|
791
|
+
of current attribute value and name.
|
751
792
|
|
752
793
|
#### `filter_attrs_from_set(set_name,...)` ####
|
753
794
|
|
@@ -756,6 +797,14 @@ The [`filter_attrs_from_set`](http://rubydoc.info/gems/attribute-filters/ActiveM
|
|
756
797
|
It takes optional arguments and a block. The result of evaluating a block will become a new value for a processed
|
757
798
|
attribute. Optional arguments will be passed as the last arguments of a block.
|
758
799
|
|
800
|
+
The evaluated block can make use of the following arguments that are passed to it:
|
801
|
+
|
802
|
+
* `attribute_value` [Object] - current attribute value that should be altered
|
803
|
+
* `attribute_name` [String] - a name of currently processed attribute
|
804
|
+
* `set_object` [Object] - currently processed set that attribute belongs to
|
805
|
+
* `set_name` [Symbol] - a name of the processed attribute set
|
806
|
+
* `args` [Array] - an optional arguments passed to the method
|
807
|
+
|
759
808
|
By default only existing, changed and non-blank attributes are processed.
|
760
809
|
You can change that behavior by adding a flags as the first arguments:
|
761
810
|
|
@@ -772,7 +821,7 @@ Example:
|
|
772
821
|
before_validation :lolize
|
773
822
|
|
774
823
|
def lolize
|
775
|
-
filter_attributes_that(:should_be_lolized, "lol") do |attribute_value,
|
824
|
+
filter_attributes_that(:should_be_lolized, "lol") do |attribute_value, attribute_name, set_object, set_name, *args|
|
776
825
|
[attribute_value, set_name.to_s, attribute_name, *args.flatten].join('-')
|
777
826
|
end
|
778
827
|
end
|
@@ -785,6 +834,12 @@ Example:
|
|
785
834
|
# => "john-should_be_lolized-username-lol"
|
786
835
|
```
|
787
836
|
|
837
|
+
You can pass a set object (`AttributeSet` instance) instead of set name as an argument. The method
|
838
|
+
will then work on that local data with one difference: the `set_name` passed to a block will be set to `nil`.
|
839
|
+
|
840
|
+
If the block is not given then the method returns an enumerator which can be used to query method later
|
841
|
+
(when the block is present).
|
842
|
+
|
788
843
|
Instead of `filter_attrs_from_set` you may also use one of the aliases:
|
789
844
|
|
790
845
|
* `attribute_filter_for_set`, `filter_attributes_which`,
|
@@ -799,6 +854,14 @@ It takes optional arguments and a block. The result of evaluating the given bloc
|
|
799
854
|
but you can interact with the attribute object from within a block.
|
800
855
|
Optional arguments will be passed as the last arguments of a block.
|
801
856
|
|
857
|
+
The evaluated block can make use of the following arguments that are passed to it:
|
858
|
+
|
859
|
+
* `attribute_value` [Object] - current attribute value that should be altered
|
860
|
+
* `attribute_name` [String] - a name of currently processed attribute
|
861
|
+
* `set_object` [Object] - currently processed set that attribute belongs to
|
862
|
+
* `set_name` [Symbol] - a name of the processed attribute set
|
863
|
+
* `args` [Array] - an optional arguments passed to the method
|
864
|
+
|
802
865
|
By default only existing, changed and non-blank attributes are processed.
|
803
866
|
You can change that behavior by adding a flags as the first arguments:
|
804
867
|
|
@@ -815,7 +878,7 @@ Example:
|
|
815
878
|
before_validation :lolize
|
816
879
|
|
817
880
|
def lolize
|
818
|
-
for_attributes_that(:should_be_lolized, :process_blank, "lol") do |
|
881
|
+
for_attributes_that(:should_be_lolized, :process_blank, "lol") do |attribute_value, attribute_name, set_object, set_name, *args|
|
819
882
|
attribute_object << '-' << [set_name.to_s, attribute_name, *args.flatten].join('-')
|
820
883
|
end
|
821
884
|
end
|
@@ -828,6 +891,12 @@ Example:
|
|
828
891
|
# => "john-should_be_lolized-username-lol"
|
829
892
|
```
|
830
893
|
|
894
|
+
You can pass a set object (`AttributeSet` instance) instead of set name as an argument. The method
|
895
|
+
will then work on that local data with one difference: the `set_name` passed to a block will be set to `nil`.
|
896
|
+
|
897
|
+
If the block is not given then the method returns an enumerator which can be used to query method later
|
898
|
+
(when the block is present).
|
899
|
+
|
831
900
|
Instead of `filter_attrs_from_set` you may also use one of the aliases:
|
832
901
|
|
833
902
|
* `attribute_call_for_set`, `call_attrs_from_set`,
|
@@ -867,107 +936,1121 @@ if the filtering operation occured.
|
|
867
936
|
|
868
937
|
There are cases that virtual attributes (the ones that does not really exist in a database)
|
869
938
|
are to be filtered. By default it won't happen since all the filtering methods assume
|
870
|
-
the presence of any processed attribute as
|
939
|
+
the presence of any processed attribute as 'a real' attribute. There are three ways
|
871
940
|
to overcome that problem.
|
872
941
|
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
942
|
+
#### Unconditional filtering ####
|
943
|
+
|
944
|
+
The first way is to pass the `:no_presence_check` and `:process_all` flags to the filtering method.
|
945
|
+
That causes filter to be executed for each attribute from a set without any conditions (no checking
|
946
|
+
for presence of setter/getter method and no checking for changes).
|
947
|
+
|
948
|
+
Be aware that by doing that you take full responsibility for attribute set names added to your set.
|
949
|
+
If you put a name of nonexistent attribute then you may get ugly error later.
|
950
|
+
|
951
|
+
With that approach you can filter virtual attributes that are inaccessible to controllers
|
952
|
+
and don't show up when model is queried for known attributes. Using it may also cause some filters
|
953
|
+
to be executed more often than needed, since the changes tracking is disabled (`process_all`).
|
954
|
+
|
955
|
+
Example:
|
956
|
+
|
957
|
+
```ruby
|
958
|
+
class User
|
959
|
+
|
960
|
+
# declare a virtual attribute
|
961
|
+
attr_accessor :real_name
|
962
|
+
|
963
|
+
# define a set
|
964
|
+
attributes_that :should_be_splitted => [ :real_name ]
|
965
|
+
|
966
|
+
# register a callback method
|
967
|
+
before_validation :split_attributes
|
968
|
+
|
969
|
+
# create a filtering method
|
970
|
+
def split_attributes
|
971
|
+
for_attributes_that(:should_be_splitted, :no_presence_check, :process_all) do |value|
|
972
|
+
names = value.split(' ')
|
973
|
+
self.first_name = names[0] # assuming first_name exists in a database
|
974
|
+
self.last_name = names[1] # assuming last_name exists in a database
|
975
|
+
end
|
976
|
+
end
|
977
|
+
|
978
|
+
end
|
979
|
+
```
|
980
|
+
|
981
|
+
#### Marking as semi-real ####
|
982
|
+
|
983
|
+
The second way is to use `treat_attributes_as_real` (or simply `treat_as_real`) clause in your model
|
984
|
+
(available from version 1.2.0 of Attribute Filters). That approach may be applied to attributes
|
985
|
+
that aren't (may not or should not be) tracked for changes and aren't (may not or should not be) accessible
|
986
|
+
(to a controller) nor marked as protected.
|
987
|
+
|
988
|
+
There are two main differences from the unconditional filtering. First is that marking attribute
|
989
|
+
as real causes it to be added to the list of all known attributes that is returned by `all_attributes_set`
|
990
|
+
(a.k.a `all_attributes`). The second is that nothing ugly will happen if there will be non-existent
|
991
|
+
attributes in a set when filtering method kicks in. That's because the filtering method doesn't require
|
992
|
+
`:no_presence_check` flag to pick up such attributes. Attributes that could not be handled are simply ignored.
|
993
|
+
|
994
|
+
The `:process_all` flag is also not needed since all virtual attributes marked as real are by default
|
995
|
+
not checked for changes.
|
996
|
+
|
997
|
+
Example:
|
998
|
+
|
999
|
+
```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
|
1007
|
+
|
1008
|
+
# define a set
|
1009
|
+
attributes_that :should_be_splitted => [ :real_name ]
|
1010
|
+
|
1011
|
+
# register a callback method
|
1012
|
+
before_validation :split_attributes
|
1013
|
+
|
1014
|
+
# create a filtering method
|
1015
|
+
def split_attributes
|
1016
|
+
for_attributes_that(:should_be_splitted) do |value|
|
1017
|
+
names = value.split(' ')
|
1018
|
+
self.first_name = names[0] # assuming first_name exists in a database
|
1019
|
+
self.last_name = names[1] # assuming last_name exists in a database
|
1020
|
+
end
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
end
|
1024
|
+
```
|
1025
|
+
|
1026
|
+
Be aware that the virtual attributes declared that way will always be filtered
|
1027
|
+
since there is no way to know whether they have changed or not.
|
1028
|
+
That may lead to strange results under some circumstances, e.g. in case of
|
1029
|
+
cutting some part of a string stored in a virtual attribute by using a filter
|
1030
|
+
registered with `before_save`; if such operation will be performed
|
1031
|
+
more than once then the filtering will be performed more than once too.
|
1032
|
+
|
1033
|
+
The presence of virtual attributes is tested by checking if both, a setter and a getter,
|
1034
|
+
methods exist, unless the `no_presence_check` flag is passed to a filtering method.
|
1035
|
+
If both accessors don't exist then the attribute is not processed.
|
1036
|
+
|
1037
|
+
This approach can be easily used with predefined filters.
|
1038
|
+
|
1039
|
+
#### Marking as trackable ####
|
1040
|
+
|
1041
|
+
Since version 1.4.0 of Attribute Filters the **recommended way of dealing with
|
1042
|
+
virtual attributes** is to make use of changes tracking available in Active Model.
|
1043
|
+
To do that look at your ORM's documentation and see
|
1044
|
+
[`ActiveModel::Dirty`](http://api.rubyonrails.org/classes/ActiveModel/Dirty.html)
|
1045
|
+
and a method `attribute_will_change` that it contains. Just call that method from within
|
1046
|
+
your setter and you're done.
|
1047
|
+
|
1048
|
+
This approach can be easily used with predefined filters.
|
1049
|
+
|
1050
|
+
Conditions:
|
1051
|
+
|
1052
|
+
* Use `attr_accessible` or `attr_protected` to mark the attribute as known
|
1053
|
+
* Use your own setter for notifying that attribute value has changed or `attr_virtual`
|
1054
|
+
|
1055
|
+
The benefit of that approach is that a filter will never be called redundantly contrary
|
1056
|
+
to previous methods.
|
1057
|
+
|
1058
|
+
Example:
|
1059
|
+
|
1060
|
+
```ruby
|
1061
|
+
class User < ActiveRecord::Base
|
1062
|
+
|
1063
|
+
# declare a virtual attribute
|
1064
|
+
attr_reader :real_name
|
1065
|
+
attr_accessible :real_name
|
1066
|
+
|
1067
|
+
# define a set
|
1068
|
+
attributes_that :should_be_splitted => [ :real_name ]
|
1069
|
+
|
1070
|
+
# 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
|
1078
|
+
|
1079
|
+
# create a filtering method
|
1080
|
+
def split_attributes
|
1081
|
+
for_attributes_that(:should_be_splitted) do |value|
|
1082
|
+
names = value.split(' ')
|
1083
|
+
self.first_name = names[0] # assuming first_name exists in a database
|
1084
|
+
self.last_name = names[1] # assuming last_name exists in a database
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
end
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
You can also use built-in DSL keyword **`attr_virtual`** that will create setter and getter
|
1092
|
+
for you:
|
1093
|
+
|
1094
|
+
```ruby
|
1095
|
+
class User < ActiveRecord::Base
|
1096
|
+
|
1097
|
+
# declare a virtual attribute
|
1098
|
+
attr_virtual :real_name
|
1099
|
+
attr_accessible :real_name
|
1100
|
+
|
1101
|
+
# define a set
|
1102
|
+
attributes_that :should_be_splitted => [ :real_name ]
|
1103
|
+
|
1104
|
+
# register a callback method
|
1105
|
+
before_validation :split_attributes
|
1106
|
+
|
1107
|
+
# create a filtering method
|
1108
|
+
def split_attributes
|
1109
|
+
for_attributes_that(:should_be_splitted) do |value|
|
1110
|
+
names = value.split(' ')
|
1111
|
+
self.first_name = names[0]
|
1112
|
+
self.last_name = names[1]
|
1113
|
+
end
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
end
|
1117
|
+
```
|
1118
|
+
|
1119
|
+
#### Marking as trackable and semi-real ####
|
1120
|
+
|
1121
|
+
That's a variant of the recommended way of dealing with virtual attributes. It may be useful
|
1122
|
+
if you don't want to (or cannot) add virtual attributes to access lists using `attr_accessible`
|
1123
|
+
or `attr_protected`.
|
1124
|
+
|
1125
|
+
Example:
|
1126
|
+
|
1127
|
+
```ruby
|
1128
|
+
class User < ActiveRecord::Base
|
1129
|
+
|
1130
|
+
# declare a virtual attribute
|
1131
|
+
attr_virtual :real_name
|
1132
|
+
|
1133
|
+
# mark the attribute as real
|
1134
|
+
treat_as_real :real_name
|
1135
|
+
|
1136
|
+
# tell the engine that all virtual attributes
|
1137
|
+
# are tracked for changes and it should pick from changed
|
1138
|
+
# not from all
|
1139
|
+
virtual_attributes_are_tracked
|
1140
|
+
|
1141
|
+
# define a set
|
1142
|
+
attributes_that :should_be_splitted => [ :real_name ]
|
1143
|
+
|
1144
|
+
# register a callback method
|
1145
|
+
before_validation :split_attributes
|
1146
|
+
|
1147
|
+
# create a filtering method
|
1148
|
+
def split_attributes
|
1149
|
+
for_attributes_that(:should_be_splitted) do |value|
|
1150
|
+
names = value.split(' ')
|
1151
|
+
self.first_name = names[0]
|
1152
|
+
self.last_name = names[1]
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
end
|
1157
|
+
```
|
1158
|
+
|
1159
|
+
Annotations
|
1160
|
+
-----------
|
1161
|
+
|
1162
|
+
Annotations are portions of data that you can bind to attribute names residing within attribute sets.
|
1163
|
+
What for? To store something that is related to the specific attribute and that should be memorized within
|
1164
|
+
a set and/or its copies (if any). You can annotate each attribute name using key -> value pairs where the key is always a symbol and value is any kind of object you want. Only existing attributes can be annotated and deleting attribute
|
1165
|
+
will remove annotations that are assigned to it.
|
1166
|
+
|
1167
|
+
When you copy a set, create difference or intersection of attribute sets, any existing annotations are also copied.
|
1168
|
+
If the operation creates a sum or joins sets then the annotations are mixed too.
|
1169
|
+
|
1170
|
+
The annotations are used by some of the predefined filters described later to precise operations that have
|
1171
|
+
to be taken (e.g. specifying separator string for attribute joining filter and so on).
|
1172
|
+
|
1173
|
+
### Creating annotations ###
|
1174
|
+
|
1175
|
+
You can create annotations during defining a set or you can add them later with `annotate_attribute_set`.
|
1176
|
+
In case of local sets you can also use a method called `annotate`.
|
1177
|
+
|
1178
|
+
#### When defining sets ####
|
1179
|
+
|
1180
|
+
When using the first method just replace attrbute name with a hash, where attribute name is a key
|
1181
|
+
and annotations are another hash containing keys and values.
|
1182
|
+
|
1183
|
+
Example:
|
1184
|
+
|
1185
|
+
```ruby
|
1186
|
+
class User
|
1187
|
+
# Set name: cool
|
1188
|
+
# Attribute name: email
|
1189
|
+
# Annotation key: some_key
|
1190
|
+
# Annotation value: some value
|
1191
|
+
|
1192
|
+
attributes_that_are cool: { :email => { :some_key => "some value" } }
|
1193
|
+
end
|
1194
|
+
```
|
1195
|
+
|
1196
|
+
The above will annotate `email` attribute within a set `should_be_something` with `some_key` => "some value" pair.
|
1197
|
+
You can mix annotated attributes with unannotated; just put the last ones in front of an array:
|
878
1198
|
|
879
|
-
|
880
|
-
|
881
|
-
|
1199
|
+
```ruby
|
1200
|
+
class User
|
1201
|
+
attributes_that_are cool: [ :some_unannotated, { :email => { :some_key => "some value" } } ]
|
1202
|
+
end
|
1203
|
+
```
|
1204
|
+
|
1205
|
+
or
|
1206
|
+
|
1207
|
+
```ruby
|
1208
|
+
class User
|
1209
|
+
attributes_that_are :cool => [ :some_unannotated, { :email => { :some_key => "some value" } } ]
|
1210
|
+
end
|
1211
|
+
```
|
1212
|
+
|
1213
|
+
#### After defining sets ####
|
1214
|
+
|
1215
|
+
To create annotations for class-level sets use the [`annotate_attribute_set`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/ClassMethods.html#annotate_attribute_set-instance_method) method (an its aliases):
|
1216
|
+
|
1217
|
+
* **`annotate_attributes_that_are`**
|
1218
|
+
* **`annotate_attributes_that`**
|
1219
|
+
* **`annotate_attributes_are`**
|
1220
|
+
* **`annotate_attributes_for`**
|
1221
|
+
* **`annotate_attributes_set`**
|
1222
|
+
* **`annotate_properties_that`**
|
1223
|
+
* **`annotate_attributes`**
|
1224
|
+
|
1225
|
+
Example:
|
1226
|
+
|
1227
|
+
```ruby
|
1228
|
+
class User
|
1229
|
+
attributes_that_are cool: [ :some_unannotated, :email, :username ]
|
1230
|
+
annotate_attributes_that_are cool: [ :email, :some_key, "some value" ]
|
1231
|
+
annotate_attributes_that_are :cool => { :username => { :some_key => "some value", :other_k => 'other v' } }
|
1232
|
+
end
|
1233
|
+
```
|
1234
|
+
|
1235
|
+
Caution: Annotating attributes that aren't present in a set
|
1236
|
+
with `annotate_attribute_set` will raise an error.
|
1237
|
+
|
1238
|
+
#### Annotating local sets ####
|
1239
|
+
|
1240
|
+
* **`annotate`**
|
1241
|
+
|
1242
|
+
Example:
|
1243
|
+
|
1244
|
+
```ruby
|
1245
|
+
class User
|
1246
|
+
def some_method
|
1247
|
+
s = ActiveModel::AttributeSet.new([:first_element, :second_element])
|
1248
|
+
s.annotate(:first_element, :key, 'value')
|
1249
|
+
end
|
1250
|
+
end
|
1251
|
+
```
|
1252
|
+
|
1253
|
+
### Removing annotations ###
|
882
1254
|
|
883
|
-
|
884
|
-
(available from version 1.2.0 of Attribute Filters). That's **the preferred one**.
|
1255
|
+
To remove annotations locally (from sets that are not directly bound to models) you can use:
|
885
1256
|
|
886
|
-
|
1257
|
+
* **`delete_annotation(attribute_name, annotation_key)`** - to delete specified annotation key for the given attribute
|
1258
|
+
* **`delete_annotations(attribute_name)`** - to delete all annotations for an attribute of the given name
|
1259
|
+
* **`remove_annotations()`** - to remove all annotations from a set
|
1260
|
+
|
1261
|
+
Be aware that using these method to delete annotations from class-level sets won't work.
|
1262
|
+
That's because you'll always get a copy when querying these sets. However there are methods that
|
1263
|
+
will work in a model:
|
1264
|
+
|
1265
|
+
* **`delete_annotations_from_set(set_name, attribute, *annotation_keys)`** - to delete annotation keys for the given attribute
|
1266
|
+
* **`delete_annotations_from_set(set_name, attribute)`** - to delete all annotation keys for the given attribute
|
1267
|
+
* **`delete_annotations_from_set(set_name => *attributes)`** - to delete all annotation keys for the given attribute
|
1268
|
+
* **`delete_annotations_from_set(set_name => { attribute => keys})`** - to delete specified annotation keys for the given attributes
|
1269
|
+
|
1270
|
+
Example:
|
1271
|
+
|
1272
|
+
```ruby
|
1273
|
+
class User
|
1274
|
+
attributes_that_are cool: [ :some_unannotated, :email ]
|
1275
|
+
|
1276
|
+
# That will work
|
1277
|
+
delete_annotations_from_set cool: :email
|
1278
|
+
delete_annotations_from_set cool: [ :email, :key_one ]
|
1279
|
+
delete_annotation_from_set cool: { :email => [:key_one, :other_key], :name => :some_key }
|
1280
|
+
|
1281
|
+
# That won't affect the global set called 'cool'
|
1282
|
+
# since we have its copy here, not the original.
|
1283
|
+
def some_method
|
1284
|
+
attributes_that_are(:cool).delete_annotation(:email)
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
# That won't affect the global set called 'cool'
|
1288
|
+
# since we have its copy here, not the original.
|
1289
|
+
attributes_that_are(:cool).delete_annotation(:email)
|
1290
|
+
end
|
1291
|
+
```
|
1292
|
+
|
1293
|
+
### Updating annotations ###
|
1294
|
+
|
1295
|
+
Calling `annotate` method again on a set or redefining set at a class-level allows to add annotations
|
1296
|
+
or modify their keys.
|
1297
|
+
|
1298
|
+
Example:
|
887
1299
|
|
888
1300
|
```ruby
|
889
1301
|
class User
|
890
|
-
|
1302
|
+
attributes_that_are :cool => { :email => { :some_key => "some value" } }
|
1303
|
+
attributes_that_are cool: { :email => { :other_key => "other_value" } }
|
1304
|
+
attributes_that_are cool: { :email => { :some_key => "another_value" } }
|
1305
|
+
annotate_attributes_that_are :cool, :email, :some_key => "x"
|
1306
|
+
delete_annotation_from_set :cool => { :email => :other_key }
|
891
1307
|
end
|
1308
|
+
|
1309
|
+
# In the result there will be only one annotation key left;
|
1310
|
+
# :some_key with the value of "x"
|
892
1311
|
```
|
893
1312
|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
1313
|
+
Caution: Annotating attributes that aren't present in a set
|
1314
|
+
with `annotate_attribute_set` or by using `annotate` method will raise an error.
|
1315
|
+
|
1316
|
+
Be aware that updating annotations directly (using `annotate` method) won't work on sets
|
1317
|
+
defined directly in classes describing models. That's because you'll always get
|
1318
|
+
a copy when querying these sets.
|
898
1319
|
|
899
1320
|
```ruby
|
900
1321
|
class User
|
901
|
-
|
902
|
-
|
1322
|
+
attributes_that_are :cool => { :email => { :some_key => "some value" } }
|
1323
|
+
|
1324
|
+
# Calling `some_method` won't work on 'cool' global set.
|
1325
|
+
def some_method
|
1326
|
+
attributes_that_are(:cool).delete_annotation(:email, :other_key)
|
1327
|
+
end
|
903
1328
|
end
|
904
1329
|
```
|
905
1330
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
1331
|
+
## Querying annotations ###
|
1332
|
+
|
1333
|
+
To check if a set has any annotations you can use one of the methods:
|
1334
|
+
|
1335
|
+
* **`has_annotation?`** - checks if a set has any annotations
|
1336
|
+
* **`has_annotations?`** - checks if a set has any annotations
|
1337
|
+
* **`has_annotation?(attribute_name)`** - checks if a set has any annotations for the given attribute
|
1338
|
+
* **`has_annotation?(attribute_name, *annotation_keys)`** - checks if a set has any annotation key for the given attribute
|
1339
|
+
|
1340
|
+
To read annotations you can use :
|
1341
|
+
|
1342
|
+
* **`annotation(attribute_name)`** - gets a hash of annotations or returns nil
|
1343
|
+
* **`annotation(attribute_name, *keys)`** - gets an array annotation values for the given keys (puts nils if key is missing) or returns nil
|
1344
|
+
|
1345
|
+
Example:
|
1346
|
+
|
1347
|
+
```ruby
|
1348
|
+
class User
|
1349
|
+
attributes_that_are cool: [ :some_unannotated, :email => { :x => :y } ]
|
1350
|
+
|
1351
|
+
def q
|
1352
|
+
attributes_that_are(:cool).annotation(:email, :x, :z)
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
def qq
|
1356
|
+
attributes_that_are(:cool).annotation(:nope, :x, :z)
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
# Calling q will return an array: [:y, nil]
|
1360
|
+
# Calling qq will return nil since attribute is not present (or not annotated)
|
1361
|
+
|
1362
|
+
end
|
1363
|
+
```
|
910
1364
|
|
911
1365
|
Predefined filters
|
912
1366
|
------------------
|
913
1367
|
|
914
1368
|
Predefined filters are ready-to-use methods
|
915
1369
|
for filtering attributes. You just have to call them
|
916
|
-
or
|
1370
|
+
or register them as [callbacks](http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html).
|
917
1371
|
|
918
|
-
To use predefined filters you have to manually
|
1372
|
+
To use all predefined filters you have to manually
|
919
1373
|
include the [`ActiveModel::AttributeFilters::Common`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common)
|
920
|
-
module.
|
921
|
-
|
922
|
-
Here is a list of the predefined filtering methods:
|
923
|
-
|
924
|
-
* `capitalize_attributes` (submodule: `Capitalize`)
|
925
|
-
* `fully_capitalize_attributes` (submodule: `Capitalize`)
|
926
|
-
* `titleize_attributes` (submodule: `Titleize`)
|
927
|
-
* `downcase_attributes` (submodule: `Downcase` or `Case`)
|
928
|
-
* `upcase_attributes` (submodule: `Upcase` or `Case`)
|
929
|
-
* `strip_attributes` (submodule: `Strip`)
|
930
|
-
* `squeeze_attributes` (submodule: `Squeeze`)
|
931
|
-
* `squish_attributes` (submodule: `Squish`)
|
1374
|
+
module. That will include **all available filtering methods** into your model.
|
932
1375
|
|
933
1376
|
Example:
|
934
1377
|
|
935
1378
|
```ruby
|
936
1379
|
class User < ActiveRecord::Base
|
937
1380
|
include ActiveModel::AttributeFilters::Common
|
938
|
-
|
939
|
-
the_attribute
|
940
|
-
|
941
|
-
the_attribute name: [:should_be_squished, :should_be_downcased, :should_be_titleized ]
|
942
|
-
|
943
|
-
before_validation :squish_attributes
|
1381
|
+
|
1382
|
+
the_attribute name: [:should_be_downcased, :should_be_titleized ]
|
1383
|
+
|
944
1384
|
before_validation :downcase_attributes
|
945
1385
|
before_validation :titleize_attributes
|
946
1386
|
end
|
947
1387
|
```
|
948
1388
|
|
949
|
-
|
1389
|
+
If you don't want to include portions of code that you'll never use, you can include some filters selectively. To do that include just a submodule containing certain filtering method:
|
950
1390
|
|
951
1391
|
```ruby
|
952
1392
|
class User < ActiveRecord::Base
|
953
|
-
include ActiveModel::AttributeFilters::Common::Squish
|
954
1393
|
include ActiveModel::AttributeFilters::Common::Downcase
|
955
1394
|
include ActiveModel::AttributeFilters::Common::Titleize
|
956
|
-
|
957
|
-
the_attribute
|
958
|
-
|
959
|
-
the_attribute name: [:should_be_squished, :should_be_downcased, :should_be_titleized ]
|
960
|
-
|
961
|
-
before_validation :squished_attributes
|
1395
|
+
|
1396
|
+
the_attribute name: [:should_be_downcased, :should_be_titleized ]
|
1397
|
+
|
962
1398
|
before_validation :downcase_attributes
|
963
1399
|
before_validation :titleize_attributes
|
964
1400
|
end
|
965
1401
|
```
|
966
1402
|
|
1403
|
+
As you can see, to use any filter you should include a proper submodule, add attribute
|
1404
|
+
names to a proper set and register a callback. The name of a set
|
1405
|
+
that a filtering method will use is predetermined and follows the
|
1406
|
+
convention that it should correspond to the name of a filter. So the
|
1407
|
+
set used by squeezing filter will be named `should_be_squeezed`.
|
1408
|
+
|
1409
|
+
For example, to squeeze attributes `name` and `email` you can write:
|
1410
|
+
|
1411
|
+
```ruby
|
1412
|
+
class User < ActiveRecord::Base
|
1413
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1414
|
+
attributes_that should_be_sqeezed: [:email, :name]
|
1415
|
+
before_validation :squeeze_attributes
|
1416
|
+
end
|
1417
|
+
```
|
1418
|
+
|
1419
|
+
The filtering methods usually come with class-level DSL methods
|
1420
|
+
that are a simple wrappers calling `the_attribute`. So you can
|
1421
|
+
also write:
|
1422
|
+
|
1423
|
+
```ruby
|
1424
|
+
class User < ActiveRecord::Base
|
1425
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1426
|
+
squeeze_attributes :email, :name
|
1427
|
+
before_validation :squeeze_attributes
|
1428
|
+
end
|
1429
|
+
```
|
1430
|
+
|
1431
|
+
### Calling all at once ###
|
1432
|
+
|
1433
|
+
There is a special method called
|
1434
|
+
[`filter_attributes`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters.html#filter_attributes-instance_method) that can be registered as a callback. It will call all possible (known) filtering methods
|
1435
|
+
in a predetermined order.
|
1436
|
+
|
1437
|
+
Example:
|
1438
|
+
|
1439
|
+
```ruby
|
1440
|
+
class User < ActiveRecord::Base
|
1441
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1442
|
+
include ActiveModel::AttributeFilters::Common::Capitalize
|
1443
|
+
|
1444
|
+
squeeze_attributes :email, :name
|
1445
|
+
capitalize_attributes :name
|
1446
|
+
|
1447
|
+
before_validation :filter_attributes
|
1448
|
+
end
|
1449
|
+
```
|
1450
|
+
|
1451
|
+
Use this method if you're really lazy.
|
1452
|
+
You can also create your own method like that and call all needed filters there:
|
1453
|
+
|
1454
|
+
```ruby
|
1455
|
+
class User < ActiveRecord::Base
|
1456
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1457
|
+
include ActiveModel::AttributeFilters::Common::Capitalize
|
1458
|
+
|
1459
|
+
squeeze_attributes :email, :name
|
1460
|
+
capitalize_attributes :name
|
1461
|
+
|
1462
|
+
before_validation :my_total_filtering_method
|
1463
|
+
|
1464
|
+
def my_total_filtering_method
|
1465
|
+
squeeze_attributes
|
1466
|
+
capitalize_attributes
|
1467
|
+
end
|
1468
|
+
end
|
1469
|
+
```
|
1470
|
+
|
1471
|
+
But to increase readability you should go with the old-fashion way and register
|
1472
|
+
each filtering callback method separately.
|
1473
|
+
|
1474
|
+
### Data types ###
|
1475
|
+
|
1476
|
+
The common filters are aware and can operate on attributes that
|
1477
|
+
are arrays or hashes. If an array or a hash is detected then
|
1478
|
+
the filtering is made **recursively** for each element (or for each value in case
|
1479
|
+
of a hash) and the produced structure is returned. If the attribute has an
|
1480
|
+
unknown type then its value is not altered at all and left intact.
|
1481
|
+
|
1482
|
+
Some of the common filters may treat arrays and hashes in a slight different
|
1483
|
+
way (e.g. joining and splitting filters do that).
|
1484
|
+
|
1485
|
+
The common filters are aware of multibyte strings so string
|
1486
|
+
operations should handle diacritics properly.
|
1487
|
+
|
1488
|
+
### List of filters ###
|
1489
|
+
|
1490
|
+
* **`capitalize_attributes`**
|
1491
|
+
* **`fully_capitalize_attributes`**
|
1492
|
+
* **`titleize_attributes`**
|
1493
|
+
* **`downcase_attributes`**
|
1494
|
+
* **`upcase_attributes`**
|
1495
|
+
* **`strip_attributes`**
|
1496
|
+
* **`squeeze_attributes`**
|
1497
|
+
* **`squish_attributes`**
|
1498
|
+
|
967
1499
|
See the
|
968
1500
|
[`ActiveModel::AttributeFilters::Common`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common)
|
969
1501
|
for detailed descriptions.
|
970
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
|
1609
|
+
|
1610
|
+
```ruby
|
1611
|
+
class User < ActiveRecord::Base
|
1612
|
+
include ActiveModel::AttributeFilters::Common::Case
|
1613
|
+
|
1614
|
+
attributes_that :should_be_titleized => [ :name ]
|
1615
|
+
before_validation :titleize_attributes
|
1616
|
+
end
|
1617
|
+
```
|
1618
|
+
|
1619
|
+
Then:
|
1620
|
+
|
1621
|
+
> `"some name"`
|
1622
|
+
|
1623
|
+
will become:
|
1624
|
+
|
1625
|
+
> `"Some Name"`
|
1626
|
+
|
1627
|
+
##### `upcase_attributes` #####
|
1628
|
+
|
1629
|
+
Upcases attributes.
|
1630
|
+
|
1631
|
+
* Callback method: `upcase_attributes`
|
1632
|
+
* Class-level helper: `upcase_attributes(*attribute_names)`
|
1633
|
+
* Uses set: `:should_be_upcased`
|
1634
|
+
* Operates on: strings, arrays of strings, hashes of strings (as values)
|
1635
|
+
* Uses annotations: no
|
1636
|
+
|
1637
|
+
Example:
|
1638
|
+
|
1639
|
+
```ruby
|
1640
|
+
class User < ActiveRecord::Base
|
1641
|
+
include ActiveModel::AttributeFilters::Common::Case
|
1642
|
+
|
1643
|
+
upcase_attributes :name
|
1644
|
+
before_validation :upcase_attributes
|
1645
|
+
end
|
1646
|
+
```
|
1647
|
+
|
1648
|
+
or
|
1649
|
+
|
1650
|
+
```ruby
|
1651
|
+
class User < ActiveRecord::Base
|
1652
|
+
include ActiveModel::AttributeFilters::Common::Case
|
1653
|
+
|
1654
|
+
attributes_that :should_be_upcased => [ :name ]
|
1655
|
+
before_validation :upcase_attributes
|
1656
|
+
end
|
1657
|
+
```
|
1658
|
+
|
1659
|
+
Then:
|
1660
|
+
|
1661
|
+
> `"some name"`
|
1662
|
+
|
1663
|
+
will become:
|
1664
|
+
|
1665
|
+
> `"SOME NAME"`
|
1666
|
+
|
1667
|
+
##### `downcase_attributes` #####
|
1668
|
+
|
1669
|
+
Downcases attributes.
|
1670
|
+
|
1671
|
+
* Callback method: `downcase_attributes`
|
1672
|
+
* Class-level helper: `downcase_attributes(*attribute_names)`
|
1673
|
+
* Uses set: `:should_be_downcased`
|
1674
|
+
* Operates on: strings, arrays of strings, hashes of strings (as values)
|
1675
|
+
* Uses annotations: no
|
1676
|
+
|
1677
|
+
Example:
|
1678
|
+
|
1679
|
+
```ruby
|
1680
|
+
class User < ActiveRecord::Base
|
1681
|
+
include ActiveModel::AttributeFilters::Common::Case
|
1682
|
+
|
1683
|
+
downcase_attributes :name
|
1684
|
+
before_validation :downcase_attributes
|
1685
|
+
end
|
1686
|
+
```
|
1687
|
+
|
1688
|
+
or
|
1689
|
+
|
1690
|
+
```ruby
|
1691
|
+
class User < ActiveRecord::Base
|
1692
|
+
include ActiveModel::AttributeFilters::Common::Case
|
1693
|
+
|
1694
|
+
attributes_that :should_be_downcased => [ :name ]
|
1695
|
+
before_validation :downcase_attributes
|
1696
|
+
end
|
1697
|
+
```
|
1698
|
+
|
1699
|
+
Then:
|
1700
|
+
|
1701
|
+
> `"SOME NAME"`
|
1702
|
+
|
1703
|
+
will become:
|
1704
|
+
|
1705
|
+
> `"some name"`
|
1706
|
+
|
1707
|
+
#### Strip ####
|
1708
|
+
|
1709
|
+
* Submodule: [`Strip`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Strip.html)
|
1710
|
+
|
1711
|
+
##### `strip_attributes` #####
|
1712
|
+
|
1713
|
+
Strips attributes of leading and trailing spaces.
|
1714
|
+
|
1715
|
+
* Callback method: `strip_attributes`
|
1716
|
+
* Class-level helper: `strip_attributes(*attribute_names)`
|
1717
|
+
* Uses set: `:should_be_stripped`
|
1718
|
+
* Operates on: strings, arrays of strings, hashes of strings (as values)
|
1719
|
+
* Uses annotations: no
|
1720
|
+
|
1721
|
+
Example:
|
1722
|
+
|
1723
|
+
```ruby
|
1724
|
+
class User < ActiveRecord::Base
|
1725
|
+
include ActiveModel::AttributeFilters::Common::Strip
|
1726
|
+
|
1727
|
+
strip_attributes :name
|
1728
|
+
before_validation :strip_attributes
|
1729
|
+
end
|
1730
|
+
```
|
1731
|
+
|
1732
|
+
or
|
1733
|
+
|
1734
|
+
```ruby
|
1735
|
+
class User < ActiveRecord::Base
|
1736
|
+
include ActiveModel::AttributeFilters::Common::Strip
|
1737
|
+
|
1738
|
+
attributes_that :should_be_stripped => [ :name ]
|
1739
|
+
before_validation :strip_attributes
|
1740
|
+
end
|
1741
|
+
```
|
1742
|
+
|
1743
|
+
Then:
|
1744
|
+
|
1745
|
+
> `" Some Name "`
|
1746
|
+
|
1747
|
+
will become:
|
1748
|
+
|
1749
|
+
> `"Some Name"`
|
1750
|
+
|
1751
|
+
#### Squeeze ####
|
1752
|
+
|
1753
|
+
* Submodule: [`Squeeze`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Squeeze.html)
|
1754
|
+
|
1755
|
+
##### `squeeze_attributes` #####
|
1756
|
+
|
1757
|
+
Squeezes attributes (squeezes repeating spaces into one).
|
1758
|
+
|
1759
|
+
* Callback method: `squeeze_attributes`
|
1760
|
+
* Class-level helper: `squeeze_attributes(*attribute_names)`
|
1761
|
+
* Uses set: `:should_be_squeezed`
|
1762
|
+
* Operates on: strings, arrays of strings, hashes of strings (as values)
|
1763
|
+
* Uses annotations: no
|
1764
|
+
|
1765
|
+
Example:
|
1766
|
+
|
1767
|
+
```ruby
|
1768
|
+
class User < ActiveRecord::Base
|
1769
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1770
|
+
|
1771
|
+
squeeze_attributes :name
|
1772
|
+
before_validation :squeeze_attributes
|
1773
|
+
end
|
1774
|
+
```
|
1775
|
+
|
1776
|
+
or
|
1777
|
+
|
1778
|
+
```ruby
|
1779
|
+
class User < ActiveRecord::Base
|
1780
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1781
|
+
|
1782
|
+
attributes_that :should_be_squeezed => [ :name ]
|
1783
|
+
before_validation :squeeze_attributes
|
1784
|
+
end
|
1785
|
+
```
|
1786
|
+
|
1787
|
+
Then:
|
1788
|
+
|
1789
|
+
> `"Some Name"`
|
1790
|
+
|
1791
|
+
will become:
|
1792
|
+
|
1793
|
+
> `"Some Name"`
|
1794
|
+
|
1795
|
+
##### `squish_attributes` #####
|
1796
|
+
|
1797
|
+
Squishes attributes (removes all whitespace characters on both ends of the string, and then changes remaining consecutive whitespace groups into one space each).
|
1798
|
+
|
1799
|
+
* Callback method: `squish_attributes`
|
1800
|
+
* Class-level helper: `squish_attributes(*attribute_names)`
|
1801
|
+
* Uses set: `:should_be_squished`
|
1802
|
+
* Operates on: strings, arrays of strings, hashes of strings (as values)
|
1803
|
+
* Uses annotations: no
|
1804
|
+
|
1805
|
+
Example:
|
1806
|
+
|
1807
|
+
```ruby
|
1808
|
+
class User < ActiveRecord::Base
|
1809
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1810
|
+
|
1811
|
+
squish_attributes :name
|
1812
|
+
before_validation :squish_attributes
|
1813
|
+
end
|
1814
|
+
```
|
1815
|
+
|
1816
|
+
or
|
1817
|
+
|
1818
|
+
```ruby
|
1819
|
+
class User < ActiveRecord::Base
|
1820
|
+
include ActiveModel::AttributeFilters::Common::Squeeze
|
1821
|
+
|
1822
|
+
attributes_that :should_be_squished => [ :name ]
|
1823
|
+
before_validation :squish_attributes
|
1824
|
+
end
|
1825
|
+
```
|
1826
|
+
|
1827
|
+
Then:
|
1828
|
+
|
1829
|
+
> `" Some Name"`
|
1830
|
+
|
1831
|
+
will become:
|
1832
|
+
|
1833
|
+
> `"Some Name"`
|
1834
|
+
|
1835
|
+
#### Split ####
|
1836
|
+
|
1837
|
+
* Submodule: [`Split`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Split.html)
|
1838
|
+
|
1839
|
+
##### `split_attributes` #####
|
1840
|
+
|
1841
|
+
Splits attributes into arrays and puts the results into other attributes or into the same attributes.
|
1842
|
+
|
1843
|
+
* Callback methods: `split_attributes`
|
1844
|
+
* Class-level helpers:
|
1845
|
+
* `split_attributes(attribute_name, parameters_hash)`
|
1846
|
+
* `split_attributes(attribute_name)`
|
1847
|
+
* Uses set: `:should_be_splitted`
|
1848
|
+
* Operates on: strings, arrays of strings, hashes of strings (as values)
|
1849
|
+
* Uses annotations: yes
|
1850
|
+
* `split_pattern` - a pattern passed to [`split`](http://www.ruby-doc.org/core/String.html#method-i-split) method (optional)
|
1851
|
+
* `split_limit` - a limit passed to `split` method (optional)
|
1852
|
+
* `split_into` - attribute names used as destinations for parts
|
1853
|
+
* `split_flatten` - flag that causes resulting array to be flattened
|
1854
|
+
|
1855
|
+
If some source attribute is an array or a hash then the filter will recursively traverse it and
|
1856
|
+
operate on each element. The filter works the same way as the `split` method from the `String` class
|
1857
|
+
of Ruby's standard library. If the filter encounters an object which is not a string nor an array or a hash,
|
1858
|
+
it simply leaves it as is.
|
1859
|
+
|
1860
|
+
You can set `:pattern` (`:split_pattern`) and `:limit` (`:split_limit`) arguments passed to
|
1861
|
+
`split` method but note that **a limit is applied to each processed string separately**,
|
1862
|
+
not to the resulting array **(if the processed attribute is an array)**. For instance,
|
1863
|
+
if there is a string containing 3 words (`'A B C'`) and the limit is set to 2 then the last two words
|
1864
|
+
will be left intact and placed in a second element of the resulting array (`['A', 'B C']`).
|
1865
|
+
If the source is an array (`['A', 'B B B B', 'C']`) the result of this operation will be array of arrays
|
1866
|
+
(`[ ['A'], ['B B'], ['C'] ]`); as you can see the limit will be applied to its second element.
|
1867
|
+
|
1868
|
+
If there are no destination attributes defined (`:into` or `:split_into` option) then
|
1869
|
+
the resulting array will be written to a current attribute. If there are destination attributes
|
1870
|
+
given then the resulting array will be written into them (each subsequent element into each next attribute).
|
1871
|
+
The elements that don't fit in the collection are simply ignored.
|
1872
|
+
|
1873
|
+
There is also `flatten` (or `:split_flatten`) parameter that causes the resulting array to be
|
1874
|
+
flattened. Note that it doesn't change how the limits work; they still will be applied but to a single
|
1875
|
+
split results, not to the whole resulting array (in case of array of arrays).
|
1876
|
+
|
1877
|
+
Examples:
|
1878
|
+
|
1879
|
+
```ruby
|
1880
|
+
class User < ActiveRecord::Base
|
1881
|
+
# Including common filter for splitting
|
1882
|
+
include ActiveModel::AttributeFilters::Common::Split
|
1883
|
+
|
1884
|
+
# Registering virtual attribute
|
1885
|
+
attr_virtual :real_name
|
1886
|
+
attr_accessible :real_name
|
1887
|
+
|
1888
|
+
# Adding attribute name to :should_be_splitted set
|
1889
|
+
split_attributes :real_name
|
1890
|
+
|
1891
|
+
# Registering callback method
|
1892
|
+
# Warning: it will be executed each time model object is validated
|
1893
|
+
# (the nice thing is that it allows to validate the results, not the unsplitted data)
|
1894
|
+
before_validation :split_attributes
|
1895
|
+
end
|
1896
|
+
```
|
1897
|
+
|
1898
|
+
or without a `split_attributes` helper:
|
1899
|
+
|
1900
|
+
```ruby
|
1901
|
+
class User < ActiveRecord::Base
|
1902
|
+
# Including common filter for splitting
|
1903
|
+
include ActiveModel::AttributeFilters::Common::Split
|
1904
|
+
|
1905
|
+
# Registering virtual attribute
|
1906
|
+
attr_virtual :real_name
|
1907
|
+
attr_accessible :real_name
|
1908
|
+
|
1909
|
+
# Adding attribute name to :should_be_splitted set (by hand)
|
1910
|
+
attributes_that :should_be_splitted => :real_name
|
1911
|
+
|
1912
|
+
# Registering callback method
|
1913
|
+
before_validation :split_attributes
|
1914
|
+
end
|
1915
|
+
```
|
1916
|
+
|
1917
|
+
The result of executing the filter above will be replacement of a string by an array containing
|
1918
|
+
words (each one in a separate element). The `real_name` attribute is a virtual attribute in this example
|
1919
|
+
but it could be real attribute. The result will be written as an array into the same attribute since there
|
1920
|
+
are no destination attributes given. So `'Paul Wolf'` will become `['Paul', 'Wolf']`.
|
1921
|
+
|
1922
|
+
Using limit:
|
1923
|
+
|
1924
|
+
```ruby
|
1925
|
+
class User < ActiveRecord::Base
|
1926
|
+
include ActiveModel::AttributeFilters::Common::Split
|
1927
|
+
|
1928
|
+
attr_virtual :real_name
|
1929
|
+
attr_accessible :real_name
|
1930
|
+
split_attributes :real_name, :limit => 2
|
1931
|
+
before_validation :split_attributes
|
1932
|
+
end
|
1933
|
+
```
|
1934
|
+
|
1935
|
+
or without a `split_attributes` keyword:
|
1936
|
+
|
1937
|
+
```ruby
|
1938
|
+
class User < ActiveRecord::Base
|
1939
|
+
include ActiveModel::AttributeFilters::Common::Split
|
1940
|
+
|
1941
|
+
attr_virtual :real_name
|
1942
|
+
attr_accessible :real_name
|
1943
|
+
|
1944
|
+
attributes_that :should_be_splitted => { :real_name => { :split_limit => 2 } }
|
1945
|
+
before_validation :split_attributes
|
1946
|
+
end
|
1947
|
+
```
|
1948
|
+
|
1949
|
+
The result of the above example will be the same as the previous one with the difference that any
|
1950
|
+
reduntant elements will be left intact and placed as the last element of an array. So for data:
|
1951
|
+
|
1952
|
+
> `'Paul Thomas Wolf'`
|
1953
|
+
|
1954
|
+
the array will be:
|
1955
|
+
|
1956
|
+
> `[ 'Paul', 'Thomas Wolf' ]`
|
1957
|
+
|
1958
|
+
Another example, let's write results to some attributes:
|
1959
|
+
|
1960
|
+
```ruby
|
1961
|
+
class User < ActiveRecord::Base
|
1962
|
+
include ActiveModel::AttributeFilters::Common::Split
|
1963
|
+
|
1964
|
+
attr_virtual :real_name
|
1965
|
+
attr_accessible :real_name
|
1966
|
+
split_attributes :real_name, :limit => 2, :into => [ :first_name, :last_name ], :pattern => ' '
|
1967
|
+
before_validation :split_attributes
|
1968
|
+
end
|
1969
|
+
```
|
1970
|
+
|
1971
|
+
(The `:pattern` is given here but you may skip it if it's a space.)
|
1972
|
+
|
1973
|
+
This will split a value of the `real_name` attribute and place the results in the attributes
|
1974
|
+
called `first_name` and `last_name`, so for:
|
1975
|
+
|
1976
|
+
> `'Paul Thomas Wolf'`
|
1977
|
+
|
1978
|
+
the result will be:
|
1979
|
+
|
1980
|
+
```
|
1981
|
+
first_name: 'Paul'
|
1982
|
+
last_name: 'Thomas Wolf'
|
1983
|
+
```
|
1984
|
+
|
1985
|
+
If you remove the limit, then it will be quite different:
|
1986
|
+
|
1987
|
+
```
|
1988
|
+
first_name: 'Paul'
|
1989
|
+
last_name: 'Thomas'
|
1990
|
+
```
|
1991
|
+
|
1992
|
+
That's because there are more results than attributes they fit into. You just have to keep in mind
|
1993
|
+
that this filter behaves like the String's split method with the difference when the results are written
|
1994
|
+
into other attributes. In that case the limit causes redundant data to be placed in the last element (if a limit
|
1995
|
+
is lower or is the same as the count of destination attributes) and its lack causes some of the resulting data to
|
1996
|
+
be ignored (if there are more slices than receiving attributes).
|
1997
|
+
|
1998
|
+
The pattern parameter (`:pattern` when using `split_attributes` or `:split_pattern` when directly
|
1999
|
+
annotating attribute in a set) should be a string.
|
2000
|
+
|
2001
|
+
#### Join ####
|
2002
|
+
|
2003
|
+
* Submodule: [`Join`](http://rubydoc.info/gems/attribute-filters/ActiveModel/AttributeFilters/Common/Join.html)
|
2004
|
+
|
2005
|
+
##### `join_attributes` #####
|
2006
|
+
|
2007
|
+
Joins attributes and places the results into other attributes or into the same attributes as strings.
|
2008
|
+
|
2009
|
+
* Callback method: `join_attributes`
|
2010
|
+
* Class-level helpers:
|
2011
|
+
* `join_attributes(attribute_name, parameters_hash)` (a.k.a `joint_attribute`)
|
2012
|
+
* `join_attributes(attribute_name)` (a.k.a `joint_attribute`)
|
2013
|
+
* Uses set: `:should_be_joined`
|
2014
|
+
* Operates on: strings, arrays of strings
|
2015
|
+
* Uses annotations: yes
|
2016
|
+
* `join_separator` - a pattern passed to [`join`](http://www.ruby-doc.org/core/Array.html#method-i-join) method (optional)
|
2017
|
+
* `join_compact` - compact flag; if true then an array is compacted before it's joined (optional)
|
2018
|
+
* `join_from` - attribute names used as sources for joins
|
2019
|
+
|
2020
|
+
The join filter uses `join` instance method of the `Array` class to produce single string from multiple strings.
|
2021
|
+
These strings may be values of other attributes (source attributes), values of an array stored in an attribute
|
2022
|
+
or mix of it. If the `:compact` (`:join_compact` in case of manually annotating a set) parameter is given
|
2023
|
+
and it's not `false` nor `nil` then results are compacted during processing. That means any slices equals to `nil` are
|
2024
|
+
removed.
|
2025
|
+
|
2026
|
+
If the parameter `:from` (or annotation key `:join_from`) was not given then a currently processed attribute
|
2027
|
+
is treated as a source (it should be an array).
|
2028
|
+
|
2029
|
+
Examples:
|
2030
|
+
|
2031
|
+
```ruby
|
2032
|
+
class User < ActiveRecord::Base
|
2033
|
+
include ActiveModel::AttributeFilters::Common::Join
|
2034
|
+
|
2035
|
+
attr_virtual :first_name, :last_name
|
2036
|
+
attr_accessible :first_name, :last_name
|
2037
|
+
join_attributes_into :real_name, :from => [ :first_name, :last_name ]
|
2038
|
+
before_validation :join_attributes
|
2039
|
+
end
|
2040
|
+
```
|
2041
|
+
|
2042
|
+
you can also switch source with destination:
|
2043
|
+
|
2044
|
+
```ruby
|
2045
|
+
join_attributes [ :first_name, :last_name ] => :real_name
|
2046
|
+
```
|
2047
|
+
|
2048
|
+
or add a descriptive keyword `:into`:
|
2049
|
+
|
2050
|
+
```ruby
|
2051
|
+
join_attributes [ :first_name, :last_name ], :into => :real_name
|
2052
|
+
```
|
2053
|
+
|
971
2054
|
Custom applications
|
972
2055
|
-------------------
|
973
2056
|
|
@@ -984,11 +2067,13 @@ class User < ActiveRecord::Base
|
|
984
2067
|
attributes_that_are required_to_trade: [ :username, :email, :real_name, :address, :account ]
|
985
2068
|
|
986
2069
|
def is_able_to_trade?
|
987
|
-
are_attributes_that_are(:required_to_trade).all.present?
|
2070
|
+
are_attributes_that_are(:required_to_trade).all.present? and
|
2071
|
+
are_attributes_that_are(:required_to_trade).all.valid?
|
988
2072
|
end
|
989
2073
|
|
990
2074
|
def attributes_missing_to_trade
|
991
|
-
attributes_that_are(:required_to_trade).list.blank?
|
2075
|
+
attributes_that_are(:required_to_trade).list.blank? +
|
2076
|
+
attributes_that_are(:required_to_trade).list.invalid?
|
992
2077
|
end
|
993
2078
|
end
|
994
2079
|
```
|