glue 0.41.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/History.txt +6 -0
  2. data/Manifest.txt +6 -0
  3. data/README.txt +130 -0
  4. data/Rakefile +16 -0
  5. data/lib/glue.rb +49 -72
  6. data/test/test_glue.rb +218 -0
  7. metadata +84 -100
  8. data/doc/AUTHORS +0 -13
  9. data/doc/CHANGELOG.1 +0 -354
  10. data/doc/LICENSE +0 -32
  11. data/doc/RELEASES +0 -207
  12. data/lib/glue/attribute.rb +0 -113
  13. data/lib/glue/attributeutils.rb +0 -117
  14. data/lib/glue/autoreload.rb +0 -60
  15. data/lib/glue/builder.rb +0 -57
  16. data/lib/glue/builder/xml.rb +0 -103
  17. data/lib/glue/cache.rb +0 -22
  18. data/lib/glue/cache/drb.rb +0 -51
  19. data/lib/glue/cache/file.rb +0 -78
  20. data/lib/glue/cache/memcached.rb +0 -68
  21. data/lib/glue/cache/memory.rb +0 -79
  22. data/lib/glue/cache/og.rb +0 -61
  23. data/lib/glue/configuration.rb +0 -305
  24. data/lib/glue/fixture.rb +0 -154
  25. data/lib/glue/html.rb +0 -12
  26. data/lib/glue/localization.rb +0 -129
  27. data/lib/glue/logger.rb +0 -208
  28. data/lib/glue/mail.rb +0 -160
  29. data/lib/glue/mailer.rb +0 -55
  30. data/lib/glue/mailer/incoming.rb +0 -41
  31. data/lib/glue/mailer/outgoing.rb +0 -119
  32. data/lib/glue/settings.rb +0 -3
  33. data/lib/glue/uri.rb +0 -190
  34. data/lib/glue/validation.rb +0 -447
  35. data/lib/html/document.rb +0 -63
  36. data/lib/html/node.rb +0 -480
  37. data/lib/html/tokenizer.rb +0 -103
  38. data/lib/html/version.rb +0 -11
  39. data/test/fixture/article.csv +0 -3
  40. data/test/fixture/article.yml +0 -13
  41. data/test/fixture/user.yml +0 -12
  42. data/test/glue/builder/tc_xml.rb +0 -57
  43. data/test/glue/tc_aspects.rb +0 -99
  44. data/test/glue/tc_attribute.rb +0 -112
  45. data/test/glue/tc_attribute_mixins.rb +0 -86
  46. data/test/glue/tc_builder.rb +0 -30
  47. data/test/glue/tc_configuration.rb +0 -135
  48. data/test/glue/tc_fixture.rb +0 -98
  49. data/test/glue/tc_localization.rb +0 -49
  50. data/test/glue/tc_logger.rb +0 -43
  51. data/test/glue/tc_mail.rb +0 -99
  52. data/test/glue/tc_stores.rb +0 -16
  53. data/test/glue/tc_uri.rb +0 -97
  54. data/test/glue/tc_validation.rb +0 -217
  55. data/test/public/dummy_mailer/registration.xhtml +0 -5
