nitro 0.12.0 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. data/{ChangeLog → CHANGELOG} +137 -0
  2. data/INSTALL +1 -2
  3. data/README +1 -1
  4. data/Rakefile +10 -61
  5. data/benchmark/{nitro/bench.rb → bench.rb} +1 -1
  6. data/benchmark/{nitro/simple-webrick-n-200.txt → simple-webrick-n-200.txt} +0 -0
  7. data/benchmark/{nitro/static-webrick-n-200.txt → static-webrick-n-200.txt} +0 -0
  8. data/benchmark/{nitro/tiny-lhttpd-n-200-c-5.txt → tiny-lhttpd-n-200-c-5.txt} +0 -0
  9. data/benchmark/{nitro/tiny-webrick-n-200-c-5.txt → tiny-webrick-n-200-c-5.txt} +0 -0
  10. data/benchmark/{nitro/tiny-webrick-n-200.txt → tiny-webrick-n-200.txt} +0 -0
  11. data/benchmark/{nitro/tiny2-webrick-n-200.txt → tiny2-webrick-n-200.txt} +0 -0
  12. data/doc/{ChangeLog.1 → CHANGELOG.1} +0 -0
  13. data/{RELEASES → doc/RELEASES} +46 -0
  14. data/doc/faq.txt +7 -0
  15. data/examples/README.windows +1 -1
  16. data/examples/ajax/controller.rb +21 -0
  17. data/examples/ajax/public/index.xhtml +70 -0
  18. data/examples/ajax/public/js/ajax.js +64 -0
  19. data/examples/ajax/run.rb +16 -0
  20. data/examples/blog/README +6 -3
  21. data/examples/blog/conf/apache.conf +2 -2
  22. data/examples/blog/conf/lhttpd.conf +2 -2
  23. data/examples/blog/log/apache.error_log +777 -0
  24. data/examples/blog/{root → public}/base.xsl +0 -0
  25. data/examples/blog/{root → public}/fcgi.rb +0 -0
  26. data/examples/blog/{root → public}/m/bubbles.gif +0 -0
  27. data/examples/blog/{root → public}/m/comments_curve.gif +0 -0
  28. data/examples/blog/{root → public}/m/down.gif +0 -0
  29. data/examples/blog/{root → public}/m/footer_bg.gif +0 -0
  30. data/examples/blog/{root → public}/m/garrow.gif +0 -0
  31. data/examples/blog/{root → public}/m/gbull.gif +0 -0
  32. data/examples/blog/{root → public}/m/grbull.gif +0 -0
  33. data/examples/blog/{root → public}/m/h1_bg.gif +0 -0
  34. data/examples/blog/{root → public}/m/header_bg.gif +0 -0
  35. data/examples/blog/{root → public}/m/nitro.gif +0 -0
  36. data/examples/blog/{root → public}/m/obull.gif +0 -0
  37. data/examples/blog/{root → public}/m/page_bg.gif +0 -0
  38. data/examples/blog/{root → public}/m/rss.gif +0 -0
  39. data/examples/blog/{root → public}/m/side_title_bg.gif +0 -0
  40. data/examples/blog/{root → public}/m/sidebar_bg.gif +0 -0
  41. data/examples/{no_xsl_blog/root → blog/public}/style.css +6 -0
  42. data/examples/blog/run.rb +10 -12
  43. data/examples/blog/{lib → src}/blog.rb +3 -3
  44. data/examples/blog/{lib/blog → src}/controller.rb +13 -2
  45. data/examples/blog/src/mailer.rb +23 -0
  46. data/examples/blog/{lib/blog/model.rb → src/models/blog.rb} +4 -7
  47. data/examples/blog/src/models/content.rb +52 -0
  48. data/examples/blog/src/views/blog_entry_email.xhtml +16 -0
  49. data/examples/blog/{root → src/views}/comments.xhtml +0 -0
  50. data/examples/blog/{root → src/views}/entry_form.xhtml +0 -0
  51. data/examples/blog/{root → src/views}/error.xhtml +0 -0
  52. data/examples/blog/{root → src/views}/index.xhtml +0 -0
  53. data/examples/blog/{root → src/views}/login.xhtml +0 -0
  54. data/examples/blog/{root → src/views}/recent_posts.xhtml +0 -0
  55. data/examples/blog/{root → src/views}/view_entry.xhtml +8 -0
  56. data/examples/blog/{root → src/views}/view_entry.xml +0 -0
  57. data/examples/blog/src/xsl/base.xsl +153 -0
  58. data/examples/blog/{root → src/xsl}/style.xsl +2 -2
  59. data/examples/no_xsl_blog/README +5 -1
  60. data/examples/no_xsl_blog/conf/apache.conf +2 -2
  61. data/examples/no_xsl_blog/conf/lhttpd.conf +2 -2
  62. data/examples/no_xsl_blog/lib/blog/model.rb +1 -1
  63. data/{lib/parts → examples/no_xsl_blog/lib}/content.rb +1 -11
  64. data/examples/no_xsl_blog/log/apache.error_log +405 -0
  65. data/examples/no_xsl_blog/{root → public}/comments.xhtml +0 -0
  66. data/examples/no_xsl_blog/{root → public}/entry_form.xhtml +0 -0
  67. data/examples/no_xsl_blog/{root → public}/fcgi.rb +0 -0
  68. data/examples/no_xsl_blog/{root → public}/index.xhtml +0 -0
  69. data/examples/no_xsl_blog/{root → public}/login.xhtml +0 -0
  70. data/examples/no_xsl_blog/{root → public}/m/bubbles.gif +0 -0
  71. data/examples/no_xsl_blog/{root → public}/m/comments_curve.gif +0 -0
  72. data/examples/no_xsl_blog/{root → public}/m/down.gif +0 -0
  73. data/examples/no_xsl_blog/{root → public}/m/footer_bg.gif +0 -0
  74. data/examples/no_xsl_blog/{root → public}/m/garrow.gif +0 -0
  75. data/examples/no_xsl_blog/{root → public}/m/gbull.gif +0 -0
  76. data/examples/no_xsl_blog/{root → public}/m/grbull.gif +0 -0
  77. data/examples/no_xsl_blog/{root → public}/m/h1_bg.gif +0 -0
  78. data/examples/no_xsl_blog/{root → public}/m/header_bg.gif +0 -0
  79. data/examples/no_xsl_blog/{root → public}/m/nitro.gif +0 -0
  80. data/examples/no_xsl_blog/{root → public}/m/obull.gif +0 -0
  81. data/examples/no_xsl_blog/{root → public}/m/page_bg.gif +0 -0
  82. data/examples/no_xsl_blog/{root → public}/m/rss.gif +0 -0
  83. data/examples/no_xsl_blog/{root → public}/m/side_title_bg.gif +0 -0
  84. data/examples/no_xsl_blog/{root → public}/m/sidebar_bg.gif +0 -0
  85. data/examples/no_xsl_blog/{root → public}/recent_posts.xhtml +0 -0
  86. data/examples/{blog/root → no_xsl_blog/public}/style.css +0 -0
  87. data/examples/no_xsl_blog/{root → public}/view_entry.xhtml +0 -0
  88. data/examples/no_xsl_blog/{root → public}/view_entry.xml +0 -0
  89. data/examples/tiny/conf/apache.conf +2 -2
  90. data/examples/tiny/log/apache.error_log +100 -0
  91. data/examples/tiny/{root → public}/fcgi.rb +0 -0
  92. data/examples/tiny/{root → public}/include.xhtml +0 -0
  93. data/examples/tiny/{root → public}/index.xhtml +0 -0
  94. data/{bin/proto/root/m → examples/tiny/public}/nitro.png +0 -0
  95. data/examples/tiny/{root → public}/upload.xhtml +0 -0
  96. data/examples/tiny/run.rb +1 -2
  97. data/examples/why_wiki/wiki.yml +1 -0
  98. data/install.rb +5 -2
  99. data/lib/nitro.rb +2 -6
  100. data/lib/nitro/adapters/fastcgi.rb +2 -2
  101. data/lib/nitro/adapters/webrick.rb +4 -4
  102. data/lib/nitro/conf.rb +5 -2
  103. data/lib/nitro/controller.rb +2 -2
  104. data/lib/nitro/dispatcher.rb +19 -8
  105. data/lib/nitro/mail.rb +252 -8
  106. data/lib/nitro/render.rb +24 -21
  107. data/lib/nitro/runner.rb +1 -1
  108. data/lib/nitro/scaffold.rb +2 -5
  109. data/lib/nitro/simple.rb +2 -1
  110. data/lib/nitro/template.rb +42 -2
  111. data/test/nitro/tc_controller.rb +9 -4
  112. data/test/nitro/tc_dispatcher.rb +4 -6
  113. data/test/nitro/tc_mail.rb +95 -0
  114. data/test/{root → public}/blog/list.xhtml +0 -0
  115. data/test/public/dummy_mailer/registration.xhtml +5 -0
  116. data/vendor/README +0 -1
  117. metadata +136 -181
  118. data/benchmark/og/bench.rb +0 -75
  119. data/benchmark/og/sqlite-no-prepare.1.txt +0 -13
  120. data/benchmark/og/sqlite-no-prepare.2.txt +0 -13
  121. data/benchmark/og/sqlite-prepare.1.txt +0 -13
  122. data/benchmark/og/sqlite-prepare.2.txt +0 -13
  123. data/bin/proto/README +0 -34
  124. data/bin/proto/conf/apache.conf +0 -1
  125. data/bin/proto/conf/app.conf.rb +0 -14
  126. data/bin/proto/conf/lhttpd.conf +0 -236
  127. data/bin/proto/ctl +0 -4
  128. data/bin/proto/lib/README +0 -5
  129. data/bin/proto/log/README +0 -3
  130. data/bin/proto/root/fcgi.rb +0 -6
  131. data/bin/proto/root/index.xhtml +0 -69
  132. data/bin/proto/root/style.css +0 -152
  133. data/bin/proto/root/style.xsl +0 -99
  134. data/doc/og_config.txt +0 -35
  135. data/doc/og_tutorial.txt +0 -595
  136. data/examples/og/README +0 -11
  137. data/examples/og/mock_example.rb +0 -50
  138. data/examples/og/mysql_to_psql.rb +0 -96
  139. data/examples/og/run.rb +0 -286
  140. data/examples/tiny/root/nitro.png +0 -0
  141. data/lib/glue.rb +0 -55
  142. data/lib/glue/array.rb +0 -61
  143. data/lib/glue/attribute.rb +0 -83
  144. data/lib/glue/cache.rb +0 -138
  145. data/lib/glue/flexob.rb +0 -12
  146. data/lib/glue/hash.rb +0 -122
  147. data/lib/glue/inflector.rb +0 -91
  148. data/lib/glue/logger.rb +0 -147
  149. data/lib/glue/misc.rb +0 -14
  150. data/lib/glue/mixins.rb +0 -36
  151. data/lib/glue/number.rb +0 -24
  152. data/lib/glue/object.rb +0 -32
  153. data/lib/glue/pool.rb +0 -60
  154. data/lib/glue/property.rb +0 -408
  155. data/lib/glue/string.rb +0 -162
  156. data/lib/glue/time.rb +0 -85
  157. data/lib/glue/validation.rb +0 -394
  158. data/lib/og.rb +0 -185
  159. data/lib/og/adapter.rb +0 -513
  160. data/lib/og/adapters/filesys.rb +0 -121
  161. data/lib/og/adapters/mysql.rb +0 -347
  162. data/lib/og/adapters/oracle.rb +0 -375
  163. data/lib/og/adapters/psql.rb +0 -273
  164. data/lib/og/adapters/sqlite.rb +0 -262
  165. data/lib/og/backend.rb +0 -297
  166. data/lib/og/connection.rb +0 -304
  167. data/lib/og/database.rb +0 -282
  168. data/lib/og/enchant.rb +0 -125
  169. data/lib/og/meta.rb +0 -373
  170. data/lib/og/mock.rb +0 -165
  171. data/lib/og/observer.rb +0 -53
  172. data/lib/og/typemacros.rb +0 -23
  173. data/lib/parts/README +0 -9
  174. data/test/glue/tc_attribute.rb +0 -22
  175. data/test/glue/tc_cache.rb +0 -45
  176. data/test/glue/tc_hash.rb +0 -38
  177. data/test/glue/tc_logger.rb +0 -39
  178. data/test/glue/tc_numbers.rb +0 -20
  179. data/test/glue/tc_property.rb +0 -89
  180. data/test/glue/tc_property_mixins.rb +0 -93
  181. data/test/glue/tc_property_type_checking.rb +0 -35
  182. data/test/glue/tc_strings.rb +0 -103
  183. data/test/glue/tc_validation.rb +0 -188
  184. data/test/og/tc_filesys.rb +0 -83
  185. data/test/og/tc_lifecycle.rb +0 -104
  186. data/test/og/tc_many_to_many.rb +0 -62
  187. data/test/og/tc_meta.rb +0 -55
  188. data/test/og/tc_observer.rb +0 -85
  189. data/test/og/tc_sqlite.rb +0 -87
  190. data/test/tc_og.rb +0 -355
  191. data/vendor/composite_sexp_processor.rb +0 -43
  192. data/vendor/parse_tree.rb +0 -745
  193. data/vendor/sexp_processor.rb +0 -453
