attribute-filters 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,10 @@
2
2
 
3
3
  require 'attribute-filters'
4
4
  require 'attribute-filters/version'
5
+ require 'attribute-filters/backports'
6
+
5
7
  require 'attribute-filters/attribute_set_enum'
8
+ require 'attribute-filters/attribute_set_annotations'
6
9
  require 'attribute-filters/attribute_set'
7
10
  require 'attribute-filters/attribute_set_query'
8
11
  require 'attribute-filters/attribute_set_attrquery'
@@ -10,6 +13,7 @@ require 'attribute-filters/attribute_set_attrquery'
10
13
  require 'attribute-filters/helpers'
11
14
  require 'attribute-filters/dsl_sets'
12
15
  require 'attribute-filters/dsl_filters'
16
+ require 'attribute-filters/dsl_attr_virtual'
13
17
  require 'attribute-filters/common_filters'
14
18
 
15
19
  if defined? ::Rails
@@ -14,7 +14,9 @@ module ActiveModel
14
14
  # This class is a data structure used to store
15
15
  # set of attributes.
16
16
  class AttributeSet < ::Set
17
- include AttributeSetEnumerable
17
+ include ActiveModel::AttributeSet::Enumerable
18
+ include ActiveModel::AttributeSet::Annotations
19
+
18
20
  # Adds the given object to the set and returns self.
19
21
  # If the object is already in the set, returns nil.
20
22
  # If the object is an array it adds each element of the array.
@@ -34,5 +36,94 @@ module ActiveModel
34
36
  end
35
37
  end
36
38
  alias_method :<<, :add
39
+
40
+ # Merges the elements of the given enumerable
41
+ # object to the attribute set and returns self.
42
+ #
43
+ # @param o [Enumerable] object to be merged
44
+ # @return [AttributeSet] current object
45
+ def merge(o)
46
+ r = super
47
+ copy_annotations(o)
48
+ r
49
+ end
50
+
51
+ # Copies internal structures.
52
+ #
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
60
+ end
61
+
62
+ # Returns a new attribute set containing elements
63
+ # common to the attribute set and the given enumerable object.
64
+ #
65
+ # @param o [Enumerable] object to intersect with
66
+ # @return [AttributeSet] intersection of objects
67
+ def &(o)
68
+ r = super
69
+ if r.is_a?(self.class)
70
+ r.send(:copy_annotations, self)
71
+ r.send(:copy_annotations, o)
72
+ end
73
+ r
74
+ end
75
+ alias_method :intersection, :&
76
+
77
+ # Deletes the given attribute name from the attribute set
78
+ # and returns self.
79
+ #
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)
87
+ r
88
+ end
89
+
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 }
100
+ end
101
+
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 }
112
+ end
113
+
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
127
+ end
37
128
  end
38
129
  end
