attribute-filters 1.3.2 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
```
|