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.
Files changed (41) hide show
  1. data.tar.gz.sig +0 -0
  2. data/.yardopts +1 -0
  3. data/ChangeLog +501 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +7 -7
  6. data/Manifest.txt +7 -0
  7. data/README.md +59 -30
  8. data/Rakefile +1 -0
  9. data/attribute-filters.gemspec +4 -4
  10. data/docs/COMMON-FILTERS.md +1067 -0
  11. data/docs/HISTORY +42 -0
  12. data/docs/TODO +29 -12
  13. data/docs/USAGE.md +718 -818
  14. data/lib/attribute-filters.rb +4 -2
  15. data/lib/attribute-filters/attribute_set.rb +144 -73
  16. data/lib/attribute-filters/attribute_set_annotations.rb +110 -77
  17. data/lib/attribute-filters/attribute_set_attrquery.rb +51 -8
  18. data/lib/attribute-filters/attribute_set_enum.rb +44 -38
  19. data/lib/attribute-filters/attribute_set_query.rb +62 -12
  20. data/lib/attribute-filters/backports.rb +36 -4
  21. data/lib/attribute-filters/common_filters.rb +83 -37
  22. data/lib/attribute-filters/common_filters/bare.rb +29 -0
  23. data/lib/attribute-filters/common_filters/case.rb +34 -19
  24. data/lib/attribute-filters/common_filters/convert.rb +259 -0
  25. data/lib/attribute-filters/common_filters/join.rb +37 -30
  26. data/lib/attribute-filters/common_filters/order.rb +105 -0
  27. data/lib/attribute-filters/common_filters/pick.rb +90 -0
  28. data/lib/attribute-filters/common_filters/presence.rb +70 -0
  29. data/lib/attribute-filters/common_filters/split.rb +37 -21
  30. data/lib/attribute-filters/common_filters/squeeze.rb +28 -9
  31. data/lib/attribute-filters/common_filters/strip.rb +7 -3
  32. data/lib/attribute-filters/dsl_attr_virtual.rb +2 -1
  33. data/lib/attribute-filters/dsl_filters.rb +14 -61
  34. data/lib/attribute-filters/dsl_sets.rb +238 -88
  35. data/lib/attribute-filters/helpers.rb +7 -1
  36. data/lib/attribute-filters/meta_set.rb +38 -0
  37. data/lib/attribute-filters/version.rb +1 -1
  38. data/spec/attribute-filters_spec.rb +178 -16
  39. data/spec/spec_helper.rb +9 -4
  40. metadata +129 -69
  41. metadata.gz.sig +0 -0
@@ -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 < ::Set
14
+ class AttributeSet < Hash
17
15
  include ActiveModel::AttributeSet::Enumerable
18
- include ActiveModel::AttributeSet::Annotations
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 o [Object,Array] object to be added to set or array of objects
30
- # @return [AttributeSet,nil]
31
- def add(o)
32
- if o.is_a?(Array)
33
- o.map{ |e| super(e) }.compact
34
- else
35
- super
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
- # Merges the elements of the given enumerable
41
- # object to the attribute set and returns self.
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
- # @param o [Enumerable] object to be merged
44
- # @return [AttributeSet] current object
45
- def merge(o)
46
- r = super
47
- copy_annotations(o)
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
- # Copies internal structures.
87
+ # Subtracts the given set from the current one
88
+ # by removing all the elements that have the same keys.
52
89
  #
53
- # @param o [AttributeSet] other set to copy from
54
- # @return [AttributeSet] current attribute set
55
- def initialize_copy(o)
56
- r = super
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
- r = super
69
- if r.is_a?(self.class)
70
- r.send(:copy_annotations, self)
71
- r.send(:copy_annotations, o)
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
- # Deletes the given attribute name from the attribute set
78
- # and returns self.
116
+ # Returns a new attribute set containing elements
117
+ # exclusive between the set and the given enumerable object
118
+ # (exclusive disjuction).
79
119
  #
