attribute-filters 1.3.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
-