@@ -0,0 +1,259 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
4
+ # Copyright:: (c) 2012 by Paweł Wilk
5
+ # License:: This program is licensed under the terms of {file:LGPL-LICENSE GNU Lesser General Public License} or {file:COPYING Ruby License}.
6
+ #
7
+ # This file contains ActiveModel::AttributeSet annotation methods.
8
+ # Annotations are additional values assigned to attribute names in a set.
9
+ # They are halpful when there is a need to memorize some additional properties or operations.
10
+
11
+ # This module contains annotations support for AttributeSet.
12
+ # It is included in AttirbuteSet class automatically
13
+ # and allows adding, deleting and removing annotations
14
+ # to certain elements of attribute sets. One element (attribute name)
15
+ module ActiveModel
16
+ module AttributeFilters
17
+ module ClassMethods
18
+ # This method is a wrapper that helps to annotate attributes.
19
+ #
20
+ # @overload annotate_attribute_set(set_name, attribute_name, annotation_key, value)
21
+ # @param set_name [Symbol,String] name of a set
22
+ # @param attribute_name [Symbol,String] name of annotated attribute
23
+ # @param annotation_key [Symbol,String] name of annotation key
24
+ # @param value [Object] annotation value
25
+ #
26
+ # @overload annotate_attribute_set(set_name, attribute_name, annotation)
27
+ # @param set_name [Symbol,String] name of a set
28
+ # @param attribute_name [Symbol,String] name of annotated attribute
29
+ # @param annotation [Hash{Symbol => Object}] annotation key => value pairs
30
+ #
31
+ # @overload annotate_attribute_set(set_name, annotation)
32
+ # @param set_name [Symbol,String] name of a set
33
+ # @param annotation [Hash{Symbol => Array<Symbol,Object>}] annotation key and value assigned to attribute name
34
+ #
35
+ # @overload annotate_attribute_set(set_name, *annotation)
36
+ # @param set_name [Symbol,String] name of a set
37
+ # @param annotation [Array<Symbol,Symbol,Object>}] attribute name, annotation key and value
38
+ #
39
+ # @overload annotate_attribute_set(set_name, annotations)
40
+ # @param set_name [Symbol,String] name of a set
41
+ # @param annotations [Hash{Symbol => Hash{Symbol => Object}}] annotation key => value pairs for attributes
42
+ def annotate_attribute_set(*args)
43
+ first_arg = args.shift
44
+ if first_arg.is_a?(Hash) # multiple sets defined
45
+ first_arg.each_pair do |k, v|
46
+ annotate_attribute_set(k, v, *args)
47
+ end
48
+ else
49
+ atr_name, an_key, an_val = args
50
+ if atr_name.is_a?(Hash)
51
+ atr_name.each_pair do |k, v|
52
+ annotate_attribute_set(first_arg, k, v)
53
+ end
54
+ elsif atr_name.is_a?(Array)
55
+ annotate_attribute_set(first_arg, *atr_name)
56
+ elsif an_key.is_a?(Hash)
57
+ an_key.each_pair do |k, v|
58
+ annotate_attribute_set(first_arg, atr_name, k, v)
59
+ end
60
+ else
61
+ unless an_key.nil? || atr_name.nil?
62
+ first_arg = first_arg.to_sym
63
+ unless __attribute_sets.include?(first_arg)
64
+ raise ArgumentError, "trying to annotate non-existent set '#{first_arg}'"
65
+ end
66
+ __attribute_sets[first_arg].annotate(atr_name, an_key, an_val)
67
+ end
68
+ end
69
+ end
70
+ nil
71
+ 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
+
81
+ # Deletes annotaion from a given set
82
+ #
83
+ # @param set_name [String,Symbol] set name
84
+ # @param atr_name [String,Symbol] attribute name
85
+ # @param annotations [Array<String,Symbol>] annotation keys
86
+ #
87
+ # @return [void]
88
+ def delete_annotation_from_set(set_name, atr_name = nil, *annotations)
89
+ if set_name.is_a?(Hash)
90
+ r = {}
91
+ set_name.each_pair do |k_set, v_attrs|
92
+ if v_attrs.is_a?(Hash)
93
+ v_attrs.each_pair do |k_attr, v_annotations|
94
+ delete_annotation_from_set(k_set, k_attr, *v_annotations)
95
+ end
96
+ else
97
+ delete_annotation_from_set(k_set, v_attrs, *annotations)
98
+ end
99
+ end
100
+ else
101
+ set_name = set_name.to_sym
102
+ return unless __attribute_sets.include?(set_name) && atr_name.present?
103
+ atr_name = atr_name.to_sym
104
+ __attribute_sets[set_name].delete_annotation(atr_name, *annotations)
105
+ end
106
+ end
107
+ alias_method :delete_annotations_from_set, :delete_annotation_from_set
108
+ alias_method :unannotate_set, :delete_annotation_from_set
109
+ end # module ClassMethods
110
+ end # module AttributeFilters
111
+
112
+ module AttributeSet::Annotations
113
+ # Adds an annotation to the given attribute.
114
+ #
115
+ # @param atr_name [String,Symbol] attribute name
116
+ # @param name [String,Symbol] annotation key
117
+ # @param value [Object] annotation value
118
+ # @raise [ArgumentError] when the given attribute name does not exist in a set
119
+ # @return [void]
120
+ def annotate(atr_name, name, value)
121
+ atr_name = atr_name.to_s unless atr_name.blank?
122
+ unless include?(atr_name)
123
+ raise ArgumentError, "attribute '#{atr_name}' must exist in order to annotate it"
124
+ end
125
+ @annotations ||= Hash.new
126
+ @annotations[atr_name] ||= Hash.new
127
+ @annotations[atr_name][name.to_sym] = value
128
+ end
129
+ alias_method :add_op, :annotate
130
+ alias_method :bind_op, :annotate
131
+
132
+ # Tests if an annotation of the given name exists in a set or if set has annotations.
133
+ #
134
+ # @overload has_annotation?
135
+ # Tests if set has any annotations.
136
+ # @return [Boolean] +true+ if the current set has any annotations, +false+ otherwise
137
+ #
138
+ # @overload has_annotation?(attribute_name)
139
+ # Tests if any annotation key for the attribute of the given name exists in a set.
140
+ # @param attribute_name [Symbol,String] name of an attribute
141
+ # @return [Boolean] +true+ if the current set has any annotations for +attribute_name+, +false+ otherwise
142
+ #
143
+ # @overload has_annotation?(attribute_name, *annotation_keys)
144
+ # Tests if any of the annotation keys for the attribute of the given name exists in a set.
145
+ # @param attribute_name [Symbol,String] name of an attribute
146
+ # @param annotation_keys [Array<String,Symbol>] annotation key names to check
147
+ # @return [Boolean] +true+ if the current set has at least one of the given +annotation_keys+ for +attribute_name+,
148
+ # +false+ otherwise
149
+ def has_annotation?(*args)
150
+ return false if annotations.nil? || annotations.empty?
151
+ return true if args.size == 0
152
+ atr_name = args.shift.to_s
153
+ a_group = annotations[atr_name]
154
+ a_group.blank? and return false
155
+ args.empty? and return true
156
+ args.any? { |a_name| a_group.key?(a_name.to_sym) }
157
+ end
158
+ alias_method :has_annotations?, :has_annotation?
159
+
160
+ # Gets an annotation for the specified attribute.
161
+ # If the second argument is ommited, it returns
162
+ # all annotations for the specified attribute.
163
+ #
164
+ # @param atr_name [String,Symbol] attribute name
165
+ # @param annotation_names [Array<String,Symbol>] optional annotation key(s)
166
+ # @return [Object,Hash,nil] duplicate of annotations hash, value of a single annotation or +nil+ if not found,
167
+ # or array of values (filled with +nil+ objects if not found)
168
+ def annotation(atr_name, *annotation_names)
169
+ atr_name.present? or return nil
170
+ has_annotations? or return nil
171
+ an_group = annotations[atr_name.to_s]
172
+ return nil if an_group.nil?
173
+ case annotation_names.size
174
+ when 0
175
+ r = Hash.new
176
+ an_group.each_pair do |k, v|
177
+ r[k] = v.is_a?(Enumerable) ? v.dup : v
178
+ end
179
+ r
180
+ when 1
181
+ r = an_group[annotation_names.first.to_sym]
182
+ r.is_a?(Enumerable) ? r.dup : r
183
+ else
184
+ annotation_names.map do |a|
185
+ r = an_group[a.to_sym]
186
+ r.is_a?(Enumerable) ? r.dup : r
187
+ end
188
+ end
189
+ end
190
+ alias_method :get_annotation, :annotation
191
+ alias_method :get_annotations, :annotation
192
+
193
+ # Deletes annotations or single annotation key for the given attribute.
194
+ # If the +annotation+ argument is not given or is +nil+
195
+ # then all annotation keys for the given attribute name are deleted.
196
+ #
197
+ # @param atr_name [String,Symbol] attribute name
198
+ # @param annotation [String,Symbol] annotation key
199
+ # @return [Hash,Object,nil] deleted annotations (hash),
200
+ # deleted annotation value or +nil+ if there wasn't anything to delete
201
+ def delete_annotation(atr_name, annotation = nil)
202
+ return nil if @annotations.nil? || atr_name.blank?
203
+ 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)
208
+ else
209
+ nil
210
+ end
211
+ end
212
+ alias_method :delete_annotations, :delete_annotation
213
+
214
+ # Removes all annotations.
215
+ # @return [void]
216
+ def remove_annotations
217
+ @annotations = nil
218
+ end
219
+
220
+ private
221
+
222
+ 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
236
+ end
237
+ end
238
+
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
259
+ end # module ActiveModel
@@ -76,11 +76,19 @@ module ActiveModel
76
76
  if !@set_object.respond_to?(method_sym) && set_name_str.slice!(/\?\z/) == '?'
