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
|
@@ -20,11 +20,20 @@ module ActiveModel
|
|
|
20
20
|
# @param set_object [AttributeSet] attribute set containing set names for which the query will be made
|
|
21
21
|
# @param am_object [Object] model object which has access to attributes (may be an instance of ActiveRecord or similar)
|
|
22
22
|
# @param attribute_name [Sting,Symbol] name of attribute the query is made for
|
|
23
|
-
def initialize(
|
|
24
|
-
|
|
23
|
+
def initialize(attribute_name = nil, am_object = nil)
|
|
24
|
+
if am_object.nil?
|
|
25
|
+
am_object = attribute_name
|
|
26
|
+
attribute_name = nil
|
|
27
|
+
unless am_object.included_modules.include?(::ActiveModel::AttributeFilters)
|
|
28
|
+
raise ::ArgumentError, "incompatible object passed to AttributeSet::AttrQuery (not a model class?)"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
25
31
|
@am_object = am_object
|
|
26
|
-
@attribute_name = attribute_name.to_s
|
|
27
32
|
@next_method = nil
|
|
33
|
+
unless attribute_name.nil?
|
|
34
|
+
@set_object = @am_object.class.filter_attribute(attribute_name)
|
|
35
|
+
@attribute_name = attribute_name.to_s
|
|
36
|
+
end
|
|
28
37
|
end
|
|
29
38
|
|
|
30
39
|
# This is a proxy method that causes some calls to be
|
|
@@ -50,7 +59,15 @@ module ActiveModel
|
|
|
50
59
|
# @param args [Array] optional arguments to be passed to a method call
|
|
51
60
|
# @yield optional block to be passed to a method call
|
|
52
61
|
def method_missing(method_sym, *args, &block)
|
|
53
|
-
|
|
62
|
+
method_sym = method_sym.to_sym
|
|
63
|
+
|
|
64
|
+
if @attribute_name.nil?
|
|
65
|
+
@attribute_name = method_sym.to_s
|
|
66
|
+
@set_object = @am_object.class.filter_attribute(@attribute_name)
|
|
67
|
+
return self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
case method_sym
|
|
54
71
|
when :are, :is, :one, :is_one, :in, :list, :be, :should,
|
|
55
72
|
:the, :a, :sets, :in_sets, :set, :in_a_set, :in_set, :belongs_to
|
|
56
73
|
self
|
|
@@ -63,16 +80,40 @@ module ActiveModel
|
|
|
63
80
|
@set_object.include?(*args, &block)
|
|
64
81
|
|
|
65
82
|
when :accessible?, :is_accessible?
|
|
66
|
-
@am_object.
|
|
83
|
+
@am_object.class.accessible_attributes.include?(@attribute_name)
|
|
67
84
|
|
|
68
85
|
when :inaccessible?, :is_inaccessible?
|
|
69
86
|
@am_object.all_inaccessible_attributes.include?(@attribute_name)
|
|
70
87
|
|
|
71
88
|
when :protected?, :is_protected?
|
|
72
|
-
@am_object.
|
|
89
|
+
@am_object.class.protected_attributes.include?(@attribute_name)
|
|
90
|
+
|
|
91
|
+
when :virtual?, :is_virtual?
|
|
92
|
+
@am_object.class.attribute_filters_virtual.include?(@attribute_name)
|
|
93
|
+
|
|
94
|
+
when :semi_real?, :is_semi_real?
|
|
95
|
+
@am_object.class.treat_as_real.include?(@attribute_name)
|
|
96
|
+
|
|
97
|
+
when :valid?, :is_valid?
|
|
98
|
+
return false unless @am_object.send(:__all_attributes).include?(@attribute_name)
|
|
99
|
+
return true if @am_object.valid?
|
|
100
|
+
not @am_object.errors.has_key?(@attribute_name.to_sym)
|
|
101
|
+
|
|
102
|
+
when :invalid?, :is_invalid?
|
|
103
|
+
return true unless @am_object.send(:__all_attributes).include?(@attribute_name)
|
|
104
|
+
return false if @am_object.valid?
|
|
105
|
+
@am_object.errors.has_key?(@attribute_name.to_sym)
|
|
106
|
+
|
|
107
|
+
when :changed?, :is_changed?, :has_changed?
|
|
108
|
+
return false unless @am_object.changed? && @am_object.send(:__all_attributes).include?(@attribute_name)
|
|
109
|
+
@am_object.changes.key?(@attribute_name)
|
|
110
|
+
|
|
111
|
+
when :unchanged?, :is_unchanged?, :hasnt_changed?, :not_changed?
|
|
112
|
+
return true unless @am_object.changed?
|
|
113
|
+
@am_object.changes.key?(@attribute_name)
|
|
73
114
|
|
|
74
115
|
else
|
|
75
|
-
set_name_str = method_sym.to_s
|
|
116
|
+
set_name_str = method_sym.to_s
|
|
76
117
|
if !@set_object.respond_to?(method_sym) && set_name_str.slice!(/\?\z/) == '?'
|
|
77
118
|
@set_object.include?(set_name_str.to_sym, &block)
|
|
78
119
|
else
|
|
@@ -96,7 +137,9 @@ module ActiveModel
|
|
|
96
137
|
:set, :in_a_set, :in_set, :in?, :in_set?, :in_a_set?, :in_the_set?, :the_set?, :set?,
|
|
97
138
|
:is_one_that?, :one_that?, :that?, :belongs_to?, :belongs_to,
|
|
98
139
|
:protected?, :is_protected?, :inaccessible?, :is_inaccessible?,
|
|
99
|
-
:accessible?, :is_accessible
|
|
140
|
+
:accessible?, :is_accessible?, :valid?, :is_valid?, :invalid?, :is_invalid?,
|
|
141
|
+
:virtual?, :is_virtual?, :semi_real?, :is_semi_real?,
|
|
142
|
+
:changed?, :has_changed?, :is_changed?, :unchanged?, :hasnt_changed?, :is_unchanged?, :not_changed?
|
|
100
143
|
true
|
|
101
144
|
else
|
|
102
145
|
@set_object.respond_to?(name) || name.to_s.slice(-1,1) == '?'
|
|
@@ -6,70 +6,76 @@
|
|
|
6
6
|
#
|
|
7
7
|
# This file contains AttributeSet::Enumerable module and AttributeSet::Enumerator class.
|
|
8
8
|
|
|
9
|
-
require 'set'
|
|
10
|
-
|
|
11
9
|
# This module adds some enumerable properties to AttributeSet objects.
|
|
12
10
|
module ActiveModel
|
|
13
|
-
class AttributeSet <
|
|
11
|
+
class AttributeSet < Hash
|
|
12
|
+
|
|
14
13
|
module Enumerable
|
|
15
14
|
# @private
|
|
16
|
-
def select
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
def select(&block)
|
|
16
|
+
block_given? or return AttributeSet::Enumerator.new(self, :select)
|
|
17
|
+
ActiveModel::AttributeSet.new.tap { |r| each_pair { |k, v| r[k] = v if yield(k, v) } }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Selects attributes that have setters and getters.
|
|
21
|
+
# @param binding [Object] optional object which should have setters and getters (default: the calling context)
|
|
22
|
+
# @return [AttributeSet::Enumerator, AttributeSet] resulting set or an enumerator if block is not given
|
|
23
|
+
def select_accessible(binding = nil)
|
|
24
|
+
if binding.nil?
|
|
25
|
+
select { |a| respond_to?(a) && respond_to?("#{a}=") }
|
|
22
26
|
else
|
|
23
|
-
|
|
27
|
+
select { |a| binding.respond_to?(a) && binding.respond_to?("#{a}=") }
|
|
24
28
|
end
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
# @private
|
|
28
32
|
def reject
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
33
|
+
block_given? or return AttributeSet::Enumerator.new(self, :reject)
|
|
34
|
+
ActiveModel::AttributeSet.new.tap { |r| each_pair { |k, v| r[k] = v unless yield(k, v) } }
|
|
37
35
|
end
|
|
38
36
|
|
|
39
37
|
# @private
|
|
40
38
|
def collect
|
|
41
|
-
|
|
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
|
|
39
|
+
block_given? ? super { |k, v| yield(k) } : AttributeSet::Enumerator.new(self, :map)
|
|
48
40
|
end
|
|
49
41
|
alias_method :map, :collect
|
|
50
42
|
|
|
51
43
|
# @private
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
r.send(:copy_annotations, self) unless r.empty?
|
|
55
|
-
r
|
|
44
|
+
def each
|
|
45
|
+
block_given? ? each_pair { |k, v| yield(k, v) } : AttributeSet::Enumerator.new(self, :each)
|
|
56
46
|
end
|
|
57
47
|
|
|
58
48
|
# @private
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
r.send(:copy_annotations, self) unless r.empty?
|
|
62
|
-
r
|
|
49
|
+
def each_pair
|
|
50
|
+
block_given? ? super : AttributeSet::Enumerator.new(self, :each_pair)
|
|
63
51
|
end
|
|
64
52
|
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
# Iterates through the name and value of each attribute found in a set.
|
|
54
|
+
# If the attribute has no getter method in the context of the given object, it is not evaluated.
|
|
55
|
+
#
|
|
56
|
+
# @param am_object [Object] active model object
|
|
57
|
+
# @param no_presence_check [Boolean] optional flag that if +true+ causes enumerator
|
|
58
|
+
# not to check if a getter method exists (defaults to +false+ – checks are done)
|
|
59
|
+
# @return [void,AttributeSet::Enumerator]
|
|
60
|
+
def each_name_value(am_object, no_presence_check = false)
|
|
61
|
+
block_given? or return AttributeSet::Enumerator.new(self, :each_name_value, am_object, no_presence_check)
|
|
62
|
+
if no_presence_check
|
|
63
|
+
each { |name| yield(name, am_object.public_send(name)) }
|
|
69
64
|
else
|
|
70
|
-
|
|
65
|
+
each { |name| yield(name, am_object.public_send(name)) if am_object.respond_to?(name) }
|
|
71
66
|
end
|
|
72
67
|
end
|
|
68
|
+
|
|
69
|
+
# @private
|
|
70
|
+
def sort
|
|
71
|
+
ActiveModel::AttributeSet[super]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @private
|
|
75
|
+
def sort_by
|
|
76
|
+
block_given? ? ActiveModel::AttributeSet[super] : AttributeSet::Enumerator.new(self, :sort_by)
|
|
77
|
+
end
|
|
78
|
+
|
|
73
79
|
end # module Enumerable
|
|
74
80
|
|
|
75
81
|
# This class adds enumerator for AttributeSet elements.
|
|
@@ -30,14 +30,19 @@ module ActiveModel
|
|
|
30
30
|
def initialize(set_object, am_object = nil)
|
|
31
31
|
if am_object.nil?
|
|
32
32
|
am_object = set_object
|
|
33
|
+
unless am_object.included_modules.include?(::ActiveModel::AttributeFilters)
|
|
34
|
+
raise ::ArgumentError, "incompatible object passed to AttributeSet::Query (not a model class?)"
|
|
35
|
+
end
|
|
33
36
|
set_object = ::ActiveModel::AttributeSet.new
|
|
34
37
|
end
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
|
|
39
|
+
if set_object.is_a?(::Symbol) || set_object.is_a?(::String) # global set assigned to model class
|
|
40
|
+
set_object = am_object.attribute_set_simple(set_object) # - duplicated in class method that gets a set
|
|
41
|
+
elsif !set_object.nil? && !set_object.is_a?(::ActiveModel::AttributeSet) # any other object
|
|
42
|
+
set_object = ::ActiveModel::AttributeSet.new(set_object) # - duplicated in AttributeSet initializer
|
|
39
43
|
end
|
|
40
|
-
|
|
44
|
+
|
|
45
|
+
@set_object = set_object # AttributeSet (assuming it's duplicated if needed)
|
|
41
46
|
@am_object = am_object
|
|
42
47
|
@next_method = nil
|
|
43
48
|
end
|
|
@@ -69,42 +74,76 @@ module ActiveModel
|
|
|
69
74
|
# @yield optional block to be passed to a method call or to a queued method call
|
|
70
75
|
# @return [Object] the returned value is passed back from called method
|
|
71
76
|
def method_missing(method_sym, *args, &block)
|
|
72
|
-
|
|
77
|
+
method_sym = method_sym.to_sym
|
|
78
|
+
|
|
79
|
+
# set name as a method name
|
|
80
|
+
if @set_object.nil?
|
|
81
|
+
@set_object = @am_object.class.attribute_set(method_sym)
|
|
82
|
+
return self
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# neutral method
|
|
86
|
+
case method_sym
|
|
73
87
|
when :are, :is, :be, :should
|
|
74
88
|
return self
|
|
75
89
|
end
|
|
76
|
-
|
|
90
|
+
|
|
91
|
+
# special selectors
|
|
77
92
|
if @next_method.nil?
|
|
78
|
-
case method_sym
|
|
93
|
+
case method_sym
|
|
94
|
+
|
|
79
95
|
when :all, :any, :none, :one
|
|
80
96
|
::ActiveModel::AttributeSet::Query.new(@set_object, @am_object). # new obj. == thread-safe
|
|
81
97
|
next_step(method_sym.to_s << "?", args, block)
|
|
98
|
+
|
|
82
99
|
when :list, :show
|
|
83
100
|
::ActiveModel::AttributeSet::Query.new(@set_object, @am_object).
|
|
84
101
|
next_step(:select, args, block)
|
|
102
|
+
|
|
85
103
|
when :valid?, :is_valid?, :are_valid?, :all_valid?, :are_all_valid?
|
|
86
104
|
self.all.valid?
|
|
105
|
+
|
|
87
106
|
when :invalid?, :is_not_valid?, :any_invalid?, :are_not_valid?, :not_valid?, :is_any_invalid?
|
|
88
107
|
self.any.invalid?
|
|
108
|
+
|
|
109
|
+
when :changed?, :any_changed?, :have_changed?, :have_any_changed?, :is_any_changed?, :has_any_changed?, :has_changed?
|
|
110
|
+
self.any.changed?
|
|
111
|
+
|
|
112
|
+
when :unchanged?, :none_changed?, :nothing_changed?, :is_unchanged?, :are_all_unchanged?,
|
|
113
|
+
:all_unchanged?, :havent_changed?, :arent_changed?, :are_not_changed?, :none_changed?, :not_changed?
|
|
114
|
+
self.all.unchanged?
|
|
115
|
+
|
|
89
116
|
else
|
|
90
117
|
r = @set_object.public_method(method_sym).call(*args, &block)
|
|
91
118
|
return r if r.respond_to?(:__in_as_proxy) || !r.is_a?(::ActiveModel::AttributeSet)
|
|
92
119
|
::ActiveModel::AttributeSet::Query.new(r, @am_object)
|
|
93
120
|
end
|
|
94
121
|
else
|
|
95
|
-
|
|
122
|
+
n_m, n_args, n_block = @next_method
|
|
96
123
|
@next_method = nil
|
|
97
124
|
# m contains a method that we should call on a set of names (e.g. all? or any?)
|
|
98
125
|
# method_sym contains a method to be called on each attribute from a set (e.g. present?)
|
|
99
126
|
r = case method_sym
|
|
127
|
+
|
|
100
128
|
when :valid?
|
|
101
129
|
@am_object.valid?
|
|
102
|
-
|
|
130
|
+
method_for_each_attr(n_m, *args) { |atr| not @am_object.errors.include?(atr.to_sym) }
|
|
131
|
+
|
|
103
132
|
when :invalid?
|
|
104
133
|
@am_object.valid?
|
|
105
|
-
|
|
134
|
+
method_for_each_attr(n_m, *args) { |atr| @am_object.errors.include?(atr.to_sym) }
|
|
135
|
+
|
|
136
|
+
when :changed?
|
|
137
|
+
method_for_each_attr(n_m, *args) { |atr| @am_object.changes.key?(atr) }
|
|
138
|
+
|
|
139
|
+
when :unchanged?
|
|
140
|
+
method_for_each_attr(n_m, *args) { |atr| not @am_object.changes.key?(atr) }
|
|
141
|
+
|
|
106
142
|
else
|
|
107
|
-
@set_object.public_method(
|
|
143
|
+
@set_object.public_method(n_m).call(*n_args) do |atr|
|
|
144
|
+
@am_object.public_send(atr).public_method(method_sym).call(*args, &block)
|
|
145
|
+
end
|
|
146
|
+
|
|
108
147
|
end
|
|
109
148
|
return r if r.respond_to?(:__in_as_proxy) || !r.is_a?(::ActiveModel::AttributeSet)
|
|
110
149
|
::ActiveModel::AttributeSet::Query.new(r, @am_object)
|
|
@@ -120,6 +159,11 @@ module ActiveModel
|
|
|
120
159
|
true
|
|
121
160
|
when :valid?, :is_valid?, :are_valid?, :all_valid?, :are_all_valid?
|
|
122
161
|
true
|
|
162
|
+
when :changed?, :any_changed?, :have_changed?, :have_any_changed?, :is_any_changed?, :has_any_changed?, :has_changed?
|
|
163
|
+
true
|
|
164
|
+
when :unchanged?, :none_changed?, :nothing_changed?, :is_unchanged?, :are_all_unchanged?,
|
|
165
|
+
:all_unchanged?, :havent_changed?, :arent_changed?, :are_not_changed?, :none_changed?, :not_changed?
|
|
166
|
+
true
|
|
123
167
|
else
|
|
124
168
|
@set_object.respond_to?(name)
|
|
125
169
|
end
|
|
@@ -185,6 +229,12 @@ module ActiveModel
|
|
|
185
229
|
@next_method = [method_name, args, block]
|
|
186
230
|
return self
|
|
187
231
|
end
|
|
232
|
+
|
|
233
|
+
private
|
|
234
|
+
|
|
235
|
+
def method_for_each_attr(m, *args, &block)
|
|
236
|
+
@set_object.public_method(m).call(*args, &block)
|
|
237
|
+
end
|
|
188
238
|
end # class Query
|
|
189
239
|
end # class AttributeSet
|
|
190
240
|
end # module ActiveModel
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
class Object
|
|
12
12
|
|
|
13
13
|
unless method_defined?(:public_send)
|
|
14
|
-
|
|
15
14
|
# @private
|
|
16
15
|
def public_send(name, *args)
|
|
17
16
|
unless public_methods.include?(name.to_s)
|
|
@@ -19,11 +18,9 @@ class Object
|
|
|
19
18
|
end
|
|
20
19
|
send(name, *args)
|
|
21
20
|
end
|
|
22
|
-
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
unless method_defined?(:public_method)
|
|
26
|
-
|
|
27
24
|
# @private
|
|
28
25
|
def public_method(name)
|
|
29
26
|
unless public_methods.include?(name.to_s)
|
|
@@ -31,7 +28,42 @@ class Object
|
|
|
31
28
|
end
|
|
32
29
|
method(name)
|
|
33
30
|
end
|
|
34
|
-
|
|
35
31
|
end
|
|
36
32
|
|
|
37
33
|
end # class Object
|
|
34
|
+
|
|
35
|
+
# @private
|
|
36
|
+
# @abstract This class is here for compatibility reasons.
|
|
37
|
+
class Hash
|
|
38
|
+
|
|
39
|
+
# @private
|
|
40
|
+
unless method_defined?(:deep_merge)
|
|
41
|
+
def deep_merge(o)
|
|
42
|
+
dup.deep_merge!(o)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @private
|
|
47
|
+
unless method_defined?(:deep_merge!)
|
|
48
|
+
def deep_merge!(other_hash)
|
|
49
|
+
other_hash.each_pair do |k, ov|
|
|
50
|
+
my_v = self[k]
|
|
51
|
+
self[k] = my_v.is_a?(Hash) && ov.is_a?(Hash) ? my_v.deep_merge(ov) : ov
|
|
52
|
+
end
|
|
53
|
+
self
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @private
|
|
58
|
+
unless method_defined?(:deep_dup)
|
|
59
|
+
def deep_dup
|
|
60
|
+
duplicate = self.dup
|
|
61
|
+
duplicate.each_pair do |k, ov|
|
|
62
|
+
my_v = duplicate[k]
|
|
63
|
+
duplicate[k] = my_v.is_a?(Hash) && ov.is_a?(Hash) ? my_v.deep_dup : ov
|
|
64
|
+
end
|
|
65
|
+
duplicate
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end # class Hash
|
|
@@ -11,63 +11,109 @@
|
|
|
11
11
|
module ActiveModel
|
|
12
12
|
module AttributeFilters
|
|
13
13
|
|
|
14
|
-
# This
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
respond_to?(:upcase_attributes) and upcase_attributes
|
|
33
|
-
respond_to?(:downcase_attributes) and downcase_attributes
|
|
34
|
-
respond_to?(:capitalize_attributes) and capitalize_attributes
|
|
35
|
-
respond_to?(:fully_capitalize_attributes) and fully_capitalize_attributes
|
|
36
|
-
respond_to?(:titleize_attributes) and titleize_attributes
|
|
37
|
-
end
|
|
14
|
+
# This module holds method for marking methods as filters.
|
|
15
|
+
module FilteringRegistration
|
|
16
|
+
# This method marks a method as filtering method
|
|
17
|
+
# and associates it with the given name of an attribute set it uses.
|
|
18
|
+
#
|
|
19
|
+
# @param method_name [Symbol] name of a method
|
|
20
|
+
# @param set_name [Symbol] name of an attribute set
|
|
21
|
+
# @return [void]
|
|
22
|
+
def filtering_method(method_name, set_name)
|
|
23
|
+
set_name = set_name.to_sym
|
|
24
|
+
return unless method_defined?(method_name)
|
|
25
|
+
f = (@__filtering_sets ||= MetaSet.new)
|
|
26
|
+
f[set_name] = method_name unless f.key?(set_name)
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
alias_method :has_filtering_method, :filtering_method
|
|
30
|
+
|
|
31
|
+
end # module FilteringRegistration
|
|
38
32
|
|
|
39
33
|
# This module contains common, ready-to-use filtering methods.
|
|
40
34
|
module Common
|
|
41
|
-
|
|
42
35
|
# @private
|
|
43
36
|
module CommonFilter
|
|
37
|
+
include FilteringRegistration
|
|
38
|
+
|
|
44
39
|
# @private
|
|
45
40
|
def included(base)
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
|
|
42
|
+
# merge filtering sets from filtering modules to models
|
|
43
|
+
fs = @__filtering_sets
|
|
44
|
+
base.class_eval { (@__filtering_sets ||= MetaSet.new).merge!(fs) } unless fs.nil?
|
|
45
|
+
|
|
46
|
+
if base.const_defined?(:ClassMethods) &&
|
|
47
|
+
base.instance_of?(::Module) &&
|
|
48
|
+
base.const_get(:ClassMethods).instance_of?(::Module)
|
|
49
|
+
|
|
50
|
+
# If we're here then this module was included by some other module
|
|
51
|
+
# (one of our own filtering submodules).
|
|
52
|
+
base.class_eval <<-EVAL
|
|
53
|
+
unless singleton_class.method_defined?(:included)
|
|
54
|
+
def self.included(base)
|
|
55
|
+
fs = @__filtering_sets
|
|
56
|
+
base.class_eval { (@__filtering_sets ||= MetaSet.new).merge!(fs) } unless fs.nil?
|
|
57
|
+
base.extend ClassMethods
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
module ClassMethods
|
|
61
|
+
include #{self.name}::ClassMethods
|
|
62
|
+
end
|
|
63
|
+
EVAL
|
|
64
|
+
|
|
48
65
|
else
|
|
66
|
+
|
|
67
|
+
# If we're here then this module was included
|
|
68
|
+
# by some model or other class.
|
|
49
69
|
base.extend ClassMethods
|
|
70
|
+
|
|
50
71
|
end
|
|
51
72
|
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
extend CommonFilter
|
|
73
|
+
end # module CommonFilter
|
|
55
74
|
|
|
56
75
|
# This module contains class methods used by the DSL
|
|
57
|
-
# to create keywords for common operations.
|
|
76
|
+
# to create keywords for common filtering operations.
|
|
77
|
+
# These keywords can be used in filtering submodules
|
|
78
|
+
# and in any model that includes them.
|
|
58
79
|
module ClassMethods
|
|
59
80
|
end
|
|
60
81
|
|
|
82
|
+
extend CommonFilter
|
|
83
|
+
|
|
61
84
|
# Include all default filters
|
|
62
85
|
# that should be available
|
|
63
86
|
# when Common module is included.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
require 'attribute-filters/common_filters/squeeze'
|
|
68
|
-
require 'attribute-filters/common_filters/split'
|
|
69
|
-
require 'attribute-filters/common_filters/join'
|
|
87
|
+
Dir[File.join(File.dirname(__FILE__), 'common_filters', '*.rb')].each do |f|
|
|
88
|
+
require f
|
|
89
|
+
end
|
|
70
90
|
|
|
71
91
|
end # module Common
|
|
92
|
+
|
|
93
|
+
# This method is a callback method that tries to call all
|
|
94
|
+
# known filtering methods if they are in use.
|
|
95
|
+
#
|
|
96
|
+
# Calling order depends on sets registering order.
|
|
97
|
+
#
|
|
98
|
+
# @return [nil]
|
|
99
|
+
def filter_attributes
|
|
100
|
+
as, fs = *self.class.class_eval { [__attribute_sets, @__filtering_sets] }
|
|
101
|
+
return if fs.blank? || as.blank?
|
|
102
|
+
as.each_pair { |set_name, o| send(fs[set_name]) if fs.has_key?(set_name) }
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Gets a list of filtering hooks that are in use.
|
|
107
|
+
#
|
|
108
|
+
# @return [MetaSet{Symbol => Symbol}] a meta set of filtering methods and associated sets
|
|
109
|
+
def filtering_methods
|
|
110
|
+
f = self.class.instance_variable_get(:@__filtering_sets)
|
|
111
|
+
f.nil? ? ActiveModel::MetaSet.new : f.dup
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
module ClassMethods
|
|
115
|
+
include FilteringRegistration
|
|
116
|
+
end
|
|
117
|
+
|
|
72
118
|
end # module AttributeFilters
|
|
73
119
|
end # module ActiveModel
|