data/lib/glue/string.rb DELETED
@@ -1,162 +0,0 @@
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 259 2005-02-15 08:54:54Z 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 DELETED
@@ -1,85 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: time.rb 259 2005-02-15 08:54:54Z 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
@@ -1,394 +0,0 @@
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
- # === Example
12
- #
13
- # class User
14
- # prop_accessor :name, String
15
- # prop_accessor :level, Fixnum
16
- #
17
- # validate_length :name, :range => 2..6
18
- # validate_unique :name, :msg => :name_allready_exists
19
- # validate_format :name, :format => /[a-z]*/, :msg => 'invalid format', :on => :create
20
- # end
21
- #
22
- # class N::CustomUserValidator
23
- # include N::Validation
24
- # validate_length :name, :range => 2..6, :msg_short => :name_too_short, :msg_long => :name_too_long
25
- # end
26
- #
27
- # user = @request.fill(User.new)
28
- # user.level = 15
29
- #
30
- # unless user.valid?
31
- # user.save
32
- # else
33
- # p user.errors[:name]
34
- # end
35
- #
36
- # unless user.save
37
- # p user.errors.on(:name)
38
- # end
39
- #
40
- # unless errors = N::CustomUserValidator.errors(user)
41
- # user.save
42
- # else
43
- # p errors[:name]
44
- # end
45
- #
46
- #--
47
- # TODO: all validation methods should imply validate_value
48
- # TODO: add validate_unique
49
- #++
50
-
51
- module Validation
52
-
53
- # Encapsulates a list of validation errors.
54
-
55
- class Errors
56
- attr_accessor :errors
57
-
58
- cattr_accessor :no_value, 'No value provided'
59
- cattr_accessor :no_confirmation, 'Invalid confirmation'
60
- cattr_accessor :invalid_format, 'Invalid format'
61
- cattr_accessor :too_short, 'Too short, must be more than %d characters long'
62
- cattr_accessor :too_long, 'Too long, must be less than %d characters long'
63
- cattr_accessor :invalid_length, 'Must be %d characters long'
64
- cattr_accessor :no_inclusion, 'The value is invalid'
65
-
66
- def initialize
67
- @errors = {}
68
- end
69
-
70
- def add(attr, message)
71
- (@errors[attr] ||= []) << message
72
- end
73
-
74
- def on(attr)
75
- @errors[attr]
76
- end
77
- alias_method :[], :on
78
-
79
- # Yields each attribute and associated message.
80
-
81
- def each
82
- @errors.each_key do |attr|
83
- @errors[attr].each { |msg| yield attr, msg }
84
- end
85
- end
86
-
87
- def size
88
- @errors.size
89
- end
90
- alias_method :count, :size
91
-
92
- def empty?
93
- @errors.empty?
94
- end
95
-
96
- def clear
97
- @errors.clear
98
- end
99
- end
100
-
101
- # If the validate method returns true, this
102
- # attributes holds the errors found.
103
-
104
- attr_accessor :errors
105
-
106
- # Call the #validate method for this object.
107
- # If validation errors are found, sets the
108
- # @errors attribute to the Errors object and
109
- # returns true.
110
-
111
- def valid?
112
- begin
113
- @errors = self.class.validate(self)
114
- return @errors.empty?
115
- rescue NoMethodError => e
116
- # gmosx: hmm this is potentially dangerous.
117
- N::Validation.eval_validate(self.class)
118
- retry
119
- end
120
- end
121
-
122
- # Evaluate the 'validate' method for the calling
123
- # class.
124
- #
125
- # WARNING: for the moment only evaluates for
126
- # on == :save
127
-
128
- def self.eval_validate(klass)
129
- code = %{
130
- def self.validate(obj, on = :save)
131
- errors = Errors.new
132
- }
133
-
134
- for val, on in klass.__meta[:validations]
135
- code << %{
136
- #{val}
137
- }
138
- end
139
-
140
- code << %{
141
- return errors
142
- end
143
- }
144
-
145
- # puts '-->', code, '<--'
146
-
147
- klass.module_eval(code)
148
- end
149
-
150
- def self.append_features(base)
151
- super
152
-
153
- base.module_eval <<-"end_eval", __FILE__, __LINE__
154
- meta :validations, []
155
- end_eval
156
-
157
- base.extend(MetaLanguage)
158
- end
159
-
160
- # Implements the Validation meta-language.
161
-
162
- module MetaLanguage
163
-
164
- # the postfix attached to confirmation attributes.
165
-
166
- mattr_accessor :confirmation_postfix, '_confirmation'
167
-
168
- # Validates that the attributes have a values, ie they are
169
- # neither nil or empty.
170
- #
171
- # === Example
172
- #
173
- # validate_value :param, :msg => 'No confirmation'
174
-
175
- def validate_value(*params)
176
- c = {
177
- :msg => N::Validation::Errors.no_value,
178
- :on => :save
179
- }
180
- c.update(params.pop) if params.last.is_a?(Hash)
181
-
182
- idx = 0
183
- for name in params
184
- code = %{
185
- if obj.#{name}.nil?
186
- errors.add(:#{name}, '#{c[:msg]}')
187
- elsif obj.#{name}.respond_to?(:empty?)
188
- errors.add(:#{name}, '#{c[:msg]}') if obj.#{name}.empty?
189
- end
190
- }
191
-
192
- __meta[:validations] << [code, c[:on]]
193
- end
194
- end
195
-
196
- # Validates the confirmation of +String+ attributes.
197
- #
198
- # === Example
199
- #
200
- # validate_confirmation :password, :msg => 'No confirmation'
201
-
202
- def validate_confirmation(*params)
203
- c = {
204
- :msg => N::Validation::Errors.no_confirmation,
205
- :postfix => N::Validation::MetaLanguage.confirmation_postfix,
206
- :on => :save
207
- }
208
- c.update(params.pop) if params.last.is_a?(Hash)
209
-
210
-
211
- for name in params
212
- confirm_name = "#{name}#{c[:postfix]}"
213
- eval "attr_accessor :#{confirm_name}"
214
-
215
- code = %{
216
- if obj.#{confirm_name}.nil? or (obj.#{confirm_name} != obj.#{name})
217
- errors.add(:#{name}, '#{c[:msg]}')
218
- end
219
- }
220
-
221
- __meta[:validations] << [code, c[:on]]
222
- end
223
- end
224
-
225
- # Validates the format of +String+ attributes.
226
- #
227
- # === Example
228
- #
229
- # validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
230
-
231
- def validate_format(*params)
232
- c = {
233
- :format => nil,
234
- :msg_no_value => N::Validation::Errors.no_value,
235
- :msg => N::Validation::Errors.invalid_format,
236
- :on => :save
237
- }
238
- c.update(params.pop) if params.last.is_a?(Hash)
239
-
240
- unless c[:format].is_a?(Regexp)
241
- raise(ArgumentError,
242
- 'A regular expression must be supplied as the :format option')
243
- end
244
-
245
- for name in params
246
- code = %{
247
- if obj.#{name}.nil?
248
- errors.add(:#{name}, '#{c[:msg_no_value]}')
249
- else
250
- unless obj.#{name}.to_s.match(/#{Regexp.quote(c[:format].source)}/)
251
- errors.add(:#{name}, '#{c[:msg]}')
252
- end
253
- end;
254
- }
255
-
256
- __meta[:validations] << [code, c[:on]]
257
- end
258
- end
259
-
260
- # Validates the length of +String+ attributes.
261
- #
262
- # === Example
263
- #
264
- # validate_length :name, :max => 30, :msg => 'Too long'
265
- # validate_length :name, :min => 2, :msg => 'Too sort'
266
- # validate_length :name, :range => 2..30
267
- # validate_length :name, :length => 15, :msg => 'Name should be %d chars long'
268
-
269
- def validate_length(*params)
270
- c = {
271
- :min => nil, :max => nil, :range => nil, :length => nil,
272
- :msg => nil,
273
- :msg_no_value => N::Validation::Errors.no_value,
274
- :msg_short => N::Validation::Errors.too_short,
275
- :msg_long => N::Validation::Errors.too_long,
276
- :on => :save
277
- }
278
- c.update(params.pop) if params.last.is_a?(Hash)
279
-
280
- min, max = c[:min], c[:max]
281
- range, length = c[:range], c[:length]
282
-
283
- count = 0
284
- [min, max, range, length].each { |o| count += 1 if o }
285
-
286
- if count == 0
287
- raise(ArgumentError,
288
- 'One of :min, :max, :range, :length must be provided!')
289
- end
290
-
291
- if count > 1
292
- raise(ArgumentError,
293
- 'The :min, :max, :range, :length options are mutually exclusive!')
294
- end
295
-
296
- for name in params
297
- if min
298
- c[:msg] ||= N::Validation::Errors.too_short
299
- code = %{
300
- if obj.#{name}.nil?
301
- errors.add(:#{name}, '#{c[:msg_no_value]}')
302
- elsif obj.#{name}.to_s.length < #{min}
303
- msg = '#{c[:msg]}'
304
- msg = (msg % #{min}) rescue msg
305
- errors.add(:#{name}, msg)
306
- end;
307
- }
308
- elsif max
309
- c[:msg] ||= N::Validation::Errors.too_long
310
- code = %{
311
- if obj.#{name}.nil?
312
- errors.add(:#{name}, '#{c[:msg_no_value]}')
313
- elsif obj.#{name}.to_s.length > #{max}
314
- msg = '#{c[:msg]}'
315
- msg = (msg % #{max}) rescue msg
316
- errors.add(:#{name}, msg)
317
- end;
318
- }
319
- elsif range
320
- code = %{
321
- if obj.#{name}.nil?
322
- errors.add(:#{name}, '#{c[:msg_no_value]}')
323
- elsif obj.#{name}.to_s.length < #{range.first}
324
- msg = '#{c[:msg_short]}'
325
- msg = (msg % #{range.first}) rescue msg
326
- errors.add(:#{name}, msg)
327
- elsif obj.#{name}.to_s.length > #{range.last}
328
- msg = '#{c[:msg_long]}'
329
- msg = (msg % #{range.last}) rescue msg
330
- errors.add(:#{name}, msg)
331
- end;
332
- }
333
- elsif length
334
- c[:msg] ||= N::Validation::Errors.invalid_length
335
- code = %{
336
- if obj.#{name}.nil?
337
- errors.add(:#{name}, '#{c[:msg_no_value]}')
338
- elsif obj.#{name}.to_s.length != #{length}
339
- msg = '#{c[:msg]}'
340
- msg = (msg % #{length}) rescue msg
341
- errors.add(:#{name}, msg)
342
- end;
343
- }
344
- end
345
-
346
- __meta[:validations] << [code, c[:on]]
347
- end
348
- end
349
-
350
- # Validates that the attributes are included in
351
- # an enumeration.
352
- #
353
- # === Example
354
- #
355
- # validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
356
- # validate_inclusion :age, :in => 5..99
357
-
358
- def validate_inclusion(*params)
359
- c = {
360
- :in => nil,
361
- :msg => N::Validation::Errors.no_inclusion,
362
- :allow_nil => false,
363
- :on => :save
364
- }
365
- c.update(params.pop) if params.last.is_a?(Hash)
366
-
367
- unless c[:in].respond_to?('include?')
368
- raise(ArgumentError,
369
- 'An object that responds to #include? must be supplied as the :in option')
370
- end
371
-
372
- for name in params
373
- if c[:allow_nil]
374
- code = %{
375
- unless obj.#{name}.nil? or (#{c[:in].inspect}).include?(obj.#{name})
376
- errors.add(:#{name}, '#{c[:msg]}')
377
- end;
378
- }
379
- else
380
- code = %{
381
- unless (#{c[:in].inspect}).include?(obj.#{name})
382
- errors.add(:#{name}, '#{c[:msg]}')
383
- end;
384
- }
385
- end
386
-
387
- __meta[:validations] << [code, c[:on]]
388
- end
389
- end
390
-
391
- end
392
- end
393
-
394
- end