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.
- data.tar.gz.sig +0 -0
- data/ChangeLog +388 -0
- data/Gemfile.lock +14 -14
- data/Manifest.txt +8 -0
- data/README.md +45 -31
- data/attribute-filters.gemspec +3 -3
- data/docs/HISTORY +63 -27
- data/docs/TODO +17 -2
- data/docs/USAGE.md +1161 -76
- data/lib/attribute-filters.rb +4 -0
- data/lib/attribute-filters/attribute_set.rb +92 -1
- data/lib/attribute-filters/attribute_set_annotations.rb +259 -0
- data/lib/attribute-filters/attribute_set_attrquery.rb +9 -8
- data/lib/attribute-filters/attribute_set_enum.rb +76 -45
- data/lib/attribute-filters/attribute_set_query.rb +65 -7
- data/lib/attribute-filters/backports.rb +37 -0
- data/lib/attribute-filters/common_filters.rb +46 -128
- data/lib/attribute-filters/common_filters/case.rb +159 -0
- data/lib/attribute-filters/common_filters/join.rb +98 -0
- data/lib/attribute-filters/common_filters/split.rb +130 -0
- data/lib/attribute-filters/common_filters/squeeze.rb +75 -0
- data/lib/attribute-filters/common_filters/strip.rb +46 -0
- data/lib/attribute-filters/dsl_attr_virtual.rb +50 -0
- data/lib/attribute-filters/dsl_filters.rb +40 -29
- data/lib/attribute-filters/dsl_sets.rb +122 -32
- data/lib/attribute-filters/helpers.rb +14 -0
- data/lib/attribute-filters/version.rb +1 -1
- data/spec/attribute-filters_spec.rb +334 -13
- data/spec/spec_helper.rb +9 -20
- metadata +37 -29
- metadata.gz.sig +0 -0
@@ -0,0 +1,98 @@
|
|
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::AttributeFilters::Common::Join module
|
8
|
+
# containing ready-to-use filtering method.
|
9
|
+
|
10
|
+
# @abstract This namespace is shared with ActveModel.
|
11
|
+
module ActiveModel
|
12
|
+
module AttributeFilters
|
13
|
+
# This module contains common, ready-to-use filtering methods.
|
14
|
+
module Common
|
15
|
+
# Joins attributes.
|
16
|
+
module Join
|
17
|
+
extend CommonFilter
|
18
|
+
|
19
|
+
# Joins attributes and writes the results into other attribute.
|
20
|
+
#
|
21
|
+
# The attrubutes to be destination for joins are taken from the attribute set
|
22
|
+
# called +should_be_joined+.
|
23
|
+
#
|
24
|
+
# The pattern used to join a string and the optional separator argument
|
25
|
+
# should be set using the model's class method +join_attribute+.
|
26
|
+
def join_attributes
|
27
|
+
filter_attrs_from_set(:should_be_joined, :process_all, :process_blank) do |atr_val, atr_name, set_obj|
|
28
|
+
from, compact = set_obj.annotation(atr_name, :join_from, :join_compact)
|
29
|
+
if from.blank?
|
30
|
+
next atr_val if !atr_val.is_a?(Array)
|
31
|
+
from = [ atr_name ]
|
32
|
+
elsif !from.is_a?(Array)
|
33
|
+
from = [ from ]
|
34
|
+
end
|
35
|
+
vals = AttributeSet::Query.new(from, self).values
|
36
|
+
separator = set_obj.has_annotation?(atr_name, :join_separator) ?
|
37
|
+
set_obj.annotation(atr_name, :join_separator) : " "
|
38
|
+
if compact && vals.respond_to?(:compact)
|
39
|
+
vals.compact.join(separator)
|
40
|
+
elsif vals.respond_to?(:join)
|
41
|
+
vals.join(separator)
|
42
|
+
else
|
43
|
+
vals
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# This submodule contains class methods needed to describe
|
49
|
+
# attribute joining.
|
50
|
+
module ClassMethods
|
51
|
+
# This method parametrizes joining operation for an attribute of the given name.
|
52
|
+
# It uses attribute set annotations to register parameters used when joining.
|
53
|
+
#
|
54
|
+
# @param atr_name [String,Symbol] attribute name
|
55
|
+
# @param parameters [Hash] parameters hash # fixme: add YARD parameters explained
|
56
|
+
# @return [void]
|
57
|
+
def join_attribute(atr_name, parameters = nil)
|
58
|
+
atr_name.is_a?(Hash) and return atr_name.each_pair { |k, v| join_attribute(k, v) }
|
59
|
+
if atr_name.is_a?(Array)
|
60
|
+
if parameters.is_a?(Symbol) || parameters.is_a?(String)
|
61
|
+
return join_attribute(parameters, atr_name)
|
62
|
+
elsif parameters.is_a?(Hash)
|
63
|
+
dst = parameters.delete(:into) || parameters.delete(:in) || parameters.delete(:destination)
|
64
|
+
if dst.nil?
|
65
|
+
raise ArgumentError, "you have to specify destination attribute using :into => 'attribute_name'"
|
66
|
+
end
|
67
|
+
parameters[:from] = atr_name
|
68
|
+
return join_attribute(dst, parameters)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
parameters = { :from => parameters } unless parameters.is_a?(Hash)
|
72
|
+
the_attribute(atr_name, :should_be_joined)
|
73
|
+
a = {}
|
74
|
+
if parameters.key?(:with)
|
75
|
+
a[:join_separator] = parameters[:with]
|
76
|
+
elsif parameters.key?(:separator)
|
77
|
+
a[:join_separator] = parameters[:separator]
|
78
|
+
elsif parameters.key?(:join_separator)
|
79
|
+
a[:join_separator] = parameters[:join_separator]
|
80
|
+
end
|
81
|
+
from = parameters[:from] || parameters[:source] || parameters[:sources] || parameters[:join_from]
|
82
|
+
compact = parameters.key?(:compact) ? !!parameters[:compact] : !!parameters[:join_compact]
|
83
|
+
a.merge!({ :join_compact => compact, :join_from => from })
|
84
|
+
annotate_attributes_that(:should_be_joined, atr_name => a)
|
85
|
+
end
|
86
|
+
alias_method :join_attributes, :join_attribute
|
87
|
+
alias_method :joint_attribute, :join_attribute
|
88
|
+
alias_method :joint_attributes, :join_attribute
|
89
|
+
alias_method :join_attributes_to, :join_attribute
|
90
|
+
alias_method :join_attributes_into, :join_attribute
|
91
|
+
end # module ClassMethods
|
92
|
+
end # module Join
|
93
|
+
|
94
|
+
include Join
|
95
|
+
|
96
|
+
end # module Common
|
97
|
+
end # module AttributeFilters
|
98
|
+
end # module ActiveModel
|
@@ -0,0 +1,130 @@
|
|
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::AttributeFilters::Common::Split module
|
8
|
+
# containing ready-to-use filtering method.
|
9
|
+
|
10
|
+
# @abstract This namespace is shared with ActveModel.
|
11
|
+
module ActiveModel
|
12
|
+
module AttributeFilters
|
13
|
+
# This module contains common, ready-to-use filtering methods.
|
14
|
+
module Common
|
15
|
+
# Splits attributes.
|
16
|
+
module Split
|
17
|
+
extend CommonFilter
|
18
|
+
# Splits attributes and writes the results into other attributes.
|
19
|
+
#
|
20
|
+
# The attrubutes to be splitted are taken from the attribute set
|
21
|
+
# called +should_be_splitted+. This method is safe to be
|
22
|
+
# used with multibyte strings (containing diacritics).
|
23
|
+
#
|
24
|
+
# The pattern used to split a string and the optional limit argument
|
25
|
+
# should be set using the model's class method +split_attribute+
|
26
|
+
# or directly with annotations.
|
27
|
+
def split_attributes
|
28
|
+
for_each_attr_from_set(:should_be_splitted) do |atr_val, atr_name, set_obj|
|
29
|
+
pattern, limit, flatten, into = set_obj.annotation(atr_name, :split_pattern, :split_limit,
|
30
|
+
:split_flatten, :split_into)
|
31
|
+
if limit.nil?
|
32
|
+
r = AttributeFiltersHelpers.each_element(atr_val, String) do |v|
|
33
|
+
v.mb_chars.split(pattern)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
r = AttributeFiltersHelpers.each_element(atr_val, String) do |v|
|
37
|
+
v.mb_chars.split(pattern, limit)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
r = [ r ] unless r.is_a?(Array)
|
41
|
+
r.flatten! if flatten
|
42
|
+
|
43
|
+
# writing collected slices
|
44
|
+
if into.blank?
|
45
|
+
public_send("#{atr_name}=", r)
|
46
|
+
else
|
47
|
+
into.each_with_index { |dst_atr, i| public_send("#{dst_atr}=", r[i]) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# This submodule contains class methods needed to describe
|
53
|
+
# attribute splitting.
|
54
|
+
module ClassMethods
|
55
|
+
# This method parametrizes splitting operation for an attribute of the given name.
|
56
|
+
# It uses attribute set annotations to register parameters used when splitting.
|
57
|
+
#
|
58
|
+
# If the separator is not specified and the source attribute is a kind of +String+ then the default
|
59
|
+
# separator is applied, which is a whitespace character. You can change that by explicitly setting
|
60
|
+
# the separator to +nil+. In such case the split will occuch for each character. If there are more
|
61
|
+
# resulting parts then destination attributes then the redundant elements are ignored. If there is
|
62
|
+
# a limit given and there are more array elements than attributes then the filter behaves like
|
63
|
+
# puts leaves the redundant (unsplittable) and puts it into the last destination attribute.
|
64
|
+
#
|
65
|
+
# If the source attribute is an array then the filter will put each element of that array into each
|
66
|
+
# destination attribute. If there are more array elements than attributes then the reduntant elements
|
67
|
+
# are ignored.
|
68
|
+
#
|
69
|
+
# If the destination attributes are not given then the split filter will generate an array and replace
|
70
|
+
# currently processed attribute with an array.
|
71
|
+
#
|
72
|
+
# The pattern parameter (+:pattern+ when using +split_attributes+ class method or +:split_pattern+ when directly
|
73
|
+
# annotating attribute in a set) should be a string. If you would like to separate each character
|
74
|
+
# you have to set it to +nil+.
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# class User < ActiveRecord::Base
|
78
|
+
# include ActiveModel::AttributeFilters::Common::Split
|
79
|
+
#
|
80
|
+
# attr_virtual :real_name
|
81
|
+
# attr_accessible :real_name
|
82
|
+
# split_attributes :real_name, :limit => 2, :into => [ :first_name, :last_name ], :pattern => ' '
|
83
|
+
# before_validation :split_attributes
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# class User < ActiveRecord::Base
|
88
|
+
# include ActiveModel::AttributeFilters::Common::Split
|
89
|
+
#
|
90
|
+
# attr_virtual :real_name
|
91
|
+
# attr_accessible :real_name
|
92
|
+
# attributes_that :should_be_splitted => { :real_name =>
|
93
|
+
# { :limit => 2,
|
94
|
+
# :into => [ :first_name, :last_name ],
|
95
|
+
# :pattern => ' ' } }
|
96
|
+
# before_validation :split_attributes
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# @param atr_name [String,Symbol] attribute name
|
100
|
+
# @param parameters [Hash] parameters hash # fixme: add YARD parameters explained
|
101
|
+
# @return [void]
|
102
|
+
def split_attribute(atr_name, parameters = nil)
|
103
|
+
atr_name.is_a?(Hash) and return atr_name.each_pair { |k, v| split_attribute(k, v) }
|
104
|
+
parameters = { :into => parameters } unless parameters.is_a?(Hash)
|
105
|
+
the_attribute(atr_name, :should_be_splitted)
|
106
|
+
pattern = parameters[:with] || parameters[:pattern] || parameters[:split_pattern]
|
107
|
+
into = parameters[:into] || parameters[:to] || parameters[:split_into]
|
108
|
+
limit = parameters[:limit] || parameters[:split_limit]
|
109
|
+
limit = limit.to_i unless limit.blank?
|
110
|
+
flatten = parameters[:flatten] || parameters[:split_flatten]
|
111
|
+
if into.blank?
|
112
|
+
into = nil
|
113
|
+
elsif !into.is_a?(Array)
|
114
|
+
into = into.respond_to?(:to_a) ? into.to_a : [ into ]
|
115
|
+
end
|
116
|
+
annotate_attributes_that(:should_be_splitted, atr_name => {
|
117
|
+
:split_flatten => flatten,
|
118
|
+
:split_pattern => pattern,
|
119
|
+
:split_limit => limit,
|
120
|
+
:split_into => into})
|
121
|
+
end
|
122
|
+
alias_method :split_attributes, :split_attribute
|
123
|
+
end # module ClassMethods
|
124
|
+
end # module Split
|
125
|
+
|
126
|
+
include Split
|
127
|
+
|
128
|
+
end # module Common
|
129
|
+
end # module AttributeFilters
|
130
|
+
end # module ActiveModel
|
@@ -0,0 +1,75 @@
|
|
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::AttributeFilters::Common::Squeeze module
|
8
|
+
# containing ready-to-use filtering method.
|
9
|
+
|
10
|
+
# @abstract This namespace is shared with ActveModel.
|
11
|
+
module ActiveModel
|
12
|
+
module AttributeFilters
|
13
|
+
# This module contains common, ready-to-use filtering methods.
|
14
|
+
module Common
|
15
|
+
# Squeezes white characters in attributes.
|
16
|
+
module Squeeze
|
17
|
+
extend CommonFilter
|
18
|
+
# Squeezes white characters in attributes.
|
19
|
+
#
|
20
|
+
# The attrubutes to be squeezed are taken from the attribute set
|
21
|
+
# called +should_be_squeezed+. This method is safe to be
|
22
|
+
# used with multibyte strings (containing diacritics).
|
23
|
+
#
|
24
|
+
# @note If a value of currently processed attribute is an array
|
25
|
+
# then any element of the array is changed.
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
def squeeze_attributes
|
29
|
+
filter_attrs_from_set(:should_be_squeezed) do |atr|
|
30
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
31
|
+
v.mb_chars.squeeze.to_s
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# This submodule contains class methods used to easily define filter.
|
36
|
+
module ClassMethods
|
37
|
+
# Registers attributes that should be squeezed.
|
38
|
+
def squeeze_attributes(*args)
|
39
|
+
attributes_that(:should_be_squeezed, args)
|
40
|
+
end
|
41
|
+
alias_method :squeeze_attribute, :squeeze_attributes
|
42
|
+
end # module ClassMethods
|
43
|
+
|
44
|
+
# Squeezes white characters in attributes, removes leading and trailing spaces and newlines.
|
45
|
+
#
|
46
|
+
# The attrubutes to be squished are taken from the attribute set
|
47
|
+
# called +should_be_squished+. This method is safe to be
|
48
|
+
# used with multibyte strings (containing diacritics).
|
49
|
+
#
|
50
|
+
# @note If a value of currently processed attribute is an array
|
51
|
+
# then any element of the array is changed.
|
52
|
+
#
|
53
|
+
# @return [void]
|
54
|
+
def squish_attributes
|
55
|
+
filter_attrs_from_set(:should_be_squished) do |atr|
|
56
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
57
|
+
v.mb_chars.squish.to_s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# This submodule contains class methods used to easily define filter.
|
62
|
+
module ClassMethods
|
63
|
+
# Registers attributes that should be squished.
|
64
|
+
def squish_attributes(*args)
|
65
|
+
attributes_that(:should_be_squished, args)
|
66
|
+
end
|
67
|
+
alias_method :squish_attribute, :squish_attributes
|
68
|
+
end # module ClassMethods
|
69
|
+
end # module Squeeze
|
70
|
+
|
71
|
+
include Squeeze
|
72
|
+
|
73
|
+
end # module Common
|
74
|
+
end # module AttributeFilters
|
75
|
+
end # module ActiveModel
|
@@ -0,0 +1,46 @@
|
|
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::AttributeFilters::Common::Strip module
|
8
|
+
# containing ready-to-use filtering method.
|
9
|
+
|
10
|
+
# @abstract This namespace is shared with ActveModel.
|
11
|
+
module ActiveModel
|
12
|
+
module AttributeFilters
|
13
|
+
# This module contains common, ready-to-use filtering methods.
|
14
|
+
module Common
|
15
|
+
# Strips attributes from leading and trailing spaces.
|
16
|
+
module Strip
|
17
|
+
extend CommonFilter
|
18
|
+
# Strips attributes from leading and trailing spaces.
|
19
|
+
#
|
20
|
+
# The attrubutes to be stripped are taken from the attribute set called
|
21
|
+
# +should_be_stripped+. It operates directly on attribute's contents.
|
22
|
+
#
|
23
|
+
# @note If a value of currently processed attribute is an array
|
24
|
+
# then any element of the array is changed.
|
25
|
+
#
|
26
|
+
# @return [void]
|
27
|
+
def strip_attributes
|
28
|
+
filter_attrs_from_set(:should_be_stripped) do |atr|
|
29
|
+
AttributeFiltersHelpers.each_element(atr, String) { |v| v.strip }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# This submodule contains class methods used to easily define filter.
|
33
|
+
module ClassMethods
|
34
|
+
# Registers attributes that should be stripped.
|
35
|
+
def strip_attributes(*args)
|
36
|
+
attributes_that(:should_be_stripped, args)
|
37
|
+
end
|
38
|
+
alias_method :strip_attribute, :strip_attributes
|
39
|
+
end # module ClassMethods
|
40
|
+
end # module Strip
|
41
|
+
|
42
|
+
include Strip
|
43
|
+
|
44
|
+
end # module Common
|
45
|
+
end # module AttributeFilters
|
46
|
+
end # module ActiveModel
|
@@ -0,0 +1,50 @@
|
|
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 attr_virtual DSL method that sets up setter, getter,
|
8
|
+
# and enables change tracking for vitual attrbutes.
|
9
|
+
|
10
|
+
# @abstract This namespace is shared with ActveModel.
|
11
|
+
module ActiveModel
|
12
|
+
module AttributeFilters
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
unless method_defined?(:attr_virtual)
|
18
|
+
# This method creates setter and getter for attributes of the given names
|
19
|
+
# and ensures that changes of their values are tracked.
|
20
|
+
#
|
21
|
+
# @note Placing +attr_writer+ with the same attribute name AFTER
|
22
|
+
# +attr_virtual+ will overwrite setter. Don't do that.
|
23
|
+
def attr_virtual(*attribute_names)
|
24
|
+
attribute_names.flatten.compact.uniq.each do |atr_name|
|
25
|
+
writer_name = "#{atr_name}="
|
26
|
+
atr_name = atr_name.to_sym
|
27
|
+
attr_reader(atr_name) unless method_defined?(atr_name)
|
28
|
+
attr_accessible(atr_name) if method_defined?(:attr_accessible)
|
29
|
+
if method_defined?(writer_name)
|
30
|
+
self.class_eval <<-EVAL
|
31
|
+
alias_method :#{atr_name}_without_change_tracking=, :#{writer_name}
|
32
|
+
def #{writer_name}(val)
|
33
|
+
attribute_will_change!('#{atr_name}') if val != '#{atr_name}'
|
34
|
+
#{atr_name}_without_change_tracking=(val)
|
35
|
+
end
|
36
|
+
EVAL
|
37
|
+
else
|
38
|
+
self.class_eval <<-EVAL
|
39
|
+
def #{writer_name}(val)
|
40
|
+
attribute_will_change!('#{atr_name}') if val != '#{atr_name}'
|
41
|
+
@#{atr_name} = val
|
42
|
+
end
|
43
|
+
EVAL
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end # def attr_virtual
|
47
|
+
end # unless method_defined?(:attr_virtual)
|
48
|
+
end # module ClassMethods
|
49
|
+
end # module AttributeFilters
|
50
|
+
end # module ActiveModel
|
@@ -13,7 +13,8 @@ module ActiveModel
|
|
13
13
|
PROCESSING_FLAGS = {
|
14
14
|
:process_blank => false,
|
15
15
|
:process_all => false,
|
16
|
-
:no_presence_check => false
|
16
|
+
:no_presence_check => false,
|
17
|
+
:include_missing => false
|
17
18
|
}.freeze
|
18
19
|
|
19
20
|
# Gets names of attributes for which filters should be applied by
|
@@ -23,7 +24,7 @@ module ActiveModel
|
|
23
24
|
# @overload attributes_to_filter(set_name, process_all, no_presence_check)
|
24
25
|
# @param set_name [String,Symbol] name of a set of attributes used to get attributes
|
25
26
|
# @param process_all [Boolean] if set then all the attributes from the attribute set are selected,
|
26
|
-
# not just attributes that
|
27
|
+
# not just attributes that have changed (defaults to +false+)
|
27
28
|
# @param no_presence_check [Boolean] if set then the checking whether attribute exists will be
|
28
29
|
# disabled (matters only when +process_all+ is also set) (defaults to +false+)
|
29
30
|
# @return [AttributeSet] set of attributes (attribute name => previous_value)
|
@@ -36,9 +37,9 @@ module ActiveModel
|
|
36
37
|
# disabled (matters only when +process_all+ is also set) (defaults to +false+)
|
37
38
|
# @return [AttributeSet] set of attributes (attribute name => previous_value)
|
38
39
|
def attributes_to_filter(set_name, process_all = false, no_presence_check = false)
|
39
|
-
atf = set_name.is_a?(::ActiveModel::AttributeSet) ? set_name :
|
40
|
+
atf = set_name.is_a?(::ActiveModel::AttributeSet) ? set_name : attribute_set_simple(set_name)
|
40
41
|
if process_all
|
41
|
-
no_presence_check ? atf : atf & (
|
42
|
+
no_presence_check ? atf : atf & all_attributes_simple(no_presence_check)
|
42
43
|
else
|
43
44
|
if self.class.filter_virtual_attributes_that_changed?
|
44
45
|
atf & changes.keys
|
@@ -66,17 +67,18 @@ module ActiveModel
|
|
66
67
|
#
|
67
68
|
# The result of the given block is used to set a new values for processed attributes.
|
68
69
|
#
|
69
|
-
# @param set_name [Symbol] name of the attribute set
|
70
|
+
# @param set_name [Symbol] name of the attribute set or a set object
|
70
71
|
# @param args [Array] optional additional arguments that will be passed to the block
|
71
72
|
# @param flags [Array<Symbol>] optional additional flags controlling the processing of attributes:
|
72
73
|
# * +:process_blank+ – tells to also process attributes that are blank (empty or +nil+)
|
73
74
|
# * +:process_all+ - tells to process all attributes, not just the ones that has changed
|
74
75
|
# * +:no_presence_check+ – tells not to check for existence of each processed attribute when processing
|
75
|
-
# all attributes; increases performance but you must care about putting
|
76
|
+
# all attributes; increases performance but you must care about putting only the existing attributes into sets
|
76
77
|
# @yield [attribute_value, set_name, attribute_name, *args] block that will be called for each attribute
|
77
78
|
# @yieldparam attribute_value [Object] current attribute value that should be altered
|
79
|
+
# @yieldparam attribute_name [String] a name of currently processed attribute
|
80
|
+
# @yieldparam set_object [Object] currently processed set that attribute belongs to
|
78
81
|
# @yieldparam set_name [Symbol] a name of the processed attribute set
|
79
|
-
# @yieldparam attribute_name [Object] a name of currently processed attribute
|
80
82
|
# @yieldparam args [Array] optional arguments passed to the method
|
81
83
|
# @yieldreturn [Object] the result of calling the block
|
82
84
|
# @return [void]
|
@@ -95,7 +97,7 @@ module ActiveModel
|
|
95
97
|
#
|
96
98
|
# end
|
97
99
|
def filter_attrs_from_set(set_name, *args, &block)
|
98
|
-
|
100
|
+
operate_on_attrs_from_set(set_name, true, *args, &block)
|
99
101
|
end
|
100
102
|
alias_method :attribute_filter_for_set, :filter_attrs_from_set
|
101
103
|
alias_method :filter_attributes_which, :filter_attrs_from_set
|
@@ -131,17 +133,20 @@ module ActiveModel
|
|
131
133
|
# method in a block or explicitly assign new, calculated value to the attribute
|
132
134
|
# using its name (also passed to a block as one of arguments).
|
133
135
|
#
|
134
|
-
# @param set_name [Symbol] name of the attribute set
|
136
|
+
# @param set_name [Symbol] name of the attribute set or a set object
|
135
137
|
# @param args [Array] optional additional arguments that will be passed to a block
|
136
138
|
# @param flags [Array<Symbol>] optional additional flags controlling the processing of attributes:
|
137
139
|
# * +:process_blank+ – tells to also process attributes that are blank (empty or +nil+)
|
138
140
|
# * +:process_all+ - tells to process all attributes, not just the ones that has changed
|
139
141
|
# * +:no_presence_check+ – tells not to check for existence of each processed attribute when processing
|
140
142
|
# all attributes; increases performance but you must care about putting into set only the existing attributes
|
143
|
+
# * +:include_missing+ – includes attributes that does not exist in a resulting iteration (their values are
|
144
|
+
# always +nil+); has effect only when +process_blank+ and +no_presence_check+ are set to +true+
|
141
145
|
# @yield [attribute_value, set_name, attribute_name, *args] block that will be called for each attribute
|
142
146
|
# @yieldparam attribute_value [Object] current attribute value that should be altered
|
147
|
+
# @yieldparam attribute_name [String] a name of currently processed attribute
|
148
|
+
# @yieldparam set_object [Object] currently processed set that attribute belongs to
|
143
149
|
# @yieldparam set_name [Symbol] a name of the processed attribute set
|
144
|
-
# @yieldparam attribute_name [Object] a name of currently processed attribute
|
145
150
|
# @yieldparam args [Array] optional arguments passed to the method
|
146
151
|
# @yieldreturn [Object] the result of calling the block
|
147
152
|
# @return [void]
|
@@ -200,6 +205,7 @@ module ActiveModel
|
|
200
205
|
end
|
201
206
|
alias_method :filter_virtual_attributes_that_changed, :filter_virtual_attributes_that_have_changed
|
202
207
|
alias_method :filter_changed_virtual_attributes, :filter_virtual_attributes_that_have_changed
|
208
|
+
alias_method :virtual_attributes_are_tracked, :filter_virtual_attributes_that_have_changed
|
203
209
|
|
204
210
|
# Gets the internal flag that causes to check virtual attributes
|
205
211
|
# for changes when selecting attributes for filtering.
|
@@ -220,49 +226,54 @@ module ActiveModel
|
|
220
226
|
|
221
227
|
# Applies operations to elements from set.
|
222
228
|
def operate_on_attrs_from_set(set_name, alter_mode, *args, &block)
|
229
|
+
block_given? or return enum_for(__method__, set_name, alter_mode, *args)
|
223
230
|
flags = AttributeFiltersHelpers.process_flags(args)
|
224
231
|
process_all = flags[:process_all]
|
225
232
|
process_blank = flags[:process_blank]
|
226
233
|
no_presence_check = flags[:no_presence_check]
|
227
|
-
|
234
|
+
include_missing = flags[:include_missing]
|
235
|
+
if set_name.is_a?(::ActiveModel::AttributeSet)
|
236
|
+
set_obj = set_name
|
237
|
+
set_name = nil
|
238
|
+
else
|
239
|
+
set_obj = attribute_set_simple(set_name)
|
240
|
+
end
|
241
|
+
attrs_to_process = attributes_to_filter(set_obj, process_all, no_presence_check)
|
228
242
|
if alter_mode
|
229
243
|
if process_blank
|
230
244
|
# filtering without testing for blank
|
231
245
|
attrs_to_process.each do |atr|
|
232
|
-
|
246
|
+
public_send("#{atr}=", yield(public_send(atr), atr, set_obj, set_name, *args))
|
233
247
|
end
|
234
248
|
else
|
235
249
|
# filtering present only
|
236
250
|
attrs_to_process.each do |atr|
|
237
|
-
v =
|
238
|
-
|
251
|
+
v = public_send(atr)
|
252
|
+
public_send("#{atr}=", yield(v, atr, set_obj, set_name, *args)) if v.present?
|
239
253
|
end
|
240
254
|
end
|
241
255
|
else
|
242
256
|
if process_blank
|
243
257
|
# calling without testing for blank
|
244
|
-
|
245
|
-
|
258
|
+
if include_missing
|
259
|
+
# including missing attributes (changing them into nils)
|
260
|
+
attrs_to_process.each do |atr|
|
261
|
+
v = respond_to?(atr) ? public_send(atr) : nil
|
262
|
+
yield(v, atr, set_obj, set_name, *args)
|
263
|
+
end
|
264
|
+
else
|
265
|
+
attrs_to_process.each do |atr|
|
266
|
+
yield(public_send(atr), atr, set_obj, set_name, *args)
|
267
|
+
end
|
246
268
|
end
|
247
269
|
else
|
248
270
|
# calling present only
|
249
271
|
attrs_to_process.each do |atr|
|
250
|
-
v =
|
251
|
-
yield(v,
|
272
|
+
v = public_send(atr)
|
273
|
+
yield(v, atr, set_obj, set_name, *args) if v.present?
|
252
274
|
end
|
253
275
|
end
|
254
276
|
end
|
255
277
|
end
|
256
|
-
|
257
|
-
private
|
258
|
-
|
259
|
-
# Helper that collects virtual attributes that
|
260
|
-
# have setters and getters.
|
261
|
-
def __vatrf(no_presence_check = false)
|
262
|
-
tar = self.class.send(:__treat_as_real)
|
263
|
-
return tar if no_presence_check || tar.empty?
|
264
|
-
tar.select { |a| respond_to?(a) && respond_to?("#{a}=") }
|
265
|
-
end
|
266
|
-
|
267
278
|
end # module AttributeFilters
|
268
279
|
end # module ActiveModel
|