attribute-filters 2.0.0 → 2.0.1
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 +51 -0
- data/README.md +19 -0
- data/attribute-filters.gemspec +3 -3
- data/docs/HISTORY +16 -2
- data/docs/TODO +0 -6
- data/docs/USAGE.md +76 -6
- data/lib/attribute-filters/active_model_insert.rb +0 -1
- data/lib/attribute-filters/attribute_set.rb +162 -154
- data/lib/attribute-filters/common_filters.rb +7 -7
- data/lib/attribute-filters/dsl_attr_virtual.rb +49 -18
- data/lib/attribute-filters/dsl_sets.rb +12 -6
- data/lib/attribute-filters/meta_set.rb +2 -11
- data/lib/attribute-filters/version.rb +2 -2
- data/spec/attribute-filters_spec.rb +3 -2
- metadata +4 -4
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/ChangeLog
CHANGED
@@ -1,3 +1,54 @@
|
|
1
|
+
commit 364a5df0dfe8bed367351c55b7b64c174b2a3d91
|
2
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
3
|
+
Date: Sun Nov 4 16:44:10 2012 +0100
|
4
|
+
|
5
|
+
Version 2.0.1
|
6
|
+
|
7
|
+
commit ae4f36ca7f011045898f0d21a2a7c4fad3064446
|
8
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
9
|
+
Date: Sun Nov 4 16:17:03 2012 +0100
|
10
|
+
|
11
|
+
Tracking changes of virtual attributes improved; wrapping method is now waiting for a getter method
|
12
|
+
|
13
|
+
commit de71213689e9246e8694f65fc26b4dfb84e9e940
|
14
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
15
|
+
Date: Sun Nov 4 16:16:10 2012 +0100
|
16
|
+
|
17
|
+
Added AttributeSet#to_hash and AttributeSet#to_attribute_set
|
18
|
+
|
19
|
+
commit 732f6a0546bd2420f676d52dcc6be54fc190ea61
|
20
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
21
|
+
Date: Sun Nov 4 03:26:57 2012 +0100
|
22
|
+
|
23
|
+
Virtual attributes tracking improved; now the placement of attr_virtual keyword does not matter
|
24
|
+
|
25
|
+
commit d7c3b087fd0cdb66ad7a42e30ae55d2d896ff505
|
26
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
27
|
+
Date: Sun Nov 4 03:25:42 2012 +0100
|
28
|
+
|
29
|
+
Created abstraction layer that allows to keep all needed data in two class instance variables:
|
30
|
+
|
31
|
+
- @attribute_sets (for sets, attributes map, virtual and semi-real items),
|
32
|
+
- @attribute_filters (for filtering methods).
|
33
|
+
|
34
|
+
commit f1e63bc24ceb941e84e354f3f28ead899eaacc77
|
35
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
36
|
+
Date: Fri Nov 2 08:43:48 2012 +0100
|
37
|
+
|
38
|
+
MetaSet improved, removed grandparent.class.bind(self).call
|
39
|
+
|
40
|
+
commit b5a915f50d823d39657a91b1513e0ba4bd690490
|
41
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
42
|
+
Date: Thu Nov 1 20:57:25 2012 +0100
|
43
|
+
|
44
|
+
Description updated
|
45
|
+
|
46
|
+
commit bda2e1073095712fe1aece55dbf40ac5fb4f4bc9
|
47
|
+
Author: Paweł Wilk <siefca@gnu.org>
|
48
|
+
Date: Thu Nov 1 20:48:24 2012 +0100
|
49
|
+
|
50
|
+
Gem description updated
|
51
|
+
|
1
52
|
commit 9d5082a0f3d5b9349b1792a420f273b9898f1a9e
|
2
53
|
Author: Paweł Wilk <siefca@gnu.org>
|
3
54
|
Date: Thu Nov 1 17:07:24 2012 +0100
|
data/README.md
CHANGED
@@ -16,6 +16,11 @@ and some syntactic sugar to Rails, thereby allowing you
|
|
16
16
|
to express filtering and grouping model attributes
|
17
17
|
in a concise and clean way.
|
18
18
|
|
19
|
+
If you are a fan of declarative style, this gem is for you.
|
20
|
+
It lets you program your models in a way that it's clear
|
21
|
+
to see what's going on with attributes just by looking
|
22
|
+
at the top of their classes.
|
23
|
+
|
19
24
|
When?
|
20
25
|
-----
|
21
26
|
|
@@ -152,6 +157,20 @@ class User < ActiveRecord::Base
|
|
152
157
|
end
|
153
158
|
```
|
154
159
|
|
160
|
+
Tracking changes and filtering virtual attributes is also easy:
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class User < ActiveRecord::Base
|
164
|
+
include ActiveModel::AttributeFilters::Split
|
165
|
+
|
166
|
+
split_attribute :real_name => [ :first_name, :last_name ]
|
167
|
+
before_validation :filter_attributes
|
168
|
+
|
169
|
+
attr_virtual :real_name
|
170
|
+
attr_accessor :real_name
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
155
174
|
Usage and more examples
|
156
175
|
-----------------------
|
157
176
|
|
data/attribute-filters.gemspec
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "attribute-filters"
|
5
|
-
s.version = "2.0.
|
5
|
+
s.version = "2.0.1.20121104164328"
|
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-11-
|
10
|
+
s.date = "2012-11-04"
|
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"]
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.rubyforge_project = "attribute-filters"
|
19
19
|
s.rubygems_version = "1.8.24"
|
20
20
|
s.signing_key = "/Users/siefca/.gem/gem-private_key.pem"
|
21
|
-
s.summary = "Concise way of filtering model attributes in Rails"
|
21
|
+
s.summary = "Concise way of filtering and grouping model attributes in Rails"
|
22
22
|
|
23
23
|
if s.respond_to? :specification_version then
|
24
24
|
s.specification_version = 3
|
data/docs/HISTORY
CHANGED
@@ -1,11 +1,25 @@
|
|
1
|
+
=== 2.0.1 / 2012-10-04
|
2
|
+
|
3
|
+
* major enhancements
|
4
|
+
|
5
|
+
* Virtual attributes changes tracking improved; the `attr_virtual` DSL keyword can be placed anywhere
|
6
|
+
* Internal sets refactored, added abstraction layer that utilizes just two class instance variables:
|
7
|
+
* `@attribute_sets` (for keeping sets, attributes map, virtual and semi-real items)
|
8
|
+
* `@attribute_filters` (for keeping filtering methods)
|
9
|
+
|
10
|
+
* minor enhancements
|
11
|
+
|
12
|
+
* Added conversion methods: AttributeSet#to_hash, AttributeSet#to_attribute_set
|
13
|
+
* `MetaSet` improved, removed grandparent.class.bind(self).call
|
14
|
+
|
1
15
|
=== 2.0.0 / 2012-10-01
|
2
16
|
|
3
17
|
* major enhancements
|
4
18
|
|
5
19
|
* Operating speed increased by 100%
|
6
20
|
* API change:
|
7
|
-
* AttributeSet is now based on a Hash
|
8
|
-
* Added MetaSet for keeping sets of attribute sets
|
21
|
+
* `AttributeSet` is now based on a Hash
|
22
|
+
* Added `MetaSet` for keeping sets of attribute sets
|
9
23
|
* Added DSL keyword for registering filtering methods
|
10
24
|
* DSL method `filter_attributes` is using filtering methods registered before
|
11
25
|
* Creation of duplicates for sets moved to class-level accessors
|
data/docs/TODO
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
|
2
|
-
* attr_virtual - wrap method_defined? and add some gotchas into it that will wait for accessor to be added
|
3
|
-
there already is a hash for attr_virtual, all we need to do is to check any added accessor against that hash
|
4
|
-
that will only apply when there weren't accessors while using attr_virtual
|
5
|
-
|
6
|
-
|
7
1
|
* add sanitize to common filters
|
8
2
|
-- sanitize_url
|
9
3
|
-- sanitize_string
|
data/docs/USAGE.md
CHANGED
@@ -508,6 +508,45 @@ Example:
|
|
508
508
|
# => #<ActiveModel::AttributeSet: {"lol"}>
|
509
509
|
```
|
510
510
|
|
511
|
+
You can check which virtual attribute is automatically tracked for changes (was added using `attr_virtual`
|
512
|
+
and has wrapped setter method), which is still waiting for its setter and getter methods to be created,
|
513
|
+
and which was added using `treat_as_virtual` with the assumption that the changes tracking will be done
|
514
|
+
manually by the properly constructed setter.
|
515
|
+
|
516
|
+
Each element of a set containing virtual attributes has a control marker attached to it (instead of annotations) so
|
517
|
+
you can read this marker to know what is the status of a certain attribute.
|
518
|
+
|
519
|
+
To get a hash of attributes and statuses pairs you may use (in an instance method that belongs to a model):
|
520
|
+
|
521
|
+
```ruby
|
522
|
+
all_virtual_attributes.to_hash
|
523
|
+
```
|
524
|
+
|
525
|
+
or just print them:
|
526
|
+
|
527
|
+
```ruby
|
528
|
+
all_virtual_attributes.each_pair{ |k,v| puts "#{k}: #{v}" }
|
529
|
+
```
|
530
|
+
|
531
|
+
To read the status of a single attribute:
|
532
|
+
|
533
|
+
```ruby
|
534
|
+
all_virtual_attributes["attribute_name"]
|
535
|
+
```
|
536
|
+
|
537
|
+
To select the attributes that are meeting certain criteria:
|
538
|
+
|
539
|
+
```ruby
|
540
|
+
all_virtual_attributes.select{|k,v| v == :tracked}
|
541
|
+
end
|
542
|
+
```
|
543
|
+
|
544
|
+
The statuses are symbols and have the following meanings:
|
545
|
+
|
546
|
+
* `:no_wrap` – a virtual attribute was added using `treat_as_virtual` and changes tracking should be done manually
|
547
|
+
* `:tracked` – a setter method of an attribute was automatically wrapped to track changes
|
548
|
+
* `:waiting` – a virtual attribute is to be tracked when its setter method will be defined
|
549
|
+
|
511
550
|
Instead of `all_virtual_attributes` you may also use the alias:
|
512
551
|
|
513
552
|
* `virtual_attributes_set`
|
@@ -1339,8 +1378,8 @@ your setter and you're done.
|
|
1339
1378
|
This approach can be easily used with predefined filters. The benefit of it,
|
1340
1379
|
contrary to other methods, is that a filter will never be called redundantly.
|
1341
1380
|
|
1342
|
-
You should use the built-in DSL keyword **`attr_virtual`** that will
|
1343
|
-
|
1381
|
+
You should use the built-in DSL keyword **`attr_virtual`** that will enable changes
|
1382
|
+
tracking for your attribute.
|
1344
1383
|
|
1345
1384
|
```ruby
|
1346
1385
|
class User < ActiveRecord::Base
|
@@ -1370,9 +1409,10 @@ end
|
|
1370
1409
|
Note that for Rails version 3 you may need to declare attribute as accessible using `attr_accessible`
|
1371
1410
|
if you want controllers to be able to update its value through assignment passed to model.
|
1372
1411
|
|
1373
|
-
You
|
1374
|
-
|
1375
|
-
|
1412
|
+
You have to create setter and getter for virtual attributes on you own.
|
1413
|
+
It may be done before or after `attr_virtual` keyword.
|
1414
|
+
|
1415
|
+
Example:
|
1376
1416
|
|
1377
1417
|
```ruby
|
1378
1418
|
class User < ActiveRecord::Base
|
@@ -1393,6 +1433,9 @@ class User < ActiveRecord::Base
|
|
1393
1433
|
end
|
1394
1434
|
filtering_method :split_attributes, :should_be_splitted
|
1395
1435
|
|
1436
|
+
# inform AF that the model has a virtual attribute
|
1437
|
+
has_virtual_attribute :real_name
|
1438
|
+
|
1396
1439
|
# own setter
|
1397
1440
|
def real_name=(val)
|
1398
1441
|
# do somehing specific here (or not)
|
@@ -1405,7 +1448,34 @@ class User < ActiveRecord::Base
|
|
1405
1448
|
@real_name
|
1406
1449
|
end
|
1407
1450
|
|
1408
|
-
|
1451
|
+
end
|
1452
|
+
```
|
1453
|
+
|
1454
|
+
Instead of `attr_virtual` you may also use its alias:
|
1455
|
+
|
1456
|
+
* `has_virtual_attribute`
|
1457
|
+
|
1458
|
+
There are (rare) cases when you may wish to treat attributes as virtual but without automagically wrapping their setter
|
1459
|
+
methods to track changes (e.g. setter is already doing it manually). In such cases **make sure that the setter and getter exist** and use low-level method called `treat_as_virtual`, for instance:
|
1460
|
+
|
1461
|
+
```ruby
|
1462
|
+
class User < ActiveRecord::Base
|
1463
|
+
|
1464
|
+
splits_attributes :real_name
|
1465
|
+
before_validation :filter_attributes
|
1466
|
+
|
1467
|
+
# inform AF that the model has a virtual attribute
|
1468
|
+
# and changes tracking is already implemented
|
1469
|
+
treats_as_virtual :real_name
|
1470
|
+
|
1471
|
+
# own getter
|
1472
|
+
attr_reader :real_name
|
1473
|
+
|
1474
|
+
# own setter that implements changes tracking
|
1475
|
+
def real_name=(val)
|
1476
|
+
attribute_will_change!('real_name') if val != real_name
|
1477
|
+
@real_name = val
|
1478
|
+
end
|
1409
1479
|
|
1410
1480
|
end
|
1411
1481
|
```
|
@@ -29,7 +29,6 @@ module ActiveModel
|
|
29
29
|
if singleton_class.method_defined?(:included)
|
30
30
|
singleton_class.send(:alias_method, :included_without_attribute_methods, :included)
|
31
31
|
singleton_class.send(:alias_method, :included, :included_with_attribute_methods)
|
32
|
-
#singleton_class.send(:alias_method_chain, :included, :attribute_methods)
|
33
32
|
end
|
34
33
|
|
35
34
|
end # ActiveModel::AttributeMethods.class_eval
|
@@ -7,131 +7,186 @@
|
|
7
7
|
# This file contains ActiveModel::AttributeSet class
|
8
8
|
# used to interact with attribute sets.
|
9
9
|
|
10
|
+
require 'set'
|
11
|
+
|
10
12
|
# @abstract This namespace is shared with ActveModel.
|
11
13
|
module ActiveModel
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
include ActiveModel::AttributeSet::Enumerable
|
16
|
-
|
17
|
-
# Helpers module shortcut
|
18
|
-
AFHelpers = ActiveModel::AttributeFilters::AttributeFiltersHelpers
|
14
|
+
module AttributeFilters
|
15
|
+
module AttributeSetMethods
|
16
|
+
include ActiveModel::AttributeSet::Enumerable
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
# @return [AttributeSet] an AttributeSet object
|
23
|
-
def initialize(*args)
|
24
|
-
return super if args.count == 0
|
25
|
-
r = super()
|
26
|
-
add(args)
|
27
|
-
r
|
28
|
-
end
|
18
|
+
# Helpers module shortcut
|
19
|
+
AFHelpers = ActiveModel::AttributeFilters::AttributeFiltersHelpers
|
29
20
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
21
|
+
# Adds the given object to the set and returns self.
|
22
|
+
# If the object is already in the set, returns nil.
|
23
|
+
# If the object is an array it adds each element of the array.
|
24
|
+
# The array is not flattened so if it contains other arrays
|
25
|
+
# then they will be added as the arrays.
|
26
|
+
# When adding an array the returning value is also an array,
|
27
|
+
# which contains elements that were successfuly added to set
|
28
|
+
# and didn't existed there before.
|
29
|
+
#
|
30
|
+
# @param args [Array<Object,Hash,Array,Enumerable>] object(s) to be added to set
|
31
|
+
# @return [AttributeSet] current attribute set object
|
32
|
+
def add(*args)
|
33
|
+
args.flatten.each do |a|
|
34
|
+
if a.is_a?(Hash)
|
35
|
+
deep_merge!(a)
|
36
|
+
elsif a.is_a?(::Enumerable)
|
37
|
+
a.each { |e| self[e] = true unless e.blank? }
|
38
|
+
else
|
39
|
+
self[a] = true
|
40
|
+
end
|
49
41
|
end
|
42
|
+
self
|
50
43
|
end
|
51
|
-
|
52
|
-
end
|
53
|
-
alias_method :<<, :add
|
44
|
+
alias_method :<<, :add
|
54
45
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
46
|
+
# Returns an array of attribute names.
|
47
|
+
#
|
48
|
+
# @return [Array<String>]
|
49
|
+
def to_a
|
50
|
+
keys
|
51
|
+
end
|
59
52
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
53
|
+
# Returns a hash based on a set.
|
54
|
+
#
|
55
|
+
# @return [Hash]
|
56
|
+
def to_hash
|
57
|
+
Hash.new.deep_merge(self)
|
58
|
+
end
|
59
|
+
alias_method :to_h, :to_hash
|
64
60
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
61
|
+
# Returns self.
|
62
|
+
#
|
63
|
+
# @return [AttributeSet]
|
64
|
+
def to_attribute_set
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns a set containing attribute names.
|
69
|
+
#
|
70
|
+
# @return [Set<String>]
|
71
|
+
def to_set
|
72
|
+
keys.to_set
|
73
|
+
end
|
74
|
+
|
75
|
+
# Adds two sets by deeply merging their contents.
|
76
|
+
# If any value stored in one set under conflicting key
|
77
|
+
# is +true+, +false+ or +nil+ then value is taken from other set.
|
78
|
+
# If one of the conflicting values is a kind of Hash and
|
79
|
+
# the other is not the it's converted to a hash which is merged in.
|
80
|
+
# Otherwise the left value wins.
|
81
|
+
#
|
82
|
+
# @return [AttributeSet] resulting set
|
83
|
+
def +(o)
|
84
|
+
my_class = self.class
|
85
|
+
o = my_class.new(o) unless o.is_a?(Hash)
|
86
|
+
r = my_class.new
|
87
|
+
(keys + o.keys).uniq.each do |k|
|
88
|
+
if self.key?(k) && o.key?(k)
|
89
|
+
r[k] = merge_set(self[k], o[k]) { |a, b| a + b }
|
90
|
+
else
|
91
|
+
r[k] = AFHelpers.safe_dup(self[k] || o[k])
|
92
|
+
end
|
82
93
|
end
|
94
|
+
r
|
83
95
|
end
|
84
|
-
r
|
85
|
-
end
|
86
96
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
97
|
+
# Subtracts the given set from the current one
|
98
|
+
# by removing all the elements that have the same keys.
|
99
|
+
#
|
100
|
+
# @return [AttributeSet] resulting set
|
101
|
+
def -(o)
|
102
|
+
o = self.class.new(o) unless o.is_a?(Hash)
|
103
|
+
reject { |k, v| o.include?(k) }
|
104
|
+
end
|
95
105
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
106
|
+
# Returns a new attribute set containing elements
|
107
|
+
# common to the attribute set and the given enumerable object.
|
108
|
+
# Annotations from other set that aren't in this set are copied.
|
109
|
+
#
|
110
|
+
# @param o [Enumerable] object to intersect with
|
111
|
+
# @return [AttributeSet] intersection of objects
|
112
|
+
def &(o)
|
113
|
+
my_class = self.class
|
114
|
+
o = my_class.new(o) unless o.is_a?(Hash)
|
115
|
+
r = my_class.new
|
116
|
+
each_pair do |k, my_v|
|
117
|
+
if o.include?(k)
|
118
|
+
r[k] = merge_set(my_v, o[k]) { |a, b| a & b }
|
119
|
+
end
|
109
120
|
end
|
121
|
+
r
|
110
122
|
end
|
111
|
-
|
112
|
-
|
113
|
-
alias_method :intersection, :&
|
114
|
-
alias_method :intersect, :&
|
123
|
+
alias_method :intersection, :&
|
124
|
+
alias_method :intersect, :&
|
115
125
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
126
|
+
# Returns a new attribute set containing elements
|
127
|
+
# exclusive between the set and the given enumerable object
|
128
|
+
# (exclusive disjuction).
|
129
|
+
#
|
130
|
+
# @param o [Enumerable] object to exclusively disjunct with
|
131
|
+
# @return [AttributeSet] resulting set
|
132
|
+
def ^(o)
|
133
|
+
my_class = self.class
|
134
|
+
o = my_class.new(o) unless o.is_a?(Hash)
|
135
|
+
r = my_class.new
|
136
|
+
(o.keys + keys).uniq.each do |k|
|
137
|
+
if key?(k)
|
138
|
+
next if o.key?(k)
|
139
|
+
src = self[k]
|
140
|
+
elsif o.key?(k)
|
141
|
+
src = o[k]
|
142
|
+
end
|
143
|
+
r[k] = AFHelpers.safe_dup(src)
|
144
|
+
end
|
145
|
+
r
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
# Internal method for merging sets.
|
151
|
+
def merge_set(my_v, ov, my_class = self.class)
|
152
|
+
if my_v.is_a?(Hash)
|
153
|
+
if ov.is_a?(Hash)
|
154
|
+
my_v = my_class.new(my_v) unless my_v.is_a?(my_class)
|
155
|
+
ov = my_class.new(ov) unless ov.is_a?(my_class)
|
156
|
+
return yield(my_v, ov)
|
157
|
+
else
|
158
|
+
a_hash = my_v
|
159
|
+
a_other = ov
|
160
|
+
end
|
161
|
+
else
|
162
|
+
if ov.is_a?(Hash)
|
163
|
+
a_hash = ov
|
164
|
+
a_other = my_v
|
165
|
+
else
|
166
|
+
return my_v === true ? ov : my_v
|
167
|
+
end
|
168
|
+
end
|
169
|
+
if a_other === true || a_other === false || a_other.nil?
|
170
|
+
a_hash
|
171
|
+
else
|
172
|
+
a_hash.deep_merge(my_class.new(ov))
|
132
173
|
end
|
133
|
-
r[k] = AFHelpers.safe_dup(src)
|
134
174
|
end
|
175
|
+
end # module AttributeSetMethods
|
176
|
+
end # module AttributeFilters
|
177
|
+
|
178
|
+
# This class is a data structure used to store
|
179
|
+
# set of attributes.
|
180
|
+
class AttributeSet < Hash
|
181
|
+
include AttributeFilters::AttributeSetMethods
|
182
|
+
|
183
|
+
# Creates a new instance of attribute set.
|
184
|
+
#
|
185
|
+
# @return [AttributeSet] an AttributeSet object
|
186
|
+
def initialize(*args)
|
187
|
+
return super if args.count == 0
|
188
|
+
r = super()
|
189
|
+
add(args)
|
135
190
|
r
|
136
191
|
end
|
137
192
|
|
@@ -149,52 +204,5 @@ module ActiveModel
|
|
149
204
|
end
|
150
205
|
end
|
151
206
|
|
152
|
-
private
|
153
|
-
|
154
|
-
# Internal method for merging sets.
|
155
|
-
def merge_set(my_v, ov, my_class = self.class)
|
156
|
-
if my_v.is_a?(Hash)
|
157
|
-
if ov.is_a?(Hash)
|
158
|
-
my_v = my_class.new(my_v) unless my_v.is_a?(my_class)
|
159
|
-
ov = my_class.new(ov) unless ov.is_a?(my_class)
|
160
|
-
return yield(my_v, ov)
|
161
|
-
else
|
162
|
-
a_hash = my_v
|
163
|
-
a_other = ov
|
164
|
-
end
|
165
|
-
else
|
166
|
-
if ov.is_a?(Hash)
|
167
|
-
a_hash = ov
|
168
|
-
a_other = my_v
|
169
|
-
else
|
170
|
-
return my_v === true ? ov : my_v
|
171
|
-
end
|
172
|
-
end
|
173
|
-
if a_other === true || a_other === false || a_other.nil?
|
174
|
-
a_hash
|
175
|
-
else
|
176
|
-
a_hash.deep_merge(my_class.new(ov))
|
177
|
-
end
|
178
|
-
end
|
179
207
|
end # class AttributeSet
|
180
|
-
|
181
|
-
# This is a kind of AttributeSet class
|
182
|
-
# but its purpose it so store other information
|
183
|
-
# than attribute names.
|
184
|
-
class MetaSet < AttributeSet
|
185
|
-
def initialize(*args)
|
186
|
-
Hash.instance_method(:initialize).bind(self).call(*args)
|
187
|
-
end
|
188
|
-
def inspect(*args)
|
189
|
-
Hash.instance_method(:inspect).bind(self).call(*args)
|
190
|
-
end
|
191
|
-
# Internal method for merging sets.
|
192
|
-
def merge_set(my_v, ov, my_class = self.class)
|
193
|
-
if my_v.is_a?(my_class) && ov.is_a?(my_class)
|
194
|
-
my_v.deep_merge(ov)
|
195
|
-
else
|
196
|
-
my_v
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
208
|
end # module ActiveModel
|
@@ -22,7 +22,7 @@ module ActiveModel
|
|
22
22
|
def filtering_method(method_name, set_name)
|
23
23
|
set_name = set_name.to_sym
|
24
24
|
return unless method_defined?(method_name)
|
25
|
-
f = (@
|
25
|
+
f = (@attribute_filters ||= MetaSet.new)
|
26
26
|
f[set_name] = method_name unless f.key?(set_name)
|
27
27
|
nil
|
28
28
|
end
|
@@ -40,8 +40,8 @@ module ActiveModel
|
|
40
40
|
def included(base)
|
41
41
|
|
42
42
|
# merge filtering sets from filtering modules to models
|
43
|
-
fs = @
|
44
|
-
base.class_eval { (@
|
43
|
+
fs = @attribute_filters
|
44
|
+
base.class_eval { (@attribute_filters ||= MetaSet.new).merge!(fs) } unless fs.nil?
|
45
45
|
|
46
46
|
if base.const_defined?(:ClassMethods) &&
|
47
47
|
base.instance_of?(::Module) &&
|
@@ -52,8 +52,8 @@ module ActiveModel
|
|
52
52
|
base.class_eval <<-EVAL
|
53
53
|
unless singleton_class.method_defined?(:included)
|
54
54
|
def self.included(base)
|
55
|
-
fs = @
|
56
|
-
base.class_eval { (@
|
55
|
+
fs = @attribute_filters
|
56
|
+
base.class_eval { (@attribute_filters ||= MetaSet.new).merge!(fs) } unless fs.nil?
|
57
57
|
base.extend ClassMethods
|
58
58
|
end
|
59
59
|
end
|
@@ -97,7 +97,7 @@ module ActiveModel
|
|
97
97
|
#
|
98
98
|
# @return [nil]
|
99
99
|
def filter_attributes
|
100
|
-
as, fs = *self.class.class_eval { [__attribute_sets, @
|
100
|
+
as, fs = *self.class.class_eval { [__attribute_sets, @attribute_filters] }
|
101
101
|
return if fs.blank? || as.blank?
|
102
102
|
as.each_pair { |set_name, o| send(fs[set_name]) if fs.has_key?(set_name) }
|
103
103
|
nil
|
@@ -107,7 +107,7 @@ module ActiveModel
|
|
107
107
|
#
|
108
108
|
# @return [MetaSet{Symbol => Symbol}] a meta set of filtering methods and associated sets
|
109
109
|
def filtering_methods
|
110
|
-
f = self.class.instance_variable_get(:@
|
110
|
+
f = self.class.instance_variable_get(:@attribute_filters)
|
111
111
|
f.nil? ? ActiveModel::MetaSet.new : f.dup
|
112
112
|
end
|
113
113
|
|
@@ -12,8 +12,48 @@ module ActiveModel
|
|
12
12
|
module AttributeFilters
|
13
13
|
module ClassMethods
|
14
14
|
|
15
|
+
unless method_defined?(:method_added)
|
16
|
+
def method_added(x); end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @private
|
20
|
+
def method_added_with_afv(method_name)
|
21
|
+
method_name = method_name.to_s
|
22
|
+
if method_name[-1] == '='
|
23
|
+
atr_name = method_name[0..-2]
|
24
|
+
have_writer = true
|
25
|
+
else
|
26
|
+
atr_name = method_name
|
27
|
+
have_writer = false
|
28
|
+
end
|
29
|
+
a = __attribute_filters_virtual[atr_name]
|
30
|
+
if a && a != :no_wrap
|
31
|
+
if have_writer && method_defined?(atr_name) || !have_writer && method_defined?("#{atr_name}=")
|
32
|
+
wrap_virtual_attribute_writer(atr_name)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
method_added_without_afv(method_name)
|
36
|
+
end
|
37
|
+
alias_method :method_added_without_afv, :method_added
|
38
|
+
alias_method :method_added, :method_added_with_afv
|
39
|
+
|
15
40
|
private
|
16
41
|
|
42
|
+
def wrap_virtual_attribute_writer(atr_name)
|
43
|
+
writer_name = "#{atr_name}=".to_sym
|
44
|
+
writer_name_wct = "#{atr_name}_without_ct"
|
45
|
+
__attribute_filters_virtual[atr_name] = false
|
46
|
+
alias_method(writer_name_wct, writer_name)
|
47
|
+
class_eval <<-EVAL
|
48
|
+
def #{writer_name}(val)
|
49
|
+
attribute_will_change!('#{atr_name}') if val != #{atr_name}
|
50
|
+
#{writer_name_wct}(val)
|
51
|
+
end
|
52
|
+
EVAL
|
53
|
+
__attribute_filters_virtual[atr_name] = :tracked
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
17
57
|
unless method_defined?(:attr_virtual)
|
18
58
|
# This method creates setter and getter for attributes of the given names
|
19
59
|
# and ensures that changes of their values are tracked.
|
@@ -22,30 +62,21 @@ module ActiveModel
|
|
22
62
|
# +attr_virtual+ will overwrite setter. Don't do that.
|
23
63
|
def attr_virtual(*attribute_names)
|
24
64
|
attribute_names.flatten.compact.uniq.each do |atr_name|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
alias_method :#{atr_name}_without_change_tracking=, :#{writer_name}
|
32
|
-
def #{writer_name}(val)
|
33
|
-
attribute_will_change!('#{atr_name}') if val != '#{atr_name}'
|
34
|
-
#{atr_name}_without_change_tracking=(val)
|
35
|
-
end
|
36
|
-
EVAL
|
65
|
+
atr_name = atr_name.to_s
|
66
|
+
writer_name = "#{atr_name}=".to_sym
|
67
|
+
if method_defined?(writer_name) && method_defined?(atr_name)
|
68
|
+
unless __attribute_filters_virtual.key?(atr_name)
|
69
|
+
wrap_virtual_attribute_writer(atr_name)
|
70
|
+
end
|
37
71
|
else
|
38
|
-
|
39
|
-
def #{writer_name}(val)
|
40
|
-
attribute_will_change!('#{atr_name}') if val != '#{atr_name}'
|
41
|
-
@#{atr_name} = val
|
42
|
-
end
|
43
|
-
EVAL
|
72
|
+
__attribute_filters_virtual[atr_name] = :waiting
|
44
73
|
end
|
45
74
|
end
|
46
75
|
nil
|
47
76
|
end # def attr_virtual
|
77
|
+
alias_method :has_virtual_attribute, :attr_virtual
|
48
78
|
end # unless method_defined?(:attr_virtual)
|
79
|
+
|
49
80
|
end # module ClassMethods
|
50
81
|
end # module AttributeFilters
|
51
82
|
end # module ActiveModel
|
@@ -454,7 +454,7 @@ module ActiveModel
|
|
454
454
|
# should be treated as virtual (even not present in the
|
455
455
|
# attributes hash provided by ORM or ActiveModel).
|
456
456
|
#
|
457
|
-
# @note This method may be used directly if your setter
|
457
|
+
# @note This method may be used directly ONLY if your setter
|
458
458
|
# notifies Rails about changes. Otherwise it's recommended
|
459
459
|
# to use the DSL keyword +attr_virtual+.
|
460
460
|
# @param attributes [Array] list of attribute names
|
@@ -467,7 +467,9 @@ module ActiveModel
|
|
467
467
|
# @return [AttributeSet] set of attribute names
|
468
468
|
def treat_as_virtual(*args)
|
469
469
|
return __attribute_filters_virtual.deep_dup if args.blank?
|
470
|
-
|
470
|
+
args.flatten.compact.each do |atr|
|
471
|
+
__attribute_filters_virtual[atr.to_s] = :no_wrap
|
472
|
+
end
|
471
473
|
nil
|
472
474
|
end
|
473
475
|
alias_method :attribute_filters_virtual, :treat_as_virtual
|
@@ -479,20 +481,24 @@ module ActiveModel
|
|
479
481
|
|
480
482
|
private
|
481
483
|
|
484
|
+
def __attribute_sets_meta
|
485
|
+
@attribute_sets ||= MetaSet.new
|
486
|
+
end
|
487
|
+
|
482
488
|
def __attribute_filters_semi_real
|
483
|
-
|
489
|
+
__attribute_sets_meta[:semi_real] ||= ActiveModel::AttributeSet.new
|
484
490
|
end
|
485
491
|
|
486
492
|
def __attribute_filters_virtual
|
487
|
-
|
493
|
+
__attribute_sets_meta[:virtual] ||= ActiveModel::AttributeSet.new
|
488
494
|
end
|
489
495
|
|
490
496
|
def __attributes_to_sets_map
|
491
|
-
|
497
|
+
__attribute_sets_meta[:attrs] ||= MetaSet.new(ActiveModel::MetaSet.new.freeze)
|
492
498
|
end
|
493
499
|
|
494
500
|
def __attribute_sets
|
495
|
-
|
501
|
+
__attribute_sets_meta[:sets] ||= MetaSet.new(ActiveModel::AttributeSet.new.freeze)
|
496
502
|
end
|
497
503
|
|
498
504
|
def add_atrs_to_set(set_name, *atrs)
|
@@ -12,17 +12,8 @@ module ActiveModel
|
|
12
12
|
# This is a kind of AttributeSet class
|
13
13
|
# but its purpose it so store other information
|
14
14
|
# than attribute names.
|
15
|
-
class MetaSet <
|
16
|
-
|
17
|
-
# @private
|
18
|
-
def initialize(*args)
|
19
|
-
Hash.instance_method(:initialize).bind(self).call(*args)
|
20
|
-
end
|
21
|
-
|
22
|
-
# @private
|
23
|
-
def inspect(*args)
|
24
|
-
Hash.instance_method(:inspect).bind(self).call(*args)
|
25
|
-
end
|
15
|
+
class MetaSet < Hash
|
16
|
+
include AttributeFilters::AttributeSetMethods
|
26
17
|
|
27
18
|
private
|
28
19
|
|
@@ -14,11 +14,11 @@ module ActiveModel
|
|
14
14
|
# @private
|
15
15
|
EMAIL = 'pw@gnu.org'
|
16
16
|
# @private
|
17
|
-
VERSION = '2.0.
|
17
|
+
VERSION = '2.0.1'
|
18
18
|
# @private
|
19
19
|
NAME = 'attribute-filters'
|
20
20
|
# @private
|
21
|
-
SUMMARY = 'Concise way of filtering model attributes in Rails'
|
21
|
+
SUMMARY = 'Concise way of filtering and grouping model attributes in Rails'
|
22
22
|
# @private
|
23
23
|
URL = 'https://rubygems.org/gems/attribute-filters/'
|
24
24
|
# @private
|
@@ -180,13 +180,14 @@ describe ActiveModel::AttributeFilters do
|
|
180
180
|
before do
|
181
181
|
TestModel.class_eval do
|
182
182
|
include ActiveModel::AttributeFilters::Common
|
183
|
-
|
183
|
+
#@__attribute_sets = nil
|
184
|
+
@attribute_sets = nil
|
184
185
|
end
|
185
186
|
@tm = TestModel.new
|
186
187
|
end
|
187
188
|
|
188
189
|
after do
|
189
|
-
TestModel.class_eval{@
|
190
|
+
TestModel.class_eval{@attribute_sets = nil}
|
190
191
|
@tm.attributes_that(:should_be_splitted).should be_empty
|
191
192
|
@tm.attributes_that(:should_be_joined).should be_empty
|
192
193
|
@tm.attributes_that(:should_be_splitted).annotation(:real_name).should == nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attribute-filters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
YzBobXpodlZadEJ3ZlpyaXRTVmhmQ0xwNXVGd3FDcVkKTkszVElaYVBDaDFT
|
37
37
|
Mi9FUzZ3WE52alErNUVuRUVMOWovcFNFb3A5RFlFQlBhTTJXRFZSNWkwakpU
|
38
38
|
QWFSV3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
39
|
-
date: 2012-11-
|
39
|
+
date: 2012-11-04 00:00:00.000000000 Z
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: railties
|
@@ -320,7 +320,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
320
320
|
version: '0'
|
321
321
|
segments:
|
322
322
|
- 0
|
323
|
-
hash:
|
323
|
+
hash: -1229665950453395191
|
324
324
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
325
325
|
none: false
|
326
326
|
requirements:
|
@@ -332,5 +332,5 @@ rubyforge_project: attribute-filters
|
|
332
332
|
rubygems_version: 1.8.24
|
333
333
|
signing_key:
|
334
334
|
specification_version: 3
|
335
|
-
summary: Concise way of filtering model attributes in Rails
|
335
|
+
summary: Concise way of filtering and grouping model attributes in Rails
|
336
336
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|