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.
- 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
|