77
77
  @set_object.include?(set_name_str.to_sym, &block)
78
78
  else
79
- @set_object.method(method_sym).call(*args, &block)
79
+ @set_object.public_method(method_sym).call(*args, &block)
80
80
  end
81
81
  end
82
82
  end
83
83
 
84
+ # Gets value of current attribute.
85
+ # If the attribute does not exist it returns +nil+.
86
+ #
87
+ # @return [Object] the value of an attribute
88
+ def value
89
+ @am_object.respond_to?(@attribute_name) ? @am_object.public_send(@attribute_name) : nil
90
+ end
91
+
84
92
  # @private
85
93
  def respond_to?(name)
86
94
  case name.to_sym
@@ -94,13 +102,6 @@ module ActiveModel
94
102
  @set_object.respond_to?(name) || name.to_s.slice(-1,1) == '?'
95
103
  end
96
104
  end
97
-
98
- # @private
99
- def is_a?(klass)
100
- super || @set_object.is_a?(klass)
101
- end
102
- alias_method :kind_of?, :is_a?
103
-
104
105
  end # class AttrQuery
105
106
  end # class AttributeSet
106
107
  end # module ActiveModel
@@ -4,57 +4,88 @@
4
4
  # Copyright:: (c) 2012 by Paweł Wilk
