attribute-filters 2.0.0 → 2.0.1
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 +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
|