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
@@ -17,9 +17,26 @@ module ActiveModel
|
|
17
17
|
class Query < BasicObject
|
18
18
|
# Creates new query object.
|
19
19
|
#
|
20
|
-
# @
|
21
|
-
#
|
22
|
-
|
20
|
+
# @overload initialize(am_object)
|
21
|
+
# Creates new query object and uses empty set as an underlying object.
|
22
|
+
# @param am_object [Object] model object which has access to attributes (may be an instance of ActiveRecord or similar)
|
23
|
+
# @return [AttributeSet::Query] query object
|
24
|
+
#
|
25
|
+
# @overload initialize(set_object, am_object)
|
26
|
+
# @param set_object [AttributeSet,String,Symbol,Array] attribute set for which the query will be made or
|
27
|
+
# known attribute set name (symbol or string) existing within the given model or an array uset to create a new set
|
28
|
+
# @param am_object [Object] model object which has access to attributes (may be an instance of ActiveRecord or similar)
|
29
|
+
# @return [AttributeSet::Query] query object
|
30
|
+
def initialize(set_object, am_object = nil)
|
31
|
+
if am_object.nil?
|
32
|
+
am_object = set_object
|
33
|
+
set_object = ::ActiveModel::AttributeSet.new
|
34
|
+
end
|
35
|
+
if set_object.is_a?(::Symbol) || set_object.is_a?(::String)
|
36
|
+
set_object = am_object.class.attribute_set(set_object).dup
|
37
|
+
elsif !set_object.is_a?(::ActiveModel::AttributeSet)
|
38
|
+
set_object = ::ActiveModel::AttributeSet.new(set_object)
|
39
|
+
end
|
23
40
|
@set_object = set_object
|
24
41
|
@am_object = am_object
|
25
42
|
@next_method = nil
|
@@ -70,7 +87,7 @@ module ActiveModel
|
|
70
87
|
when :invalid?, :is_not_valid?, :any_invalid?, :are_not_valid?, :not_valid?, :is_any_invalid?
|
71
88
|
self.any.invalid?
|
72
89
|
else
|
73
|
-
r = @set_object.
|
90
|
+
r = @set_object.public_method(method_sym).call(*args, &block)
|
74
91
|
return r if r.respond_to?(:__in_as_proxy) || !r.is_a?(::ActiveModel::AttributeSet)
|
75
92
|
::ActiveModel::AttributeSet::Query.new(r, @am_object)
|
76
93
|
end
|
@@ -82,12 +99,12 @@ module ActiveModel
|
|
82
99
|
r = case method_sym
|
83
100
|
when :valid?
|
84
101
|
@am_object.valid?
|
85
|
-
@set_object.
|
102
|
+
@set_object.public_method(m).call(*args) { |atr| not @am_object.errors.has_key?(atr.to_sym) }
|
86
103
|
when :invalid?
|
87
104
|
@am_object.valid?
|
88
|
-
@set_object.
|
105
|
+
@set_object.public_method(m).call(*args) { |atr| @am_object.errors.has_key?(atr.to_sym) }
|
89
106
|
else
|
90
|
-
@set_object.
|
107
|
+
@set_object.public_method(m).call { |atr| @am_object.public_send(atr).public_method(method_sym).call(*args, &block) }
|
91
108
|
end
|
92
109
|
return r if r.respond_to?(:__in_as_proxy) || !r.is_a?(::ActiveModel::AttributeSet)
|
93
110
|
::ActiveModel::AttributeSet::Query.new(r, @am_object)
|
@@ -114,6 +131,47 @@ module ActiveModel
|
|
114
131
|
end
|
115
132
|
alias_method :kind_of?, :is_a?
|
116
133
|
|
134
|
+
# @private
|
135
|
+
def instance_of?(klass)
|
136
|
+
super || @set_object.instance_of?(klass)
|
137
|
+
end
|
138
|
+
|
139
|
+
# @private
|
140
|
+
def instance_eval(*args, &block)
|
141
|
+
@set_object.instance_eval(*args, &block)
|
142
|
+
end
|
143
|
+
|
144
|
+
# @private
|
145
|
+
def instance_exec(*args, &block)
|
146
|
+
@set_object.instance_exec(*args, &block)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Gets values of attributes from current set.
|
150
|
+
# If an attribute does not exist it puts +nil+ in its place.
|
151
|
+
#
|
152
|
+
# @return [Array] attribute values
|
153
|
+
def values
|
154
|
+
r = []
|
155
|
+
@am_object.for_each_attr_from_set(@set_object, :process_all,
|
156
|
+
:process_blank,
|
157
|
+
:no_presence_check,
|
158
|
+
:include_missing) { |a| r << a }
|
159
|
+
r
|
160
|
+
end
|
161
|
+
|
162
|
+
# Gets attribute names and their values for attributes from current set.
|
163
|
+
# If an attribute does not exist it puts +nil+ as its value.
|
164
|
+
#
|
165
|
+
# @return [Hash{String => Object}] attribute names and their values
|
166
|
+
def values_hash
|
167
|
+
r = {}
|
168
|
+
@am_object.for_each_attr_from_set(@set_object, :process_all,
|
169
|
+
:process_blank,
|
170
|
+
:no_presence_check,
|
171
|
+
:include_missing) { |a, n| r[n] = a }
|
172
|
+
r
|
173
|
+
end
|
174
|
+
|
117
175
|
protected
|
118
176
|
|
119
177
|
# Queues any method of the given name to be called when next
|
@@ -0,0 +1,37 @@
|
|
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 backports for compatibility with previous Ruby versions.
|
8
|
+
|
9
|
+
# @private
|
10
|
+
# @abstract This class is here for compatibility reasons.
|
11
|
+
class Object
|
12
|
+
|
13
|
+
unless method_defined?(:public_send)
|
14
|
+
|
15
|
+
# @private
|
16
|
+
def public_send(name, *args)
|
17
|
+
unless public_methods.include?(name.to_s)
|
18
|
+
raise NoMethodError.new("undefined method `#{name}' for \"#{self.inspect}\":#{self.class}")
|
19
|
+
end
|
20
|
+
send(name, *args)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
unless method_defined?(:public_method)
|
26
|
+
|
27
|
+
# @private
|
28
|
+
def public_method(name)
|
29
|
+
unless public_methods.include?(name.to_s)
|
30
|
+
raise NameError.new("undefined method `#{name}' for class `#{self.class}'")
|
31
|
+
end
|
32
|
+
method(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end # class Object
|
@@ -10,145 +10,63 @@
|
|
10
10
|
# @abstract This namespace is shared with ActveModel.
|
11
11
|
module ActiveModel
|
12
12
|
module AttributeFilters
|
13
|
-
# This module contains common, ready-to-use filtering methods.
|
14
|
-
module Common
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
14
|
+
# This method is a callback method that tries to call all
|
15
|
+
# known filtering methods if they are present.
|
16
|
+
#
|
17
|
+
# Calling order:
|
18
|
+
# * split_attributes
|
19
|
+
# * join_attributes
|
20
|
+
# * squeeze_attributes
|
21
|
+
# * strip_attributes
|
22
|
+
# * upcase_attributes
|
23
|
+
# * downcase_attributes
|
24
|
+
# * capitalize_attributes
|
25
|
+
# * fully_capitalize_attributes
|
26
|
+
# * titleize_attributes
|
27
|
+
def filter_attributes
|
28
|
+
respond_to?(:split_attributes) and split_attributes
|
29
|
+
respond_to?(:join_attributes) and join_attributes
|
30
|
+
respond_to?(:squeeze_attributes) and squeeze_attributes
|
31
|
+
respond_to?(:strip_attributes) and strip_attributes
|
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
|
28
38
|
|
29
|
-
|
30
|
-
|
31
|
-
# Downcases attributes.
|
32
|
-
#
|
33
|
-
# The attrubutes to be downcased are taken from the attribute set
|
34
|
-
# called +should_be_downcased+. This method is safe to be
|
35
|
-
# used with multibyte strings (containing diacritics).
|
36
|
-
#
|
37
|
-
# @return [void]
|
38
|
-
def downcase_attributes
|
39
|
-
filter_attrs_from_set(:should_be_downcased) do |atr|
|
40
|
-
atr.mb_chars.downcase.to_s
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Upcases attributes.
|
46
|
-
module Upcase
|
47
|
-
# Upcases attributes.
|
48
|
-
#
|
49
|
-
# The attrubutes to be upcased are taken from the attribute set
|
50
|
-
# called +should_be_upcased+. This method is safe to be
|
51
|
-
# used with multibyte strings (containing diacritics).
|
52
|
-
#
|
53
|
-
# @return [void]
|
54
|
-
def upcase_attributes
|
55
|
-
filter_attrs_from_set(:should_be_upcased) do |atr|
|
56
|
-
atr.mb_chars.upcase.to_s
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Operates on attributes' case.
|
62
|
-
module Case
|
63
|
-
include Upcase
|
64
|
-
include Downcase
|
65
|
-
end
|
39
|
+
# This module contains common, ready-to-use filtering methods.
|
40
|
+
module Common
|
66
41
|
|
67
|
-
#
|
68
|
-
module
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# @return [void]
|
76
|
-
def capitalize_attributes
|
77
|
-
filter_attrs_from_set(:should_be_capitalized) do |atr|
|
78
|
-
atr.mb_chars.capitalize.to_s
|
42
|
+
# @private
|
43
|
+
module CommonFilter
|
44
|
+
# @private
|
45
|
+
def included(base)
|
46
|
+
if base == ActiveModel::AttributeFilters::Common
|
47
|
+
base::ClassMethods.send(:include, self::ClassMethods)
|
48
|
+
else
|
49
|
+
base.extend ClassMethods
|
79
50
|
end
|
80
51
|
end
|
81
|
-
|
82
|
-
# Fully capitalizes attributes (capitalizes each word and squeezes spaces).
|
83
|
-
#
|
84
|
-
# The attrubutes to be fully capitalized are taken from the attribute set
|
85
|
-
# called +should_be_fully_capitalized+ and from set +should_be_titleized+.
|
86
|
-
# This method is safe to be used with multibyte strings (containing diacritics).
|
87
|
-
#
|
88
|
-
# @return [void]
|
89
|
-
def titleize_with_squeezed_spaces
|
90
|
-
s = attribute_set(:should_be_fully_capitalized) + attribute_set(:should_be_titleized)
|
91
|
-
filter_attrs_from_set(s) do |atr|
|
92
|
-
atr.mb_chars.split(' ').map { |n| n.capitalize }.join(' ')
|
93
|
-
end
|
94
|
-
end
|
95
|
-
alias_method :fully_capitalize_attributes, :titleize_with_squeezed_spaces
|
96
|
-
|
97
52
|
end
|
98
53
|
|
99
|
-
|
100
|
-
module Squeeze
|
101
|
-
# Squeezes white characters in attributes.
|
102
|
-
#
|
103
|
-
# The attrubutes to be squeezed are taken from the attribute set
|
104
|
-
# called +should_be_squeezed+. This method is safe to be
|
105
|
-
# used with multibyte strings (containing diacritics).
|
106
|
-
#
|
107
|
-
# @return [void]
|
108
|
-
def squeeze_attributes
|
109
|
-
filter_attrs_from_set(:should_be_squeezed) do |atr|
|
110
|
-
atr.mb_chars.squeeze.to_s
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
54
|
+
extend CommonFilter
|
114
55
|
|
115
|
-
#
|
116
|
-
|
117
|
-
|
118
|
-
#
|
119
|
-
# The attrubutes to be squished are taken from the attribute set
|
120
|
-
# called +should_be_squished+. This method is safe to be
|
121
|
-
# used with multibyte strings (containing diacritics).
|
122
|
-
#
|
123
|
-
# @return [void]
|
124
|
-
def squish_attributes
|
125
|
-
filter_attrs_from_set(:should_be_squished) do |atr|
|
126
|
-
atr.mb_chars.squish.to_s
|
127
|
-
end
|
128
|
-
end
|
56
|
+
# This module contains class methods used by the DSL
|
57
|
+
# to create keywords for common operations.
|
58
|
+
module ClassMethods
|
129
59
|
end
|
130
60
|
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
#
|
135
|
-
# The attrubutes to be titleized are taken from the attribute set
|
136
|
-
# called +should_be_titleized+. This method is safe to be
|
137
|
-
# used with multibyte strings (containing diacritics).
|
138
|
-
#
|
139
|
-
# @return [void]
|
140
|
-
def titleize_attributes
|
141
|
-
filter_attrs_from_set(:should_be_titleized) do |atr|
|
142
|
-
atr.mb_chars.titleize.to_s
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
61
|
+
# Include all default filters
|
62
|
+
# that should be available
|
63
|
+
# when Common module is included.
|
146
64
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
65
|
+
require 'attribute-filters/common_filters/strip'
|
66
|
+
require 'attribute-filters/common_filters/case'
|
67
|
+
require 'attribute-filters/common_filters/squeeze'
|
68
|
+
require 'attribute-filters/common_filters/split'
|
69
|
+
require 'attribute-filters/common_filters/join'
|
152
70
|
|
153
71
|
end # module Common
|
154
72
|
end # module AttributeFilters
|
@@ -0,0 +1,159 @@
|
|
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::Case 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
|
+
|
16
|
+
# This module contains attribute filters responsible for changing the case of letters.
|
17
|
+
module Case
|
18
|
+
extend CommonFilter
|
19
|
+
# Downcases attributes.
|
20
|
+
#
|
21
|
+
# The attrubutes to be downcased are taken from the attribute set
|
22
|
+
# called +should_be_downcased+. This method is safe to be
|
23
|
+
# used with multibyte strings (containing diacritics).
|
24
|
+
#
|
25
|
+
# @note If a value of currently processed attribute is an array
|
26
|
+
# then any element of the array is changed.
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
def downcase_attributes
|
30
|
+
filter_attrs_from_set(:should_be_downcased) do |atr|
|
31
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
32
|
+
v.mb_chars.downcase.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# This submodule contains class methods used to easily define filter.
|
38
|
+
module ClassMethods
|
39
|
+
# Registers attributes that should be downcased.
|
40
|
+
def downcase_attributes(*args)
|
41
|
+
attributes_that(:should_be_downcased, args)
|
42
|
+
end
|
43
|
+
alias_method :downcase_attribute, :downcase_attributes
|
44
|
+
end # module ClassMethods
|
45
|
+
|
46
|
+
# Upcases attributes.
|
47
|
+
#
|
48
|
+
# The attrubutes to be upcased are taken from the attribute set
|
49
|
+
# called +should_be_upcased+. This method is safe to be
|
50
|
+
# used with multibyte strings (containing diacritics).
|
51
|
+
#
|
52
|
+
# @note If a value of currently processed attribute is an array
|
53
|
+
# then any element of the array is changed.
|
54
|
+
#
|
55
|
+
# @return [void]
|
56
|
+
def upcase_attributes
|
57
|
+
filter_attrs_from_set(:should_be_upcased) do |atr|
|
58
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
59
|
+
v.mb_chars.upcase.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# This submodule contains class methods used to easily define filter.
|
65
|
+
module ClassMethods
|
66
|
+
# Registers attributes that should be upcased.
|
67
|
+
def upcase_attributes(*args)
|
68
|
+
attributes_that(:should_be_upcased, args)
|
69
|
+
end
|
70
|
+
alias_method :upcase_attribute, :upcase_attributes
|
71
|
+
end # module ClassMethods
|
72
|
+
|
73
|
+
# Titleizes attributes.
|
74
|
+
#
|
75
|
+
# The attrubutes to be titleized are taken from the attribute set
|
76
|
+
# called +should_be_titleized+. This method is safe to be
|
77
|
+
# used with multibyte strings (containing diacritics).
|
78
|
+
#
|
79
|
+
# @note If a value of currently processed attribute is an array
|
80
|
+
# then any element of the array is changed.
|
81
|
+
#
|
82
|
+
# @return [void]
|
83
|
+
def titleize_attributes
|
84
|
+
filter_attrs_from_set(:should_be_titleized) do |atr|
|
85
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
86
|
+
v.mb_chars.titleize.to_s
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# This submodule contains class methods used to easily define filter.
|
92
|
+
module ClassMethods
|
93
|
+
# Registers attributes that should be titleized.
|
94
|
+
def titleize_attributes(*args)
|
95
|
+
attributes_that(:should_be_titleized, args)
|
96
|
+
end
|
97
|
+
alias_method :titleize_attribute, :titleize_attributes
|
98
|
+
end # module ClassMethods
|
99
|
+
|
100
|
+
# Capitalizes attributes.
|
101
|
+
#
|
102
|
+
# The attrubutes to be capitalized are taken from the attribute set
|
103
|
+
# called +should_be_capitalized+. This method is safe to be
|
104
|
+
# used with multibyte strings (containing diacritics).
|
105
|
+
#
|
106
|
+
# @note If a value of currently processed attribute is an array
|
107
|
+
# then any element of the array is changed.
|
108
|
+
#
|
109
|
+
# @return [void]
|
110
|
+
def capitalize_attributes
|
111
|
+
filter_attrs_from_set(:should_be_capitalized) do |atr|
|
112
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
113
|
+
v.mb_chars.capitalize.to_s
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Fully capitalizes attributes (capitalizes each word and squeezes spaces).
|
119
|
+
#
|
120
|
+
# The attrubutes to be fully capitalized are taken from the attribute set
|
121
|
+
# called +should_be_fully_capitalized+ and from set +should_be_titleized+.
|
122
|
+
# This method is safe to be used with multibyte strings (containing diacritics).
|
123
|
+
#
|
124
|
+
# @note If a value of currently processed attribute is an array
|
125
|
+
# then any element of the array is changed.
|
126
|
+
#
|
127
|
+
# @return [void]
|
128
|
+
def titleize_with_squeezed_spaces
|
129
|
+
s = attribute_set_simple(:should_be_fully_capitalized) + attribute_set_simple(:should_be_titleized)
|
130
|
+
filter_attrs_from_set(s) do |atr|
|
131
|
+
AttributeFiltersHelpers.each_element(atr, String) do |v|
|
132
|
+
v.mb_chars.split(' ').map { |n| n.capitalize }.join(' ')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
alias_method :fully_capitalize_attributes, :titleize_with_squeezed_spaces
|
137
|
+
|
138
|
+
# This submodule contains class methods used to easily define filter.
|
139
|
+
module ClassMethods
|
140
|
+
# Registers attributes that should be capitalized.
|
141
|
+
def capitalize_attributes(*args)
|
142
|
+
attributes_that(:should_be_capitalized, args)
|
143
|
+
end
|
144
|
+
alias_method :capitalize_attribute, :capitalize_attributes
|
145
|
+
|
146
|
+
# Registers attributes that should be fully capitalized.
|
147
|
+
def fully_capitalize_attributes(*args)
|
148
|
+
attributes_that(:should_be_fully_capitalized, args)
|
149
|
+
end
|
150
|
+
alias_method :fully_capitalize_attribute, :fully_capitalize_attributes
|
151
|
+
alias_method :titleize_with_squeezed_spaces, :fully_capitalize_attributes
|
152
|
+
end # module ClassMethods
|
153
|
+
end # module Case
|
154
|
+
|
155
|
+
include Case
|
156
|
+
|
157
|
+
end # module Common
|
158
|
+
end # module AttributeFilters
|
159
|
+
end # module ActiveModel
|