glue 0.41.0 → 1.0.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.
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