80
- # @note Use subtract to delete many items at once.
81
- # @param o [Symbol,String] attribute name to delete from set
82
- # @return [AttributeSet] current attribute set
83
- def delete(o)
84
- o = o.to_s
85
- r = super
86
- r.nil? or delete_annotation(o)
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
- # Deletes every attribute of the attribute set
91
- # for which the given block evaluates to +true+,
92
- # and returns self.
93
- #
94
- # @yield [o] block that controlls if an element should be deleted
95
- # @yieldparam o [String] current attribute
96
- # @return [AttributeSet] current attribute set
97
- def delete_if
98
- block_given? or return enum_for(__method__)
99
- super { |o| r = yield(o) and delete_annotation(o) ; r }
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
- # Deletes every attribute of the attribute set
103
- # for which the given block evaluates to +false+,
104
- # and returns self.
105
- #
106
- # @yield [o] block that controlls if an element should be kept
107
- # @yieldparam o [String] current attribute
108
- # @return [AttributeSet] current attribute set
109
- def keep_if
110
- block_given? or return enum_for(__method__)
111
- super { |o| r = yield(o) or delete_annotation(o) ; r }
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
- # Returns a new attribute set containing elements
115
- # exclusive between the set and the given enumerable object
116
- # (exlusive disjuction).
117
- #
118
- # @param o [Enumerable] object to exclusively disjunct with
119
- # @return [AttributeSet] resulting set
120
- def ^(o)
121
- n = self.class.new(o)
122
- n.remove_annotations
123
- each { |ob| if n.include?(ob) then n.delete(ob) else n.add(ob) end }
124
- n.send(:copy_annotations, self)
125
- n.send(:copy_annotations, o)
126
- n
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) # multiple sets defined
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.include?(first_arg)
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, :annotate_attribute_set
73
- alias_method :annotate_attributes_that, :annotate_attribute_set
74
- alias_method :annotate_attributes_are, :annotate_attribute_set
75
- alias_method :annotate_attributes_for, :annotate_attribute_set
76
- alias_method :annotate_attributes_set, :annotate_attribute_set
77
- alias_method :annotate_properties_that, :annotate_attribute_set
78
- alias_method :annotate_attributes, :annotate_attribute_set
79
- alias_method :attribute_set_annotate, :annotate_attribute_set
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 [void]
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.include?(set_name) && atr_name.present?
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 :unannotate_set, :delete_annotation_from_set
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
- module AttributeSet::Annotations
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 [void]
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 include?(atr_name)
185
+ unless key?(atr_name)
123
186
  raise ArgumentError, "attribute '#{atr_name}' must exist in order to annotate it"
124
187
  end
125
- @annotations ||= Hash.new
126
- @annotations[atr_name] ||= Hash.new
127
- @annotations[atr_name][name.to_sym] = value
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 annotations.nil? || annotations.empty?
213
+ return false if empty?
151
214
  return true if args.size == 0
152
215
  atr_name = args.shift.to_s
153
- a_group = annotations[atr_name]
154
- a_group.blank? and return false
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 = annotations[atr_name.to_s]
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 do |k, v|
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
- r = an_group[annotation_names.first.to_sym]
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 do |a|
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 @annotations.nil? || atr_name.blank?
259
+ return nil if atr_name.blank?
203
260
  atr_name = atr_name.to_s
204
- if annotation.nil?
205
- @annotations.delete(atr_name)
206
- elsif @annotations.has_key?(atr_name)
207
- @annotations[atr_name].delete(annotation.to_sym)
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
- nil
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 [void]
276
+ # @return [nil]
216
277
  def remove_annotations
217
- @annotations = nil
278
+ each_pair { |k, v| self[k] = true }
279
+ nil
218
280
  end
219
281
 
220
- private
221
-
282
+ # Returns all annotations that are set.
283
+ #
284
+ # @return [Hash{String => Hash}] annotations indexed by attribute names
222
285
  def annotations
223
- @annotations
224
- end
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
- def copy_missing_annotations(from, other)
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