glue 0.0.1 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +19 -0
- data/INSTALL +56 -0
- data/README +3 -0
- data/Rakefile +73 -10
- data/doc/AUTHORS +16 -0
- data/doc/LICENSE +33 -0
- data/doc/RELEASES +5 -0
- data/install.rb +47 -0
- data/lib/glue.rb +57 -59
- data/lib/glue/array.rb +61 -0
- data/lib/glue/attribute.rb +83 -0
- data/lib/glue/cache.rb +138 -0
- data/lib/glue/flexob.rb +12 -0
- data/lib/glue/hash.rb +122 -0
- data/lib/glue/inflector.rb +91 -0
- data/lib/glue/logger.rb +147 -0
- data/lib/glue/misc.rb +14 -0
- data/lib/glue/mixins.rb +36 -0
- data/lib/glue/number.rb +24 -0
- data/lib/glue/object.rb +32 -0
- data/lib/glue/pool.rb +60 -0
- data/lib/glue/property.rb +408 -0
- data/lib/glue/string.rb +162 -0
- data/lib/glue/time.rb +85 -0
- data/lib/glue/validation.rb +461 -0
- data/test/glue/tc_attribute.rb +22 -0
- data/test/glue/tc_cache.rb +45 -0
- data/test/glue/tc_hash.rb +38 -0
- data/test/glue/tc_logger.rb +39 -0
- data/test/glue/tc_numbers.rb +20 -0
- data/test/glue/tc_property.rb +91 -0
- data/test/glue/tc_property_mixins.rb +93 -0
- data/test/glue/tc_property_type_checking.rb +35 -0
- data/test/glue/tc_strings.rb +103 -0
- data/test/glue/tc_validation.rb +214 -0
- metadata +95 -89
- data/History.txt +0 -6
- data/Manifest.txt +0 -7
- data/README.txt +0 -127
- data/bin/glue +0 -1
- data/test/test_glue.rb +0 -218
data/lib/glue/string.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# * Anastasios Koutoumanos <ak@navel.gr>
|
3
|
+
# * Elias Karakoulakis <ekarak@ktismata.com>
|
4
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
5
|
+
# $Id: string.rb 282 2005-03-10 12:24:53Z gmosx $
|
6
|
+
|
7
|
+
require "uri"
|
8
|
+
|
9
|
+
module N;
|
10
|
+
|
11
|
+
# General string utilities collection.
|
12
|
+
#
|
13
|
+
# === Design:
|
14
|
+
#
|
15
|
+
# Implement as a module to avoid class polution. You can
|
16
|
+
# still Ruby's advanced features to include the module in your
|
17
|
+
# class. Passing the object to act upon allows to check for nil,
|
18
|
+
# which isn't possible if you use self.
|
19
|
+
#
|
20
|
+
# === TODO:
|
21
|
+
#
|
22
|
+
# - implement a method that returns easy to remember
|
23
|
+
# pseudo-random strings
|
24
|
+
# - add aliases for those methods in Kernel.
|
25
|
+
|
26
|
+
module StringUtils
|
27
|
+
|
28
|
+
# Move this in String class?
|
29
|
+
#
|
30
|
+
# Tests a string for a valid value (non nil, not empty)
|
31
|
+
#
|
32
|
+
def self.valid?(string)
|
33
|
+
return (not ((nil == string) or (string.empty?)))
|
34
|
+
end
|
35
|
+
|
36
|
+
# returns short abstract of long strings (first 'count'
|
37
|
+
# characters, chopped at the nearest word, appended by '...')
|
38
|
+
# force_cutoff: break forcibly at 'count' chars. Does not accept
|
39
|
+
# count < 2.
|
40
|
+
|
41
|
+
def self.head(string, count = 128, force_cutoff = false, ellipsis="...")
|
42
|
+
return nil unless string
|
43
|
+
return nil if count < 2
|
44
|
+
|
45
|
+
if string.size > count
|
46
|
+
cut_at = force_cutoff ? count : (string.index(' ', count-1) || count)
|
47
|
+
xstring = string.slice(0, cut_at)
|
48
|
+
return xstring.chomp(" ") + ellipsis
|
49
|
+
else
|
50
|
+
return string
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Apply a set of rules (regular expression matches) to the
|
55
|
+
# string
|
56
|
+
#
|
57
|
+
# === Requirements:
|
58
|
+
# - the rules must be applied in order! So we cannot use a
|
59
|
+
# hash because the ordering is not guaranteed! we use an
|
60
|
+
# array instead.
|
61
|
+
#
|
62
|
+
# === Input:
|
63
|
+
# the string to rewrite
|
64
|
+
# the array containing rule-pairs (match, rewrite)
|
65
|
+
#
|
66
|
+
# === Output:
|
67
|
+
# the rewritten string
|
68
|
+
|
69
|
+
MATCH = 0
|
70
|
+
REWRITE = 1
|
71
|
+
|
72
|
+
def self.rewrite(string, rules)
|
73
|
+
return nil unless string
|
74
|
+
|
75
|
+
# gmosx: helps to find bugs
|
76
|
+
raise ArgumentError.new('The rules parameter is nil') unless rules
|
77
|
+
|
78
|
+
rewritten_string = string.dup
|
79
|
+
|
80
|
+
for rule in rules
|
81
|
+
rewritten_string.gsub!(rule[MATCH], rule[REWRITE])
|
82
|
+
end
|
83
|
+
|
84
|
+
return (rewritten_string or string)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Enforces a maximum width of a string inside an
|
88
|
+
# html container. If the string exceeds this maximum width
|
89
|
+
# the string gets wraped.
|
90
|
+
#
|
91
|
+
# Not really useful, better use the CSS overflow: hidden
|
92
|
+
# functionality.
|
93
|
+
#
|
94
|
+
# === Input:
|
95
|
+
# the string to be wrapped
|
96
|
+
# the enforced width
|
97
|
+
# the separator used for wrapping
|
98
|
+
#
|
99
|
+
# === Output:
|
100
|
+
# the wrapped string
|
101
|
+
#
|
102
|
+
# === Example:
|
103
|
+
# text = "1111111111111111111111111111111111111111111"
|
104
|
+
# text = wrap(text, 10, " ")
|
105
|
+
# p text # => "1111111111 1111111111 1111111111"
|
106
|
+
#
|
107
|
+
# See the test cases to better understand the behaviour!
|
108
|
+
|
109
|
+
def self.wrap(string, width = 20, separator = " ")
|
110
|
+
return nil unless string
|
111
|
+
|
112
|
+
re = /([^#{separator}]{1,#{width}})/
|
113
|
+
wrapped_string = string.scan(re).join(separator)
|
114
|
+
|
115
|
+
return wrapped_string
|
116
|
+
end
|
117
|
+
|
118
|
+
# Replace dangerours chars in filenames
|
119
|
+
=begin
|
120
|
+
def self.rationalize_filename(filename)
|
121
|
+
return nil unless filename
|
122
|
+
# gmosx: rationalize a copy!!! (add unit test)
|
123
|
+
xfilename = filename.dup()
|
124
|
+
# gmosx: replace some dangerous chars!
|
125
|
+
xfilename.gsub!(/ /, "-")
|
126
|
+
xfilename.gsub!(/!/, "")
|
127
|
+
xfilename.gsub!(/'/, "")
|
128
|
+
xfilename.gsub!(/\(/, "")
|
129
|
+
xfilename.gsub!(/\)/, "")
|
130
|
+
# xfilename = self.to_greeklish(xfilename)
|
131
|
+
return xfilename
|
132
|
+
end
|
133
|
+
=end
|
134
|
+
|
135
|
+
# Returns a random string. one possible use is
|
136
|
+
# password initialization.
|
137
|
+
#
|
138
|
+
# === Input:
|
139
|
+
# the maximum length of the string
|
140
|
+
#
|
141
|
+
# === Output:
|
142
|
+
# the random string
|
143
|
+
|
144
|
+
def self.random(max_length = 8, char_re = /[\w\d]/)
|
145
|
+
# gmosx: this is a nice example of input parameter checking.
|
146
|
+
# this is NOT a real time called method so we can add this
|
147
|
+
# check. Congrats to the author.
|
148
|
+
raise ArgumentError.new('char_re must be a regular expression!') unless char_re.is_a?(Regexp)
|
149
|
+
|
150
|
+
string = ""
|
151
|
+
|
152
|
+
while string.length < max_length
|
153
|
+
ch = rand(255).chr
|
154
|
+
string << ch if ch =~ char_re
|
155
|
+
end
|
156
|
+
|
157
|
+
return string
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
data/lib/glue/time.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
+
# $Id: time.rb 282 2005-03-10 12:24:53Z gmosx $
|
4
|
+
|
5
|
+
require 'time.rb'
|
6
|
+
|
7
|
+
module N;
|
8
|
+
|
9
|
+
# General time utilities collection
|
10
|
+
#
|
11
|
+
# Implement as a module to avoid class polution. You can
|
12
|
+
# still Ruby's advanced features to include the module in your
|
13
|
+
# class. Passing the object to act upon allows to check for nil,
|
14
|
+
# which isn't possible if you use self.
|
15
|
+
#
|
16
|
+
# == TODO
|
17
|
+
#
|
18
|
+
# - SOS: add test units.
|
19
|
+
# - add aliases for those methods in Kernel ?
|
20
|
+
|
21
|
+
module TimeUtils
|
22
|
+
|
23
|
+
NOW = Time.now
|
24
|
+
NEVER = Time.mktime(2038)
|
25
|
+
ZERO = Time.mktime(1972)
|
26
|
+
|
27
|
+
# Convert the time to a nice String representation.
|
28
|
+
|
29
|
+
def self.date_time(time)
|
30
|
+
return nil unless time
|
31
|
+
return time.strftime("%d-%m-%Y %H:%M")
|
32
|
+
end
|
33
|
+
|
34
|
+
# This method calculates the days extrema given two time objects.
|
35
|
+
# start time is the given time1 at 00:00:00
|
36
|
+
# end time is the given time2 at 23:59:59:999
|
37
|
+
#
|
38
|
+
# Input:
|
39
|
+
# - the two times (if only time1 is provided then you get an extrema
|
40
|
+
# of exactly one day extrema.
|
41
|
+
#
|
42
|
+
# Output
|
43
|
+
# - the time range. you can get the start/end times using
|
44
|
+
# range methods.
|
45
|
+
|
46
|
+
def self.days_extrema(time1, time2=nil)
|
47
|
+
time2 = time1 if (not time2.valid? Time)
|
48
|
+
time2 = NEVER if (time2 <= time1)
|
49
|
+
start_time = Time.self.start_of_day(time1)
|
50
|
+
end_time = self.end_of_day(time2)
|
51
|
+
return (start_time..end_time)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set time to start of day
|
55
|
+
|
56
|
+
def self.start_of_day(time)
|
57
|
+
return Time.mktime(time.year, time.month, time.day, 0, 0, 0, 0)
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Set time to end of day
|
62
|
+
|
63
|
+
def self.end_of_day(time)
|
64
|
+
return Time.mktime(time.year, time.month, time.day, 23, 59, 59, 999)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Returns true only if day of time is included in the
|
69
|
+
# range (stime..etime). Only year days are checked.
|
70
|
+
|
71
|
+
def self.time_in_day_range(time, stime=ZERO, etime=NEVER)
|
72
|
+
if (etime <= stime)
|
73
|
+
Logger.debug "Invalid end time (#{etime} < #{stime})" if $DBG
|
74
|
+
etime = NEVER
|
75
|
+
end
|
76
|
+
|
77
|
+
stime = start_of_day(stime)
|
78
|
+
etime = end_of_day(etime)
|
79
|
+
|
80
|
+
return (stime..etime).include?(time)
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,461 @@
|
|
1
|
+
# * George Moschovitis <gm@navel.gr>
|
2
|
+
# (c) 2004-2005 Navel, all rights reserved.
|
3
|
+
# $Id$
|
4
|
+
|
5
|
+
module N
|
6
|
+
|
7
|
+
# Implements a meta-language for validating managed
|
8
|
+
# objects. Typically used in Validator objects but can be
|
9
|
+
# included in managed objects too.
|
10
|
+
#
|
11
|
+
# The following validation macros are available:
|
12
|
+
#
|
13
|
+
# * validate_value
|
14
|
+
# * validate_confirmation
|
15
|
+
# * validate_format
|
16
|
+
# * validate_length
|
17
|
+
# * validate_inclusion
|
18
|
+
#
|
19
|
+
# Og/Database specific validation methods are added in the
|
20
|
+
# file og/validation.rb
|
21
|
+
#
|
22
|
+
# === Example
|
23
|
+
#
|
24
|
+
# class User
|
25
|
+
# prop_accessor :name, String
|
26
|
+
# prop_accessor :level, Fixnum
|
27
|
+
#
|
28
|
+
# validate_length :name, :range => 2..6
|
29
|
+
# validate_unique :name, :msg => :name_allready_exists
|
30
|
+
# validate_format :name, :format => /[a-z]*/, :msg => 'invalid format', :on => :create
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# class N::CustomUserValidator
|
34
|
+
# include N::Validation
|
35
|
+
# validate_length :name, :range => 2..6, :msg_short => :name_too_short, :msg_long => :name_too_long
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# user = @request.fill(User.new)
|
39
|
+
# user.level = 15
|
40
|
+
#
|
41
|
+
# unless user.valid?
|
42
|
+
# user.save
|
43
|
+
# else
|
44
|
+
# p user.errors[:name]
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# unless user.save
|
48
|
+
# p user.errors.on(:name)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# unless errors = N::CustomUserValidator.errors(user)
|
52
|
+
# user.save
|
53
|
+
# else
|
54
|
+
# p errors[:name]
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
#--
|
58
|
+
# TODO: all validation methods should imply validate_value
|
59
|
+
# TODO: add validate_unique
|
60
|
+
#++
|
61
|
+
|
62
|
+
module Validation
|
63
|
+
|
64
|
+
# Encapsulates a list of validation errors.
|
65
|
+
|
66
|
+
class Errors
|
67
|
+
attr_accessor :errors
|
68
|
+
|
69
|
+
cattr_accessor :no_value, 'No value provided'
|
70
|
+
cattr_accessor :no_confirmation, 'Invalid confirmation'
|
71
|
+
cattr_accessor :invalid_format, 'Invalid format'
|
72
|
+
cattr_accessor :too_short, 'Too short, must be more than %d characters long'
|
73
|
+
cattr_accessor :too_long, 'Too long, must be less than %d characters long'
|
74
|
+
cattr_accessor :invalid_length, 'Must be %d characters long'
|
75
|
+
cattr_accessor :no_inclusion, 'The value is invalid'
|
76
|
+
cattr_accessor :no_numeric, 'The value must be numeric'
|
77
|
+
cattr_accessor :not_unique, 'The value is already used'
|
78
|
+
|
79
|
+
def initialize
|
80
|
+
@errors = {}
|
81
|
+
end
|
82
|
+
|
83
|
+
def add(attr, message)
|
84
|
+
(@errors[attr] ||= []) << message
|
85
|
+
end
|
86
|
+
|
87
|
+
def on(attr)
|
88
|
+
@errors[attr]
|
89
|
+
end
|
90
|
+
alias_method :[], :on
|
91
|
+
|
92
|
+
# Yields each attribute and associated message.
|
93
|
+
|
94
|
+
def each
|
95
|
+
@errors.each_key do |attr|
|
96
|
+
@errors[attr].each { |msg| yield attr, msg }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def size
|
101
|
+
@errors.size
|
102
|
+
end
|
103
|
+
alias_method :count, :size
|
104
|
+
|
105
|
+
def empty?
|
106
|
+
@errors.empty?
|
107
|
+
end
|
108
|
+
|
109
|
+
def clear
|
110
|
+
@errors.clear
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# If the validate method returns true, this
|
115
|
+
# attributes holds the errors found.
|
116
|
+
|
117
|
+
attr_accessor :errors
|
118
|
+
|
119
|
+
# Call the #validate method for this object.
|
120
|
+
# If validation errors are found, sets the
|
121
|
+
# @errors attribute to the Errors object and
|
122
|
+
# returns true.
|
123
|
+
|
124
|
+
def valid?
|
125
|
+
begin
|
126
|
+
@errors = self.class.validate(self)
|
127
|
+
return @errors.empty?
|
128
|
+
rescue NoMethodError => e
|
129
|
+
# gmosx: hmm this is potentially dangerous.
|
130
|
+
N::Validation.eval_validate(self.class)
|
131
|
+
retry
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Evaluate the 'validate' method for the calling
|
136
|
+
# class.
|
137
|
+
#
|
138
|
+
# WARNING: for the moment only evaluates for
|
139
|
+
# on == :save
|
140
|
+
|
141
|
+
def self.eval_validate(klass)
|
142
|
+
code = %{
|
143
|
+
def self.validate(obj, on = :save)
|
144
|
+
errors = Errors.new
|
145
|
+
}
|
146
|
+
|
147
|
+
for val, on in klass.__meta[:validations]
|
148
|
+
code << %{
|
149
|
+
#{val}
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
code << %{
|
154
|
+
return errors
|
155
|
+
end
|
156
|
+
}
|
157
|
+
|
158
|
+
klass.module_eval(code)
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.append_features(base)
|
162
|
+
super
|
163
|
+
|
164
|
+
base.module_eval <<-"end_eval", __FILE__, __LINE__
|
165
|
+
meta :validations, []
|
166
|
+
end_eval
|
167
|
+
|
168
|
+
base.extend(MetaLanguage)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Implements the Validation meta-language.
|
172
|
+
|
173
|
+
module MetaLanguage
|
174
|
+
|
175
|
+
# the postfix attached to confirmation attributes.
|
176
|
+
|
177
|
+
mattr_accessor :confirmation_postfix, '_confirmation'
|
178
|
+
|
179
|
+
# Validates that the attributes have a values, ie they are
|
180
|
+
# neither nil or empty.
|
181
|
+
#
|
182
|
+
# === Example
|
183
|
+
#
|
184
|
+
# validate_value :param, :msg => 'No confirmation'
|
185
|
+
|
186
|
+
def validate_value(*params)
|
187
|
+
c = {
|
188
|
+
:msg => N::Validation::Errors.no_value,
|
189
|
+
:on => :save
|
190
|
+
}
|
191
|
+
c.update(params.pop) if params.last.is_a?(Hash)
|
192
|
+
|
193
|
+
idx = 0
|
194
|
+
for name in params
|
195
|
+
code = %{
|
196
|
+
if obj.#{name}.nil?
|
197
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
198
|
+
elsif obj.#{name}.respond_to?(:empty?)
|
199
|
+
errors.add(:#{name}, '#{c[:msg]}') if obj.#{name}.empty?
|
200
|
+
end
|
201
|
+
}
|
202
|
+
|
203
|
+
__meta[:validations] << [code, c[:on]]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Validates the confirmation of +String+ attributes.
|
208
|
+
#
|
209
|
+
# === Example
|
210
|
+
#
|
211
|
+
# validate_confirmation :password, :msg => 'No confirmation'
|
212
|
+
|
213
|
+
def validate_confirmation(*params)
|
214
|
+
c = {
|
215
|
+
:msg => N::Validation::Errors.no_confirmation,
|
216
|
+
:postfix => N::Validation::MetaLanguage.confirmation_postfix,
|
217
|
+
:on => :save
|
218
|
+
}
|
219
|
+
c.update(params.pop) if params.last.is_a?(Hash)
|
220
|
+
|
221
|
+
|
222
|
+
for name in params
|
223
|
+
confirm_name = "#{name}#{c[:postfix]}"
|
224
|
+
eval "attr_accessor :#{confirm_name}"
|
225
|
+
|
226
|
+
code = %{
|
227
|
+
if obj.#{confirm_name}.nil? or (obj.#{confirm_name} != obj.#{name})
|
228
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
229
|
+
end
|
230
|
+
}
|
231
|
+
|
232
|
+
__meta[:validations] << [code, c[:on]]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Validates the format of +String+ attributes.
|
237
|
+
# WARNING: regexp options are ignored.
|
238
|
+
#
|
239
|
+
# === Example
|
240
|
+
#
|
241
|
+
# validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
|
242
|
+
#
|
243
|
+
#--
|
244
|
+
# FIXME: how to get the Regexp options?
|
245
|
+
#++
|
246
|
+
|
247
|
+
def validate_format(*params)
|
248
|
+
c = {
|
249
|
+
:format => nil,
|
250
|
+
:msg_no_value => N::Validation::Errors.no_value,
|
251
|
+
:msg => N::Validation::Errors.invalid_format,
|
252
|
+
:on => :save
|
253
|
+
}
|
254
|
+
c.update(params.pop) if params.last.is_a?(Hash)
|
255
|
+
|
256
|
+
unless c[:format].is_a?(Regexp)
|
257
|
+
raise(ArgumentError,
|
258
|
+
'A regular expression must be supplied as the :format option')
|
259
|
+
end
|
260
|
+
|
261
|
+
for name in params
|
262
|
+
code = %{
|
263
|
+
if obj.#{name}.nil?
|
264
|
+
errors.add(:#{name}, '#{c[:msg_no_value]}')
|
265
|
+
else
|
266
|
+
unless obj.#{name}.to_s.match(/#{c[:format].source}/)
|
267
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
268
|
+
end
|
269
|
+
end;
|
270
|
+
}
|
271
|
+
|
272
|
+
__meta[:validations] << [code, c[:on]]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# Validates the length of +String+ attributes.
|
277
|
+
#
|
278
|
+
# === Example
|
279
|
+
#
|
280
|
+
# validate_length :name, :max => 30, :msg => 'Too long'
|
281
|
+
# validate_length :name, :min => 2, :msg => 'Too sort'
|
282
|
+
# validate_length :name, :range => 2..30
|
283
|
+
# validate_length :name, :length => 15, :msg => 'Name should be %d chars long'
|
284
|
+
|
285
|
+
def validate_length(*params)
|
286
|
+
c = {
|
287
|
+
:min => nil, :max => nil, :range => nil, :length => nil,
|
288
|
+
:msg => nil,
|
289
|
+
:msg_no_value => N::Validation::Errors.no_value,
|
290
|
+
:msg_short => N::Validation::Errors.too_short,
|
291
|
+
:msg_long => N::Validation::Errors.too_long,
|
292
|
+
:on => :save
|
293
|
+
}
|
294
|
+
c.update(params.pop) if params.last.is_a?(Hash)
|
295
|
+
|
296
|
+
min, max = c[:min], c[:max]
|
297
|
+
range, length = c[:range], c[:length]
|
298
|
+
|
299
|
+
count = 0
|
300
|
+
[min, max, range, length].each { |o| count += 1 if o }
|
301
|
+
|
302
|
+
if count == 0
|
303
|
+
raise(ArgumentError,
|
304
|
+
'One of :min, :max, :range, :length must be provided!')
|
305
|
+
end
|
306
|
+
|
307
|
+
if count > 1
|
308
|
+
raise(ArgumentError,
|
309
|
+
'The :min, :max, :range, :length options are mutually exclusive!')
|
310
|
+
end
|
311
|
+
|
312
|
+
for name in params
|
313
|
+
if min
|
314
|
+
c[:msg] ||= N::Validation::Errors.too_short
|
315
|
+
code = %{
|
316
|
+
if obj.#{name}.nil?
|
317
|
+
errors.add(:#{name}, '#{c[:msg_no_value]}')
|
318
|
+
elsif obj.#{name}.to_s.length < #{min}
|
319
|
+
msg = '#{c[:msg]}'
|
320
|
+
msg = (msg % #{min}) rescue msg
|
321
|
+
errors.add(:#{name}, msg)
|
322
|
+
end;
|
323
|
+
}
|
324
|
+
elsif max
|
325
|
+
c[:msg] ||= N::Validation::Errors.too_long
|
326
|
+
code = %{
|
327
|
+
if obj.#{name}.nil?
|
328
|
+
errors.add(:#{name}, '#{c[:msg_no_value]}')
|
329
|
+
elsif obj.#{name}.to_s.length > #{max}
|
330
|
+
msg = '#{c[:msg]}'
|
331
|
+
msg = (msg % #{max}) rescue msg
|
332
|
+
errors.add(:#{name}, msg)
|
333
|
+
end;
|
334
|
+
}
|
335
|
+
elsif range
|
336
|
+
code = %{
|
337
|
+
if obj.#{name}.nil?
|
338
|
+
errors.add(:#{name}, '#{c[:msg_no_value]}')
|
339
|
+
elsif obj.#{name}.to_s.length < #{range.first}
|
340
|
+
msg = '#{c[:msg_short]}'
|
341
|
+
msg = (msg % #{range.first}) rescue msg
|
342
|
+
errors.add(:#{name}, msg)
|
343
|
+
elsif obj.#{name}.to_s.length > #{range.last}
|
344
|
+
msg = '#{c[:msg_long]}'
|
345
|
+
msg = (msg % #{range.last}) rescue msg
|
346
|
+
errors.add(:#{name}, msg)
|
347
|
+
end;
|
348
|
+
}
|
349
|
+
elsif length
|
350
|
+
c[:msg] ||= N::Validation::Errors.invalid_length
|
351
|
+
code = %{
|
352
|
+
if obj.#{name}.nil?
|
353
|
+
errors.add(:#{name}, '#{c[:msg_no_value]}')
|
354
|
+
elsif obj.#{name}.to_s.length != #{length}
|
355
|
+
msg = '#{c[:msg]}'
|
356
|
+
msg = (msg % #{length}) rescue msg
|
357
|
+
errors.add(:#{name}, msg)
|
358
|
+
end;
|
359
|
+
}
|
360
|
+
end
|
361
|
+
|
362
|
+
__meta[:validations] << [code, c[:on]]
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
# Validates that the attributes are included in
|
367
|
+
# an enumeration.
|
368
|
+
#
|
369
|
+
# === Example
|
370
|
+
#
|
371
|
+
# validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
|
372
|
+
# validate_inclusion :age, :in => 5..99
|
373
|
+
|
374
|
+
def validate_inclusion(*params)
|
375
|
+
c = {
|
376
|
+
:in => nil,
|
377
|
+
:msg => N::Validation::Errors.no_inclusion,
|
378
|
+
:allow_nil => false,
|
379
|
+
:on => :save
|
380
|
+
}
|
381
|
+
c.update(params.pop) if params.last.is_a?(Hash)
|
382
|
+
|
383
|
+
unless c[:in].respond_to?('include?')
|
384
|
+
raise(ArgumentError,
|
385
|
+
'An object that responds to #include? must be supplied as the :in option')
|
386
|
+
end
|
387
|
+
|
388
|
+
for name in params
|
389
|
+
if c[:allow_nil]
|
390
|
+
code = %{
|
391
|
+
unless obj.#{name}.nil? or (#{c[:in].inspect}).include?(obj.#{name})
|
392
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
393
|
+
end;
|
394
|
+
}
|
395
|
+
else
|
396
|
+
code = %{
|
397
|
+
unless (#{c[:in].inspect}).include?(obj.#{name})
|
398
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
399
|
+
end;
|
400
|
+
}
|
401
|
+
end
|
402
|
+
|
403
|
+
__meta[:validations] << [code, c[:on]]
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
# Validates that the attributes have numeric values.
|
408
|
+
#
|
409
|
+
# [+:integer+]
|
410
|
+
# Only accept integers
|
411
|
+
#
|
412
|
+
# [+:msg]
|
413
|
+
# Custom message
|
414
|
+
#
|
415
|
+
# [+:on:]
|
416
|
+
# When to validate
|
417
|
+
#
|
418
|
+
# === Example
|
419
|
+
#
|
420
|
+
# validate_numeric :age, :msg => 'Must be an integer'
|
421
|
+
|
422
|
+
def validate_numeric(*params)
|
423
|
+
c = {
|
424
|
+
:integer => false,
|
425
|
+
:msg => N::Validation::Errors.no_numeric,
|
426
|
+
:on => :save
|
427
|
+
}
|
428
|
+
c.update(params.pop) if params.last.is_a?(Hash)
|
429
|
+
|
430
|
+
for name in params
|
431
|
+
if c[:integer]
|
432
|
+
code = %{
|
433
|
+
unless obj.#{name}.to_s =~ /^[\\+\\-]?\\d+$/
|
434
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
435
|
+
end;
|
436
|
+
}
|
437
|
+
else
|
438
|
+
code = %{
|
439
|
+
begin
|
440
|
+
Kernel.Float(obj.#{name})
|
441
|
+
rescue ArgumentError, TypeError
|
442
|
+
errors.add(:#{name}, '#{c[:msg]}')
|
443
|
+
end;
|
444
|
+
}
|
445
|
+
end
|
446
|
+
|
447
|
+
__meta[:validations] << [code, c[:on]]
|
448
|
+
end
|
449
|
+
end
|
450
|
+
alias_method :validate_numericality, :validate_numeric
|
451
|
+
|
452
|
+
end
|
453
|
+
|
454
|
+
end
|
455
|
+
|
456
|
+
end
|
457
|
+
|
458
|
+
class Module # :nodoc: all
|
459
|
+
include N::Validation::MetaLanguage
|
460
|
+
end
|
461
|
+
|