5
5
  # License:: This program is licensed under the terms of {file:LGPL-LICENSE GNU Lesser General Public License} or {file:COPYING Ruby License}.
6
6
  #
7
- # This file contains AttributeSetEnumerable module and AttributeSetEnumerator class.
7
+ # This file contains AttributeSet::Enumerable module and AttributeSet::Enumerator class.
8
+
9
+ require 'set'
8
10
 
9
11
  # This module adds some enumerable properties to AttributeSet objects.
10
- module AttributeSetEnumerable
11
- # @private
12
- def select
13
- if block_given?
14
- ActiveModel::AttributeSet.new.tap do |r|
15
- each { |e| r << e if yield(e) }
12
+ module ActiveModel
13
+ class AttributeSet < ::Set
14
+ module Enumerable
15
+ # @private
16
+ def select
17
+ if block_given?
18
+ ActiveModel::AttributeSet.new.tap do |r|
19
+ each { |e| r << e if yield(e) }
20
+ r.send(:copy_annotations, self) unless r.empty?
21
+ end
22
+ else
23
+ AttributeSet::Enumerator.new(self, :select)
24
+ end
25
+ end
26
+
27
+ # @private
28
+ def reject
29
+ if block_given?
30
+ ActiveModel::AttributeSet.new.tap do |r|
31
+ each { |e| r << e unless yield(e) }
32
+ r.send(:copy_annotations, self) unless r.empty?
33
+ end
34
+ else
35
+ AttributeSet::Enumerator.new(self, :reject)
36
+ end
16
37
  end
17
- else
18
- AttributeSetEnumerator.new(self, :select)
19
- end
20
- end
21
-
22
- # @private
23
- def reject
24
- if block_given?
25
- ActiveModel::AttributeSet.new.tap do |r|
26
- each { |e| r << e unless yield(e) }
38
+
39
+ # @private
40
+ def collect
41
+ if block_given?
42
+ ActiveModel::AttributeSet.new.tap do |r|
43
+ each { |e| r << yield(e) }
44
+ end
45
+ else
46
+ AttributeSet::Enumerator.new(self, :map)
47
+ end
48
+ end
49
+ alias_method :map, :collect
50
+
51
+ # @private
52
+ def sort
53
+ r = ActiveModel::AttributeSet.new(super)
54
+ r.send(:copy_annotations, self) unless r.empty?
55
+ r
27
56
  end
28
- else
29
- AttributeSetEnumerator.new(self, :reject)
30
- end
31
- end
32
-
33
- # @private
34
- def collect
35
- if block_given?
36
- ActiveModel::AttributeSet.new.tap do |r|
37
- each { |e| r << yield(e) }
57
+
58
+ # @private
59
+ def sort_by
60
+ r = ActiveModel::AttributeSet.new(super)
61
+ r.send(:copy_annotations, self) unless r.empty?
62
+ r
38
63
  end
39
- else
40
- AttributeSetEnumerator.new(self, :map)
41
- end
42
- end
43
- alias_method :map, :collect
44
-
45
- # @private
46
- def sort
47
- ActiveModel::AttributeSet.new(super)
48
- end
49
-
50
- # @private
51
- def sort_by
52
- ActiveModel::AttributeSet.new(super)
53
- end
64
+
65
+ # @private
66
+ def each
67
+ if block_given?
68
+ super
69
+ else
70
+ AttributeSet::Enumerator.new(self, :each)
71
+ end
72
+ end
73
+ end # module Enumerable
74
+
75
+ # This class adds enumerator for AttributeSet elements.
76
+ class Enumerator < ::Enumerator
77
+ include ActiveModel::AttributeSet::Enumerable
78
+ end # class Enumerator
79
+
80
+ end # class AttributeSet
81
+ end # module ActiveModel
82
+
83
+ # @abstract This module is here for compatibility reasons.
84
+ module AttributeSetEnumerable
85
+ include ActiveModel::AttributeSet::Enumerable
54
86
  end
55
87
 
56
- # This class adds enumerator for AttributeSet elements.
57
- class AttributeSetEnumerator < ::Enumerator
88
+ # @abstract This class is here for compatibility reasons.
89
+ class AttributeSetEnumerator < ActiveModel::AttributeSet::Enumerator
58
90
  include AttributeSetEnumerable
59
91
  end
60
-