attribute-filters 1.4.0 → 2.0.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/.yardopts +1 -0
- data/ChangeLog +501 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +7 -7
- data/Manifest.txt +7 -0
- data/README.md +59 -30
- data/Rakefile +1 -0
- data/attribute-filters.gemspec +4 -4
- data/docs/COMMON-FILTERS.md +1067 -0
- data/docs/HISTORY +42 -0
- data/docs/TODO +29 -12
- data/docs/USAGE.md +718 -818
- data/lib/attribute-filters.rb +4 -2
- data/lib/attribute-filters/attribute_set.rb +144 -73
- data/lib/attribute-filters/attribute_set_annotations.rb +110 -77
- data/lib/attribute-filters/attribute_set_attrquery.rb +51 -8
- data/lib/attribute-filters/attribute_set_enum.rb +44 -38
- data/lib/attribute-filters/attribute_set_query.rb +62 -12
- data/lib/attribute-filters/backports.rb +36 -4
- data/lib/attribute-filters/common_filters.rb +83 -37
- data/lib/attribute-filters/common_filters/bare.rb +29 -0
- data/lib/attribute-filters/common_filters/case.rb +34 -19
- data/lib/attribute-filters/common_filters/convert.rb +259 -0
- data/lib/attribute-filters/common_filters/join.rb +37 -30
- data/lib/attribute-filters/common_filters/order.rb +105 -0
- data/lib/attribute-filters/common_filters/pick.rb +90 -0
- data/lib/attribute-filters/common_filters/presence.rb +70 -0
- data/lib/attribute-filters/common_filters/split.rb +37 -21
- data/lib/attribute-filters/common_filters/squeeze.rb +28 -9
- data/lib/attribute-filters/common_filters/strip.rb +7 -3
- data/lib/attribute-filters/dsl_attr_virtual.rb +2 -1
- data/lib/attribute-filters/dsl_filters.rb +14 -61
- data/lib/attribute-filters/dsl_sets.rb +238 -88
- data/lib/attribute-filters/helpers.rb +7 -1
- data/lib/attribute-filters/meta_set.rb +38 -0
- data/lib/attribute-filters/version.rb +1 -1
- data/spec/attribute-filters_spec.rb +178 -16
- data/spec/spec_helper.rb +9 -4
- metadata +129 -69
- metadata.gz.sig +0 -0
data/lib/attribute-filters.rb
CHANGED
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
require 'attribute-filters'
|
|
4
4
|
require 'attribute-filters/version'
|
|
5
5
|
require 'attribute-filters/backports'
|
|
6
|
+
require 'attribute-filters/helpers'
|
|
6
7
|
|
|
7
8
|
require 'attribute-filters/attribute_set_enum'
|
|
8
|
-
require 'attribute-filters/attribute_set_annotations'
|
|
9
9
|
require 'attribute-filters/attribute_set'
|
|
10
|
+
require 'attribute-filters/meta_set'
|
|
11
|
+
require 'attribute-filters/attribute_set_annotations'
|
|
10
12
|
require 'attribute-filters/attribute_set_query'
|
|
11
13
|
require 'attribute-filters/attribute_set_attrquery'
|
|
12
14
|
|
|
13
|
-
require 'attribute-filters/helpers'
|
|
14
15
|
require 'attribute-filters/dsl_sets'
|
|
15
16
|
require 'attribute-filters/dsl_filters'
|
|
16
17
|
require 'attribute-filters/dsl_attr_virtual'
|
|
@@ -21,3 +22,4 @@ if defined? ::Rails
|
|
|
21
22
|
else
|
|
22
23
|
require 'attribute-filters/active_model_insert'
|
|
23
24
|
end
|
|
25
|
+
|
|
@@ -7,15 +7,25 @@
|
|
|
7
7
|
# This file contains ActiveModel::AttributeSet class
|
|
8
8
|
# used to interact with attribute sets.
|
|
9
9
|
|
|
10
|
-
require 'set'
|
|
11
|
-
|
|
12
10
|
# @abstract This namespace is shared with ActveModel.
|
|
13
11
|
module ActiveModel
|
|
14
12
|
# This class is a data structure used to store
|
|
15
13
|
# set of attributes.
|
|
16
|
-
class AttributeSet <
|
|
14
|
+
class AttributeSet < Hash
|
|
17
15
|
include ActiveModel::AttributeSet::Enumerable
|
|
18
|
-
|
|
16
|
+
|
|
17
|
+
# Helpers module shortcut
|
|
18
|
+
AFHelpers = ActiveModel::AttributeFilters::AttributeFiltersHelpers
|
|
19
|
+
|
|
20
|
+
# Creates a new instance of attribute set.
|
|
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
|
|
19
29
|
|
|
20
30
|
# Adds the given object to the set and returns self.
|
|
21
31
|
# If the object is already in the set, returns nil.
|
|
@@ -26,104 +36,165 @@ module ActiveModel
|
|
|
26
36
|
# which contains elements that were successfuly added to set
|
|
27
37
|
# and didn't existed there before.
|
|
28
38
|
#
|
|
29
|
-
# @param
|
|
30
|
-
# @return [AttributeSet
|
|
31
|
-
def add(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
# @param args [Array<Object,Hash,Array,Enumerable>] object(s) to be added to set
|
|
40
|
+
# @return [AttributeSet] current attribute set object
|
|
41
|
+
def add(*args)
|
|
42
|
+
args.flatten.each do |a|
|
|
43
|
+
if a.is_a?(Hash)
|
|
44
|
+
deep_merge!(a)
|
|
45
|
+
elsif a.is_a?(::Enumerable)
|
|
46
|
+
a.each { |e| self[e] = true unless e.blank? }
|
|
47
|
+
else
|
|
48
|
+
self[a] = true
|
|
49
|
+
end
|
|
36
50
|
end
|
|
51
|
+
self
|
|
37
52
|
end
|
|
38
53
|
alias_method :<<, :add
|
|
39
54
|
|
|
40
|
-
#
|
|
41
|
-
|
|
55
|
+
# @private
|
|
56
|
+
def to_a
|
|
57
|
+
keys
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# @private
|
|
61
|
+
def to_set
|
|
62
|
+
keys.to_set
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Adds two sets by deeply merging their contents.
|
|
66
|
+
# If any value stored in one set under conflicting key
|
|
67
|
+
# is +true+, +false+ or +nil+ then value is taken from other set.
|
|
68
|
+
# If one of the conflicting values is a kind of Hash and
|
|
69
|
+
# the other is not the it's converted to a hash which is merged in.
|
|
70
|
+
# Otherwise the left value wins.
|
|
42
71
|
#
|
|
43
|
-
# @
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
# @return [AttributeSet] resulting set
|
|
73
|
+
def +(o)
|
|
74
|
+
my_class = self.class
|
|
75
|
+
o = my_class.new(o) unless o.is_a?(Hash)
|
|
76
|
+
r = my_class.new
|
|
77
|
+
(keys + o.keys).uniq.each do |k|
|
|
78
|
+
if self.key?(k) && o.key?(k)
|
|
79
|
+
r[k] = merge_set(self[k], o[k]) { |a, b| a + b }
|
|
80
|
+
else
|
|
81
|
+
r[k] = AFHelpers.safe_dup(self[k] || o[k])
|
|
82
|
+
end
|
|
83
|
+
end
|
|
48
84
|
r
|
|
49
85
|
end
|
|
50
86
|
|
|
51
|
-
#
|
|
87
|
+
# Subtracts the given set from the current one
|
|
88
|
+
# by removing all the elements that have the same keys.
|
|
52
89
|
#
|
|
53
|
-
# @
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
remove_annotations
|
|
58
|
-
copy_annotations(o)
|
|
59
|
-
r
|
|
90
|
+
# @return [AttributeSet] resulting set
|
|
91
|
+
def -(o)
|
|
92
|
+
o = self.class.new(o) unless o.is_a?(Hash)
|
|
93
|
+
reject { |k, v| o.include?(k) }
|
|
60
94
|
end
|
|
61
95
|
|
|
62
96
|
# Returns a new attribute set containing elements
|
|
63
97
|
# common to the attribute set and the given enumerable object.
|
|
98
|
+
# Annotations from other set that aren't in this set are copied.
|
|
64
99
|
#
|
|
65
100
|
# @param o [Enumerable] object to intersect with
|
|
66
101
|
# @return [AttributeSet] intersection of objects
|
|
67
102
|
def &(o)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
103
|
+
my_class = self.class
|
|
104
|
+
o = my_class.new(o) unless o.is_a?(Hash)
|
|
105
|
+
r = my_class.new
|
|
106
|
+
each_pair do |k, my_v|
|
|
107
|
+
if o.include?(k)
|
|
108
|
+
r[k] = merge_set(my_v, o[k]) { |a, b| a & b }
|
|
109
|
+
end
|
|
72
110
|
end
|
|
73
111
|
r
|
|
74
112
|
end
|
|
75
113
|
alias_method :intersection, :&
|
|
114
|
+
alias_method :intersect, :&
|
|
76
115
|
|
|
77
|
-
#
|
|
78
|
-
# and
|
|
116
|
+
# Returns a new attribute set containing elements
|
|
117
|
+
# exclusive between the set and the given enumerable object
|
|
118
|
+
# (exclusive disjuction).
|
|
79
119
|
#
|
|
80
|
-
# @
|
|
81
|
-
# @
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
o = o.
|
|
85
|
-
r =
|
|
86
|
-
|
|
120
|
+
# @param o [Enumerable] object to exclusively disjunct with
|
|
121
|
+
# @return [AttributeSet] resulting set
|
|
122
|
+
def ^(o)
|
|
123
|
+
my_class = self.class
|
|
124
|
+
o = my_class.new(o) unless o.is_a?(Hash)
|
|
125
|
+
r = my_class.new
|
|
126
|
+
(o.keys + keys).uniq.each do |k|
|
|
127
|
+
if key?(k)
|
|
128
|
+
next if o.key?(k)
|
|
129
|
+
src = self[k]
|
|
130
|
+
elsif o.key?(k)
|
|
131
|
+
src = o[k]
|
|
132
|
+
end
|
|
133
|
+
r[k] = AFHelpers.safe_dup(src)
|
|
134
|
+
end
|
|
87
135
|
r
|
|
88
136
|
end
|
|
89
137
|
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
# @private
|
|
139
|
+
def inspect
|
|
140
|
+
ids = (Thread.current[:attributefilters] ||= [])
|
|
141
|
+
ids.include?(object_id) and return sprintf('#<%s: {...}>', self.class.name)
|
|
142
|
+
begin
|
|
143
|
+
ids << object_id
|
|
144
|
+
r = []
|
|
145
|
+
each_pair { |k, v| r << (v.is_a?(Hash) ? "#{k}+" : k) }
|
|
146
|
+
return sprintf('#<%s: {%s}>', self.class, r.inspect[1..-2])
|
|
147
|
+
ensure
|
|
148
|
+
ids.pop
|
|
149
|
+
end
|
|
100
150
|
end
|
|
101
151
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
112
178
|
end
|
|
179
|
+
end # class AttributeSet
|
|
113
180
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
127
198
|
end
|
|
128
199
|
end
|
|
129
|
-
end
|
|
200
|
+
end # module ActiveModel
|
|
@@ -22,26 +22,31 @@ module ActiveModel
|
|
|
22
22
|
# @param attribute_name [Symbol,String] name of annotated attribute
|
|
23
23
|
# @param annotation_key [Symbol,String] name of annotation key
|
|
24
24
|
# @param value [Object] annotation value
|
|
25
|
+
# @return [nil]
|
|
25
26
|
#
|
|
26
27
|
# @overload annotate_attribute_set(set_name, attribute_name, annotation)
|
|
27
28
|
# @param set_name [Symbol,String] name of a set
|
|
28
29
|
# @param attribute_name [Symbol,String] name of annotated attribute
|
|
29
30
|
# @param annotation [Hash{Symbol => Object}] annotation key => value pairs
|
|
31
|
+
# @return [nil]
|
|
30
32
|
#
|
|
31
33
|
# @overload annotate_attribute_set(set_name, annotation)
|
|
32
34
|
# @param set_name [Symbol,String] name of a set
|
|
33
35
|
# @param annotation [Hash{Symbol => Array<Symbol,Object>}] annotation key and value assigned to attribute name
|
|
36
|
+
# @return [nil]
|
|
34
37
|
#
|
|
35
38
|
# @overload annotate_attribute_set(set_name, *annotation)
|
|
36
39
|
# @param set_name [Symbol,String] name of a set
|
|
37
40
|
# @param annotation [Array<Symbol,Symbol,Object>}] attribute name, annotation key and value
|
|
41
|
+
# @return [nil]
|
|
38
42
|
#
|
|
39
43
|
# @overload annotate_attribute_set(set_name, annotations)
|
|
40
44
|
# @param set_name [Symbol,String] name of a set
|
|
41
45
|
# @param annotations [Hash{Symbol => Hash{Symbol => Object}}] annotation key => value pairs for attributes
|
|
46
|
+
# @return [nil]
|
|
42
47
|
def annotate_attribute_set(*args)
|
|
43
48
|
first_arg = args.shift
|
|
44
|
-
if first_arg.is_a?(Hash)
|
|
49
|
+
if first_arg.is_a?(Hash) # multiple sets defined
|
|
45
50
|
first_arg.each_pair do |k, v|
|
|
46
51
|
annotate_attribute_set(k, v, *args)
|
|
47
52
|
end
|
|
@@ -60,7 +65,7 @@ module ActiveModel
|
|
|
60
65
|
else
|
|
61
66
|
unless an_key.nil? || atr_name.nil?
|
|
62
67
|
first_arg = first_arg.to_sym
|
|
63
|
-
unless __attribute_sets.
|
|
68
|
+
unless __attribute_sets.key?(first_arg)
|
|
64
69
|
raise ArgumentError, "trying to annotate non-existent set '#{first_arg}'"
|
|
65
70
|
end
|
|
66
71
|
__attribute_sets[first_arg].annotate(atr_name, an_key, an_val)
|
|
@@ -69,22 +74,78 @@ module ActiveModel
|
|
|
69
74
|
end
|
|
70
75
|
nil
|
|
71
76
|
end
|
|
72
|
-
alias_method :annotate_attributes_that_are,
|
|
73
|
-
alias_method :annotate_attributes_that,
|
|
74
|
-
alias_method :annotate_attributes_are,
|
|
75
|
-
alias_method :annotate_attributes_for,
|
|
76
|
-
alias_method :annotate_attributes_set,
|
|
77
|
-
alias_method :annotate_properties_that,
|
|
78
|
-
alias_method :annotate_attributes,
|
|
79
|
-
alias_method :
|
|
80
|
-
|
|
77
|
+
alias_method :annotate_attributes_that_are, :annotate_attribute_set
|
|
78
|
+
alias_method :annotate_attributes_that, :annotate_attribute_set
|
|
79
|
+
alias_method :annotate_attributes_are, :annotate_attribute_set
|
|
80
|
+
alias_method :annotate_attributes_for, :annotate_attribute_set
|
|
81
|
+
alias_method :annotate_attributes_set, :annotate_attribute_set
|
|
82
|
+
alias_method :annotate_properties_that, :annotate_attribute_set
|
|
83
|
+
alias_method :annotate_attributes, :annotate_attribute_set
|
|
84
|
+
alias_method :annotates_attributes_that_are, :annotate_attribute_set
|
|
85
|
+
alias_method :annotates_attributes_that, :annotate_attribute_set
|
|
86
|
+
alias_method :annotates_attributes_are, :annotate_attribute_set
|
|
87
|
+
alias_method :annotates_attributes_for, :annotate_attribute_set
|
|
88
|
+
alias_method :annotates_attributes_set, :annotate_attribute_set
|
|
89
|
+
alias_method :annotates_properties_that, :annotate_attribute_set
|
|
90
|
+
alias_method :annotates_attributes, :annotate_attribute_set
|
|
91
|
+
alias_method :attribute_set_annotate, :annotate_attribute_set
|
|
92
|
+
|
|
93
|
+
# Helps in adding attributes to sets with annotations used to store parameters.
|
|
94
|
+
#
|
|
95
|
+
# @param set_name [Symbol,String] name of a set
|
|
96
|
+
# @param param_defs [Hash{Symbol => Array<Symbol,String>}]
|
|
97
|
+
# @param default_param [Symbol,String,nil]
|
|
98
|
+
# @param attribute_defs [Hash{Symbol => Object}, Array<Symbol,String>]
|
|
99
|
+
# @return [nil]
|
|
100
|
+
# @example
|
|
101
|
+
# setup_attributes_set :should_be_filled,
|
|
102
|
+
# { 'atr_name' => { :with => 'x', :fill_any => true }, :other_atr => 'text' },
|
|
103
|
+
# { :fill_value => [ :with, :fill_with, :value, :content ] },
|
|
104
|
+
# :fill_value
|
|
105
|
+
def setup_attributes_set(set_name, attribute_defs, param_defs = {}, default_param = nil)
|
|
106
|
+
# create parameter keys conversion hash
|
|
107
|
+
pdefs = {}
|
|
108
|
+
param_defs.each_pair do |k, v|
|
|
109
|
+
k = k.to_sym
|
|
110
|
+
if v.is_a?(Array)
|
|
111
|
+
v.each { |x| pdefs[x.to_sym] = k }
|
|
112
|
+
else
|
|
113
|
+
pdefs[v.to_sym] = k
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
# process attribute -> annotations pairs or other arguments given
|
|
117
|
+
if attribute_defs.is_a?(Array)
|
|
118
|
+
attribute_defs.each { |arg| setup_attributes_set(set_name, arg, param_defs, default_param) }
|
|
119
|
+
elsif attribute_defs.is_a?(Hash)
|
|
120
|
+
output_set = {}
|
|
121
|
+
attribute_defs.each_pair do |atr_name, atr_annotations|
|
|
122
|
+
atr_name = atr_name.to_s
|
|
123
|
+
output_set[atr_name] = {}
|
|
124
|
+
if atr_annotations.is_a?(Hash)
|
|
125
|
+
atr_annotations.each_pair do |an_key, an_val|
|
|
126
|
+
an_key = an_key.to_sym
|
|
127
|
+
an_key = pdefs[an_key] if pdefs.key?(an_key)
|
|
128
|
+
output_set[atr_name][an_key] = an_val
|
|
129
|
+
end
|
|
130
|
+
elsif !default_param.nil?
|
|
131
|
+
output_set[atr_name][default_param.to_sym] = atr_annotations
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
attribute_set(set_name, output_set)
|
|
135
|
+
else
|
|
136
|
+
attribute_set(set_name, *attribute_defs)
|
|
137
|
+
end
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
alias_method :setup_attributes_that, :setup_attributes_set
|
|
141
|
+
|
|
81
142
|
# Deletes annotaion from a given set
|
|
82
143
|
#
|
|
83
144
|
# @param set_name [String,Symbol] set name
|
|
84
145
|
# @param atr_name [String,Symbol] attribute name
|
|
85
146
|
# @param annotations [Array<String,Symbol>] annotation keys
|
|
86
147
|
#
|
|
87
|
-
# @return [
|
|
148
|
+
# @return [nil]
|
|
88
149
|
def delete_annotation_from_set(set_name, atr_name = nil, *annotations)
|
|
89
150
|
if set_name.is_a?(Hash)
|
|
90
151
|
r = {}
|
|
@@ -99,32 +160,34 @@ module ActiveModel
|
|
|
99
160
|
end
|
|
100
161
|
else
|
|
101
162
|
set_name = set_name.to_sym
|
|
102
|
-
return unless __attribute_sets.
|
|
163
|
+
return nil unless __attribute_sets.key?(set_name) && atr_name.present?
|
|
103
164
|
atr_name = atr_name.to_sym
|
|
104
165
|
__attribute_sets[set_name].delete_annotation(atr_name, *annotations)
|
|
105
166
|
end
|
|
167
|
+
nil
|
|
106
168
|
end
|
|
107
169
|
alias_method :delete_annotations_from_set, :delete_annotation_from_set
|
|
108
|
-
alias_method :
|
|
170
|
+
alias_method :deletes_annotation_from_set, :delete_annotation_from_set
|
|
171
|
+
alias_method :deletes_annotations_from_set, :delete_annotation_from_set
|
|
109
172
|
end # module ClassMethods
|
|
110
173
|
end # module AttributeFilters
|
|
111
174
|
|
|
112
|
-
|
|
175
|
+
class AttributeSet
|
|
113
176
|
# Adds an annotation to the given attribute.
|
|
114
177
|
#
|
|
115
178
|
# @param atr_name [String,Symbol] attribute name
|
|
116
179
|
# @param name [String,Symbol] annotation key
|
|
117
180
|
# @param value [Object] annotation value
|
|
118
181
|
# @raise [ArgumentError] when the given attribute name does not exist in a set
|
|
119
|
-
# @return [
|
|
182
|
+
# @return [nil]
|
|
120
183
|
def annotate(atr_name, name, value)
|
|
121
184
|
atr_name = atr_name.to_s unless atr_name.blank?
|
|
122
|
-
unless
|
|
185
|
+
unless key?(atr_name)
|
|
123
186
|
raise ArgumentError, "attribute '#{atr_name}' must exist in order to annotate it"
|
|
124
187
|
end
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
188
|
+
self[atr_name] = Hash.new unless self[atr_name].is_a?(Hash)
|
|
189
|
+
self[atr_name][name.to_sym] = value
|
|
190
|
+
nil
|
|
128
191
|
end
|
|
129
192
|
alias_method :add_op, :annotate
|
|
130
193
|
alias_method :bind_op, :annotate
|
|
@@ -147,11 +210,11 @@ module ActiveModel
|
|
|
147
210
|
# @return [Boolean] +true+ if the current set has at least one of the given +annotation_keys+ for +attribute_name+,
|
|
148
211
|
# +false+ otherwise
|
|
149
212
|
def has_annotation?(*args)
|
|
150
|
-
return false if
|
|
213
|
+
return false if empty?
|
|
151
214
|
return true if args.size == 0
|
|
152
215
|
atr_name = args.shift.to_s
|
|
153
|
-
a_group =
|
|
154
|
-
a_group.blank?
|
|
216
|
+
a_group = self[atr_name]
|
|
217
|
+
return false if a_group.blank? || !a_group.is_a?(Hash)
|
|
155
218
|
args.empty? and return true
|
|
156
219
|
args.any? { |a_name| a_group.key?(a_name.to_sym) }
|
|
157
220
|
end
|
|
@@ -168,23 +231,17 @@ module ActiveModel
|
|
|
168
231
|
def annotation(atr_name, *annotation_names)
|
|
169
232
|
atr_name.present? or return nil
|
|
170
233
|
has_annotations? or return nil
|
|
171
|
-
an_group =
|
|
172
|
-
return nil if an_group.nil?
|
|
234
|
+
an_group = self[atr_name.to_s]
|
|
235
|
+
return nil if an_group.nil? || !an_group.is_a?(Hash)
|
|
173
236
|
case annotation_names.size
|
|
174
237
|
when 0
|
|
175
238
|
r = Hash.new
|
|
176
|
-
an_group.each_pair
|
|
177
|
-
r[k] = v.is_a?(Enumerable) ? v.dup : v
|
|
178
|
-
end
|
|
239
|
+
an_group.each_pair { |k, v| r[k] = AFHelpers.safe_dup(v) }
|
|
179
240
|
r
|
|
180
241
|
when 1
|
|
181
|
-
|
|
182
|
-
r.is_a?(Enumerable) ? r.dup : r
|
|
242
|
+
AFHelpers.safe_dup(an_group[annotation_names.first.to_sym])
|
|
183
243
|
else
|
|
184
|
-
annotation_names.map
|
|
185
|
-
r = an_group[a.to_sym]
|
|
186
|
-
r.is_a?(Enumerable) ? r.dup : r
|
|
187
|
-
end
|
|
244
|
+
annotation_names.map { |a| AFHelpers.safe_dup(an_group[a.to_sym]) }
|
|
188
245
|
end
|
|
189
246
|
end
|
|
190
247
|
alias_method :get_annotation, :annotation
|
|
@@ -199,61 +256,37 @@ module ActiveModel
|
|
|
199
256
|
# @return [Hash,Object,nil] deleted annotations (hash),
|
|
200
257
|
# deleted annotation value or +nil+ if there wasn't anything to delete
|
|
201
258
|
def delete_annotation(atr_name, annotation = nil)
|
|
202
|
-
return nil if
|
|
259
|
+
return nil if atr_name.blank?
|
|
203
260
|
atr_name = atr_name.to_s
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
261
|
+
return nil unless key?(atr_name)
|
|
262
|
+
ag = self[atr_name]
|
|
263
|
+
return nil if ag === true
|
|
264
|
+
if annotation.nil? || !ag.is_a?(Hash)
|
|
265
|
+
self[atr_name] = true
|
|
266
|
+
return ag
|
|
208
267
|
else
|
|
209
|
-
|
|
268
|
+
r = ag.delete(annotation.to_sym)
|
|
269
|
+
self[atr_name] = true if ag.empty?
|
|
270
|
+
r
|
|
210
271
|
end
|
|
211
272
|
end
|
|
212
273
|
alias_method :delete_annotations, :delete_annotation
|
|
213
274
|
|
|
214
275
|
# Removes all annotations.
|
|
215
|
-
# @return [
|
|
276
|
+
# @return [nil]
|
|
216
277
|
def remove_annotations
|
|
217
|
-
|
|
278
|
+
each_pair { |k, v| self[k] = true }
|
|
279
|
+
nil
|
|
218
280
|
end
|
|
219
281
|
|
|
220
|
-
|
|
221
|
-
|
|
282
|
+
# Returns all annotations that are set.
|
|
283
|
+
#
|
|
284
|
+
# @return [Hash{String => Hash}] annotations indexed by attribute names
|
|
222
285
|
def annotations
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
def copy_annotations(o)
|
|
227
|
-
return unless o.is_a?(self.class) && o.has_annotations?
|
|
228
|
-
@annotations ||= Hash.new
|
|
229
|
-
o.send(:annotations).each_pair do |atr, annotations_group|
|
|
230
|
-
if include?(atr)
|
|
231
|
-
current_group = (@annotations[atr] ||= Hash.new)
|
|
232
|
-
annotations_group.each_pair do |annotation_name, v|
|
|
233
|
-
current_group[annotation_name] ||= (v.is_a?(Enumerable) ? v.dup : v)
|
|
234
|
-
end
|
|
235
|
-
end
|
|
286
|
+
Hash.new.tap do |r|
|
|
287
|
+
each_pair { |atr_name, an| r[atr_name] = an.deep_dup if an.is_a?(Hash) }
|
|
236
288
|
end
|
|
237
289
|
end
|
|
238
290
|
|
|
239
|
-
|
|
240
|
-
@annotations ||= Hash.new
|
|
241
|
-
from.each_pair do |atr, annotations_group|
|
|
242
|
-
if include?(atr) && (other.nil? || !other.has_key?(atr))
|
|
243
|
-
current_group = (@annotations[atr] ||= Hash.new)
|
|
244
|
-
annotations_group.each_pair do |annotation_name, v|
|
|
245
|
-
current_group[annotation_name] ||= (v.is_a?(Enumerable) ? v.dup : v)
|
|
246
|
-
end
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
def remove_different_annotations(other)
|
|
252
|
-
return unless other.is_a?(self.class) && other.has_annotations?
|
|
253
|
-
@annotations ||= Hash.new
|
|
254
|
-
other.send(:annotations).each_pair do |atr, annotations_group|
|
|
255
|
-
include?(atr) and delete_annotation(atr)
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
end # module AttributeSet::Annotations
|
|
291
|
+
end # class AttributeSet
|
|
259
292
|
end # module ActiveModel
|