data/lib/glue/mailer.rb DELETED
@@ -1,55 +0,0 @@
1
- require 'glue/mail'
2
- require 'glue/mailer/incoming'
3
- require 'glue/mailer/outgoing'
4
- require 'glue/configuration'
5
-
6
- require 'socket'
7
-
8
- module Glue
9
-
10
- # Handles incoming and outgoing emails. Can be called from
11
- # a Controller or a standalone script (target of the MTA).
12
-
13
- class Mailer < Mail
14
- include IncomingMailer
15
- include OutgoingMailer
16
-
17
- # The outgoing mail server configuration.
18
-
19
- setting :server, :default => {
20
- :address => 'localhost',
21
- :port => 25,
22
- :domain => Socket.gethostname,
23
- :username => nil,
24
- :password => nil,
25
- :authentication => nil
26
- }, :doc => 'The outgoing server configuration'
27
-
28
- # The delivery method. The following options are
29
- # supported:
30
- #
31
- # * :smtp
32
- # * :sendmail
33
- # * :test
34
-
35
- setting :delivery_method, :default => :smtp, :doc => 'The delivery method'
36
-
37
- # Disable deliveries, useful for testing.
38
-
39
- setting :disable_deliveries, :default => false, :doc => 'Dissable deliveries?'
40
-
41
- # The default template root.
42
-
43
- setting :template_root, :default => 'template/mail', :doc => 'The default template root'
44
-
45
- # The default from address
46
-
47
- setting :from, :default => 'bot@nitroproject.org', :doc => 'The default from address'
48
-
49
- # An array to store the delivered mails, useful
50
- # for testing.
51
-
52
- cattr_accessor :deliveries; @@deliveries = []
53
- end
54
-
55
- end
@@ -1,41 +0,0 @@
1
- module Glue
2
-
3
- # Add support for incoming mail handling.
4
- #
5
- # You need to setup your MTA to support incoming email
6
- # handling. Here is an example for Postfix. Edit these files:
7
- #
8
- # /etc/postfix/master.cf:
9
- # mailman unix - n n - - pipe
10
- # flags= user=nobody argv=/path/to/ruby /path/to/app/script/runner.rb Mailer.receive(STDIN)
11
- #
12
- # /etc/postfix/main.cf:
13
- # transport_maps = hash:/etc/postfix/transport
14
- # virtual_mailbox_domains = lists.yourdomain.com
15
- #
16
- # /etc/postfix/transport:
17
- # lists.yourdomain.com mailman:
18
- #
19
- # Then run:
20
- #
21
- # sudo postmap transport
22
- # sudo postfix stop
23
- # sudo postfix start
24
-
25
- module IncomingMailer
26
- on_included %{ base.extend ClassMethods }
27
-
28
- # You can overide this class for specialized handling.
29
-
30
- def receive(mail)
31
- end
32
-
33
- module ClassMethods
34
- def receive(encoded)
35
- mail = Glue::Mail.new_from_encoded(encoded)
36
- self.new.receive(mail)
37
- end
38
- end
39
- end
40
-
41
- end
@@ -1,119 +0,0 @@
1
- module Glue
2
-
3
- # Add support for outgoing mail handling.
4
-
5
- module OutgoingMailer
6
- # The root directory where the templates reside.
7
-
8
- attr_accessor :template_root
9
-
10
- on_included %{ base.extend ClassMethods }
11
-
12
- def initialize(from = nil, to = nil, subject = nil, body = Nitro::FileTemplate.new)
13
- super
14
- @charset = Mailer.default_charset.dup
15
- @encode_subject = Mailer.encode_subject
16
- @template_root = Mailer.template_root
17
- end
18
-
19
- module ClassMethods
20
- def method_missing(method_symbol, *params) #:nodoc:
21
- case method_symbol.id2name
22
- when /^create_([_a-z]*)/
23
- create_from_method($1, *params)
24
- when /^deliver_([_a-z]*)/
25
- begin
26
- deliver(send("create_" + $1, *params))
27
- rescue Object => e
28
- raise e # FIXME
29
- end
30
- end
31
- end
32
-
33
- def mail(from, to, subject, body, timestamp = nil, headers = {}, encode = Mail.encode_subject, charset = Mail.default_charset) #:nodoc:
34
- deliver(create(from, to, subject, body, timestamp, headers, charset))
35
- end
36
-
37
- def create(from, to, subject, body, timestamp = nil, headers = {}, encode = Mail.encode_subject, charset = Mail.default_charset) #:nodoc:
38
- m = Mail.new
39
- m.to, m.subject, m.body, m.from = to, ( encode ? quoted_printable(subject, charset) : subject ), body, from
40
- # m.date = timestamp.respond_to?("to_time") ? timestamp.to_time : (timestamp || Time.now)
41
- # m.set_content_type "text", "plain", { "charset" => charset }
42
- headers.each do |k, v|
43
- m[k] = v
44
- end
45
- return m
46
- end
47
-
48
- def deliver(mail) #:nodoc:
49
- # gmosx, FIXME: for some STUPID reason, delivery_method
50
- # returns nil, investigate.
51
- Mailer.delivery_method = :smtp
52
- unless Mailer.disable_deliveries
53
- send("perform_delivery_#{Mailer.delivery_method}", mail)
54
- end
55
- end
56
-
57
- def quoted_printable(text, charset) #:nodoc:
58
- text = text.gsub( /[^a-z ]/i ) { "=%02x" % $&[0] }.gsub( / /, "_" )
59
- "=?#{charset}?Q?#{text}?="
60
- end
61
-
62
- private
63
-
64
- def create_from_method(method_name, *params)
65
- mailer = new
66
-
67
- mailer.send(method_name, *params)
68
-
69
- unless mailer.body.is_a? String
70
- mailer.body = render_body(method_name, mailer)
71
- end
72
-
73
- mail = create(
74
- mailer.from, mailer.to, mailer.subject,
75
- mailer.body, mailer.sent_on,
76
- mailer.headers, mailer.charset
77
- )
78
-
79
- mail.cc = mailer.cc if mailer.cc
80
- mail.bcc = mailer.bcc if mailer.bcc
81
-
82
- return mail
83
- end
84
-
85
- # Render the body by expanding the template
86
-
87
- def render_body(method_name, mailer)
88
- mailer.body.template_filename = "#{mailer.template_root}/#{method_name.to_s}.xhtml"
89
- return mailer.body.process
90
- end
91
-
92
- # Deliver emails using SMTP.
93
-
94
- def perform_delivery_smtp(mail) # :nodoc:
95
- c = Mailer.server
96
- Net::SMTP.start(c[:address], c[:port], c[:domain], c[:username], c[:password], c[:authentication]) do |smtp|
97
- smtp.send_message(mail.encoded, mail.from, *[mail.to, mail.cc, mail.bcc].compact)
98
- end
99
- end
100
-
101
- # Deliver emails using sendmail.
102
-
103
- def perform_delivery_sendmail(mail) # :nodoc:
104
- IO.popen('/usr/sbin/sendmail -i -t', 'w+') do |sm|
105
- sm.print(mail.encoded)
106
- sm.flush
107
- end
108
- end
109
-
110
- # Used for testing, does not actually send the
111
- # mail.
112
-
113
- def perform_delivery_test(mail) # :nodoc:
114
- deliveries << mail
115
- end
116
- end
117
- end
118
-
119
- end
data/lib/glue/settings.rb DELETED
@@ -1,3 +0,0 @@
1
- # Just an alias for glue/configuration.
2
-
3
- require 'glue/configuration'
data/lib/glue/uri.rb DELETED
@@ -1,190 +0,0 @@
1
- require 'uri'
2
- require 'cgi'
3
-
4
- require 'facet/string/blank'
5
-
6
- module Glue
7
-
8
- # URI utilities collection.
9
- #
10
- # === Design
11
- #
12
- # Implement as a module to avoid class polution. You can still
13
- # use Ruby's advanced features to include the module in your
14
- # class. Passing the object to act upon allows to check for nil,
15
- # which isn't possible if you use self.
16
- #
17
- # The uris passed as parameters are typically strings.
18
- #--
19
- # gmosx, TODO: deprecate this.
20
- #++
21
-
22
- module UriUtils
23
-
24
- # Decode the uri components.
25
-
26
- def self.decode(uri)
27
- # gmosx: hmm is this needed?
28
- # guard against invalid filenames for example pictures with
29
- # spaces uploaded by users
30
- escaped_uri = uri.gsub(/ /, "+")
31
-
32
- if md = URI::REGEXP::REL_URI.match(escaped_uri)
33
-
34
- path = "#{md[5]}#{md[6]}"
35
- type = File.extname(path)
36
- query_string = md[7]
37
-
38
- # real_path = "#{$root_dir}/#{path}"
39
-
40
- parameters = Glue::UriUtils.query_string_to_hash(query_string)
41
- path.gsub!(/\+/, " ")
42
-
43
- return [path, type, parameters, query_string]
44
-
45
- end # match
46
-
47
- # this is usefull for uncovering bugs!
48
- raise ArgumentError.new("the parameter '#{uri}' is not a valid uri")
49
- end
50
-
51
- # Extend the basic query string parser provided by the cgi module.
52
- # converts single valued params (the most common case) to
53
- # objects instead of arrays
54
- #
55
- # Input:
56
- # the query string
57
- #
58
- # Output:
59
- # hash of parameters, contains arrays for multivalued parameters
60
- # (multiselect, checkboxes , etc)
61
- # If no query string is provided (nil or "") returns an empty hash.
62
-
63
- def self.query_string_to_hash(query_string)
64
- return {} unless query_string
65
-
66
- query_parameters = CGI::parse(query_string)
67
-
68
- query_parameters.each { |key, val|
69
- # replace the array with an object
70
- query_parameters[key] = val[0] if 1 == val.length
71
- }
72
-
73
- # set default value to nil! cgi sets this to []
74
- query_parameters.default = nil
75
-
76
- return query_parameters
77
- end
78
-
79
- # Given a hash with parameter/value pairs construct a
80
- # standard query string. This method only encodes simple
81
- # types (Numeric, String) to avoid query string polution
82
- # with marshal, etc.
83
- #
84
- # gmosx, FIXME: only numeric and strings are passed to
85
- # the latest code, so update old code and optimize this!
86
- #
87
- # Input:
88
- # the parameter hash
89
- #
90
- # Output:
91
- # the query string
92
-
93
- def self.hash_to_query_string(parameters)
94
- return nil unless parameters
95
- pairs = []
96
- parameters.each { |param, value|
97
- # only encode simple classes !
98
-
99
- if value.is_a?(Numeric) or value.is_a?(String)
100
- pairs << "#{param}=#{CGI.escape(value.to_s)}"
101
- end
102
- }
103
- return pairs.join(";")
104
- end
105
-
106
- # This method returns the query string of a uri
107
- #
108
- # Input:
109
- # the uri
110
- #
111
- # Output:
112
- # the query string.
113
- # returns nil if no query string
114
-
115
- def self.get_query_string(uri)
116
- return nil unless uri
117
- # gmosx: INVESTIGATE ruby's URI seems to differently handle
118
- # abs and rel uris.
119
- if md = URI::REGEXP::ABS_URI.match(uri)
120
- return md[8]
121
- elsif md = URI::REGEXP::REL_URI.match(uri)
122
- return md[7]
123
- end
124
- return nil
125
- end
126
-
127
- # Removes the query string from a uri
128
- #
129
- # Input:
130
- # the uri
131
- #
132
- # Output:
133
- # the chomped uri.
134
-
135
- def self.chomp_query_string(uri)
136
- return nil unless uri
137
- query_string = self.get_query_string(uri)
138
- return uri.dup.chomp("?#{query_string}")
139
- end
140
-
141
- # Get a uri and a hash of parameters. Inject the hash values
142
- # as parameters in the query sting path. Returns the full
143
- # uri.
144
- #
145
- # Input:
146
- # the uri to filter (String)
147
- # hash of parameters to update
148
- #
149
- # Output:
150
- # the full updated query string
151
- #
152
- # === TODO:
153
- # optimize this a litle bit.
154
-
155
- def self.update_query_string(uri, parameters)
156
- query_string = self.get_query_string(uri)
157
- rest = uri.dup.gsub(/\?#{query_string}/, "")
158
-
159
- hash = self.query_string_to_hash(query_string)
160
- hash.update(parameters)
161
- query_string = self.hash_to_query_string(hash)
162
-
163
- unless query_string.blank?
164
- return "#{rest}?#{query_string}"
165
- else
166
- return rest
167
- end
168
- end
169
-
170
- # TODO: find a better name.
171
- # Gets the request uri, injects extra parameters in the query string
172
- # and returns a new uri. The request object is not modified.
173
- # There is always a qs string so an extra test is skipped.
174
-
175
- def self.update_request_uri(request, parameters)
176
- hash = request.parameters.dup()
177
- hash.update(parameters)
178
-
179
- # use this in hash_to_querystring.
180
- query_string = hash.collect { |k, v|
181
- "#{k}=#{v}"
182
- }.join(";")
183
-
184
- return "#{request.translated_uri}?#{query_string}"
185
- end
186
-
187
- end
188
-
189
- end
190
-
@@ -1,447 +0,0 @@
1
- require 'facets/core/class/cattr'
2
- require 'facets/core/module/on_included'
3
- require 'facets/more/inheritor'
4
-
5
- require 'glue/configuration'
6
-
7
- module Glue
8
-
9
- # Implements a meta-language for validating managed
10
- # objects. Typically used in Validator objects but can be
11
- # included in managed objects too.
12
- #
13
- # Additional og-related validation macros can be found
14
- # in lib/og/validation.
15
- #
16
- # The following validation macros are available:
17
- #
18
- # * validate_value
19
- # * validate_confirmation
20
- # * validate_format
21
- # * validate_length
22
- # * validate_inclusion
23
- #
24
- # Og/Database specific validation methods are added in the
25
- # file og/validation.rb
26
- #
27
- # === Example
28
- #
29
- # class User
30
- # attr_accessor :name, String
31
- # attr_accessor :level, Fixnum
32
- #
33
- # validate_length :name, :range => 2..6
34
- # validate_unique :name, :msg => :name_allready_exists
35
- # validate_format :name, :format => /[a-z]*/, :msg => 'invalid format', :on => :create
36
- # end
37
- #
38
- # class CustomUserValidator
39
- # include Validation
40
- # validate_length :name, :range => 2..6, :msg_short => :name_too_short, :msg_long => :name_too_long
41
- # end
42
- #
43
- # user = @request.fill(User.new)
44
- # user.level = 15
45
- #
46
- # unless user.valid?
47
- # user.save
48
- # else
49
- # p user.errors[:name]
50
- # end
51
- #
52
- # unless user.save
53
- # p user.errors.on(:name)
54
- # end
55
- #
56
- # unless errors = CustomUserValidator.errors(user)
57
- # user.save
58
- # else
59
- # p errors[:name]
60
- # end
61
- #--
62
- # TODO: all validation methods should imply validate_value.
63
- #++
64
-
65
- module Validation
66
-
67
- # The postfix attached to confirmation attributes.
68
-
69
- setting :confirmation_postfix, :default => '_confirmation', :doc => 'The postfix attached to confirmation attributes.'
70
-
71
- # Encapsulates a list of validation errors.
72
-
73
- class Errors
74
- attr_accessor :errors
75
-
76
- setting :no_value, :default => 'No value provided'
77
- setting :no_confirmation, :default => 'Invalid confirmation'
78
- setting :invalid_format, :default => 'Invalid format'
79
- setting :too_short, :default => 'Too short, must be more than %d characters long'
80
- setting :too_long, :default => 'Too long, must be less than %d characters long'
81
- setting :invalid_length, :default => 'Must be %d characters long'
82
- setting :no_inclusion, :default => 'The value is invalid'
83
- setting :no_numeric, :default => 'The value must be numeric'
84
- setting :not_unique, :default => 'The value is already used'
85
-
86
- def initialize
87
- @errors = {}
88
- end
89
-
90
- def add(attr, message)
91
- (@errors[attr] ||= []) << message
92
- end
93
-
94
- def on(attr)
95
- @errors[attr]
96
- end
97
- alias_method :[], :on
98
-
99
- # Yields each attribute and associated message.
100
-
101
- def each
102
- @errors.each_key do |attr|
103
- @errors[attr].each { |msg| yield attr, msg }
104
- end
105
- end
106
-
107
- def size
108
- @errors.size
109
- end
110
- alias_method :count, :size
111
-
112
- def empty?
113
- @errors.empty?
114
- end
115
-
116
- def clear
117
- @errors.clear
118
- end
119
-
120
- def to_a
121
- @errors.inject([]) { |a, kv| a << kv }
122
- end
123
-
124
- def join(glue)
125
- @errors.to_a.join(glue)
126
- end
127
- end
128
-
129
- # A Key is used to uniquely identify a validation rule.
130
-
131
- class Key
132
- attr_reader :validation
133
- attr_reader :field
134
-
135
- def initialize(validation, field)
136
- @validation, @field = validation.to_s, field.to_s
137
- end
138
-
139
- def hash
140
- "#{@validation}-#{@field}".hash
141
- end
142
-
143
- def ==(other)
144
- self.validation == other.validation and self.field == other.field
145
- end
146
- end
147
-
148
- # If the validate method returns true, this
149
- # attributes holds the errors found.
150
-
151
- attr_accessor :errors
152
-
153
- # Call the #validate method for this object.
154
- # If validation errors are found, sets the
155
- # @errors attribute to the Errors object and
156
- # returns true.
157
-
158
- def valid?
159
- validate
160
- @errors.empty?
161
- end
162
-
163
- # Evaluate the class and see if it is valid.
164
- # Can accept any parameter for 'on' event,
165
- # and defaults to :save
166
-
167
- def validate(on = :save)
168
- @errors = Errors.new
169
-
170
- return if self.class.validations.length == 0
171
-
172
- for event, block in self.class.validations
173
- block.call(self) if event == on.to_sym
174
- end
175
- end
176
-
177
- on_included %{
178
- base.module_eval do
179
- inheritor(:validations, [], :+) unless @validations
180
- # THINK: investigate carefuly!
181
- def self.included(cbase)
182
- super
183
- cbase.send :include, Glue::Validation
184
- end
185
- end
186
- base.extend(ClassMethods)
187
- }
188
-
189
- # Implements the Validation meta-language.
190
-
191
- module ClassMethods
192
- # Validates that the attributes have a values, ie they are
193
- # neither nil or empty.
194
- #
195
- # === Example
196
- #
197
- # validate_value :param, :msg => 'No confirmation'
198
-
199
- def validate_value(*params)
200
- c = parse_config(params, :msg => Glue::Validation::Errors.no_value, :on => :save)
201
-
202
- params.each do |field|
203
- define_validation(:value, field, c[:on]) do |obj|
204
- value = obj.send(field)
205
- obj.errors.add(field, c[:msg]) if value.nil? || (value.respond_to?(:empty?) && value.empty?)
206
- end
207
- end
208
- end
209
-
210
- # Validates the confirmation of +String+ attributes.
211
- #
212
- # === Example
213
- #
214
- # validate_confirmation :password, :msg => 'No confirmation'
215
-
216
- def validate_confirmation(*params)
217
- c = parse_config(params,
218
- :msg => Glue::Validation::Errors.no_confirmation,
219
- :postfix => Glue::Validation.confirmation_postfix,
220
- :on => :save
221
- )
222
-
223
- params.each do |field|
224
- confirm_name = field.to_s + c[:postfix]
225
- attr_accessor confirm_name.to_sym
226
-
227
- define_validation(:confirmation, field, c[:on]) do |obj|
228
- value = obj.send(field)
229
- obj.errors.add(field, c[:msg]) if value.nil? or obj.send(confirm_name) != value
230
- end
231
- end
232
- end
233
-
234
- # Validates the format of +String+ attributes.
235
- # WARNING: regexp options are ignored.
236
- #
237
- # === Example
238
- #
239
- # validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
240
- #
241
- #--
242
- # FIXME: how to get the Regexp options?
243
- #++
244
-
245
- def validate_format(*params)
246
- c = parse_config(params,
247
- :format => nil,
248
- :msg_no_value => Glue::Validation::Errors.no_value,
249
- :msg => Glue::Validation::Errors.invalid_format,
250
- :on => :save
251
- )
252
-
253
- unless c[:format].is_a?(Regexp)
254
- raise ArgumentError, "A regular expression must be supplied as the :format option for validate_format."
255
- end
256
-
257
- params.each do |field|
258
- define_validation(:format, field, c[:on]) do |obj|
259
- value = obj.send(field)
260
- obj.errors.add(field, c[:msg]) if value.nil? or not value.to_s.match(c[:format].source)
261
- end
262
- end
263
- end
264
-
265
- # Validates the length of +String+ attributes.
266
- #
267
- # === Example
268
- #
269
- # validate_length :name, :max => 30, :msg => 'Too long'
270
- # validate_length :name, :min => 2, :msg => 'Too sort'
271
- # validate_length :name, :range => 2..30
272
- # validate_length :name, :length => 15, :msg => 'Name should be %d chars long'
273
-
274
- LENGTHS = [:min, :max, :range, :length].freeze
275
-
276
- def validate_length(*params)
277
- c = parse_config(params,
278
- # :lengths => {:min => nil, :max => nil, :range => nil, :length => nil},
279
- :min => nil, :max => nil, :range => nil, :length => nil,
280
- :msg => nil,
281
- :msg_no_value => Glue::Validation::Errors.no_value,
282
- :msg_short => Glue::Validation::Errors.too_short,
283
- :msg_long => Glue::Validation::Errors.too_long,
284
- :msg_invalid => Glue::Validation::Errors.invalid_length,
285
- :on => :save
286
- )
287
-
288
- length_params = c.reject {|k,v| !LENGTHS.include?(k) || v.nil? }
289
- valid_count = length_params.reject{|k,v| v.nil?}.length
290
-
291
- if valid_count == 0
292
- raise ArgumentError, 'One of :min, :max, :range, or :length must be provided!'
293
- elsif valid_count > 1
294
- raise ArgumentError, 'You can only use one of :min, :max, :range, or :length options!'
295
- end
296
-
297
- operation, required = length_params.keys[0], length_params.values[0]
298
-
299
- params.each do |field|
300
- define_validation(:length, field, c[:on]) do |obj|
301
- msg = c[:msg]
302
- value = obj.send(field)
303
- if value.nil?
304
- obj.errors.add(field, c[:msg_no_value])
305
- else
306
- case operation
307
- when :min
308
- msg ||= c[:msg_short]
309
- obj.errors.add(field, msg % required) if value.length < required
310
- when :max
311
- msg ||= c[:msg_long]
312
- obj.errors.add(field, msg % required) if value.length > required
313
- when :range
314
- min, max = required.first, required.last
315
- if value.length < min
316
- msg ||= c[:msg_short]
317
- obj.errors.add(field, msg % min)
318
- end
319
- if value.length > max
320
- msg ||= c[:msg_long]
321
- obj.errors.add(field, msg % min)
322
- end
323
- when :length
324
- msg ||= c[:msg_invalid]
325
- obj.errors.add(field, msg % required) if value.length != required
326
- end
327
- end
328
- end
329
- end
330
- end
331
-
332
- # Validates that the attributes are included in
333
- # an enumeration.
334
- #
335
- # === Example
336
- #
337
- # validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
338
- # validate_inclusion :age, :in => 5..99
339
-
340
- def validate_inclusion(*params)
341
- c = parse_config(params,
342
- :in => nil,
343
- :msg => Glue::Validation::Errors.no_inclusion,
344
- :allow_nil => false,
345
- :on => :save
346
- )
347
-
348
- unless c[:in].respond_to?('include?')
349
- raise(ArgumentError, 'An object that responds to #include? must be supplied as the :in option')
350
- end
351
-
352
- params.each do |field|
353
- define_validation(:inclusion, field, c[:on]) do |obj|
354
- value = obj.send(field)
355
- unless (c[:allow_nil] && value.nil?) or c[:in].include?(value)
356
- obj.errors.add(field, c[:msg])
357
- end
358
- end
359
- end
360
- end
361
-
362
- # Validates that the attributes have numeric values.
363
- #
364
- # [+:integer+]
365
- # Only accept integers
366
- #
367
- # [+:msg]
368
- # Custom message
369
- #
370
- # [+:on:]
371
- # When to validate
372
- #
373
- # === Example
374
- #
375
- # validate_numeric :age, :msg => 'Must be an integer'
376
-
377
- def validate_numeric(*params)
378
- c = parse_config(params,
379
- :integer => false,
380
- :msg => Glue::Validation::Errors.no_numeric,
381
- :on => :save
382
- )
383
-
384
- params.each do |field|
385
- define_validation(:numeric, field, c[:on]) do |obj|
386
- value = obj.send(field).to_s
387
- if c[:integer]
388
- unless value =~ /^[\+\-]*\d+$/
389
- obj.errors.add(field, c[:msg])
390
- end
391
- else
392
- begin
393
- Float(value)
394
- rescue ArgumentError, TypeError
395
- obj.errors.add(field, c[:msg])
396
- end
397
- end
398
- end
399
- end
400
- end
401
- alias_method :validate_numericality, :validate_numeric
402
-
403
- protected
404
-
405
- # Parse the configuration for a validation by comparing
406
- # the default options with the user-specified options,
407
- # and returning the results.
408
-
409
- def parse_config(params, defaults = {})
410
- defaults.update(params.pop) if params.last.is_a?(Hash)
411
- defaults
412
- end
413
-
414
- # Define a validation for this class on the specified event.
415
- # Specify the on event for when this validation should be
416
- # checked.
417
- #
418
- # An extra check is performed to avoid multiple validations.
419
- #
420
- # This example creates a validation for the 'save' event,
421
- # and will add an error to the record if the 'name' property
422
- # of the validating object is nil.
423
- #
424
- # === Example
425
- #
426
- # field = :name
427
- #
428
- # define_validation(:value, field, :save) do |object|
429
- # object.errors.add(field, "You must set a value for #{field}") if object.send(field).nil?
430
- # end
431
-
432
- def define_validation(val, field, on = :save, &block)
433
- vk = Validation::Key.new(val, field)
434
- unless validations.find { |v| v[2] == vk }
435
- validations! << [on, block, vk]
436
- end
437
- end
438
-
439
- end
440
-
441
- end
442
-
443
- end
444
-
445
- class Module # :nodoc: all
446
- include Glue::Validation::ClassMethods
447
- end