nitro 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/AUTHORS +14 -4
  2. data/ChangeLog +192 -1
  3. data/README +50 -6
  4. data/RELEASES +60 -0
  5. data/Rakefile +1 -1
  6. data/bin/cluster.rb +2 -2
  7. data/bin/new_form.rb +1 -1
  8. data/examples/blog/config.rb +5 -4
  9. data/examples/blog/lib/blog.rb +56 -36
  10. data/examples/blog/root/comments.xhtml +5 -2
  11. data/examples/blog/root/entry_form.xhtml +7 -2
  12. data/examples/blog/root/login.xhtml +1 -1
  13. data/examples/blog/root/style.xsl +7 -0
  14. data/examples/og/mock_example.rb +6 -9
  15. data/examples/og/mysql_to_psql.rb +100 -0
  16. data/examples/og/run.rb +8 -17
  17. data/lib/glue.rb +7 -8
  18. data/lib/glue/array.rb +1 -1
  19. data/lib/glue/attribute.rb +86 -0
  20. data/lib/glue/cache.rb +1 -1
  21. data/lib/glue/hash.rb +1 -1
  22. data/lib/glue/inflector.rb +1 -1
  23. data/lib/glue/logger.rb +118 -18
  24. data/lib/glue/mixins.rb +1 -1
  25. data/lib/glue/number.rb +1 -1
  26. data/lib/glue/pool.rb +1 -1
  27. data/lib/glue/property.rb +48 -31
  28. data/lib/glue/string.rb +1 -1
  29. data/lib/glue/time.rb +2 -2
  30. data/lib/glue/validation.rb +400 -0
  31. data/lib/nitro/application.rb +6 -6
  32. data/lib/nitro/builders/form.rb +5 -5
  33. data/lib/nitro/builders/rss.rb +1 -1
  34. data/lib/nitro/builders/xhtml.rb +119 -0
  35. data/lib/nitro/builders/xml.rb +111 -0
  36. data/lib/nitro/config.rb +6 -6
  37. data/lib/nitro/events.rb +1 -1
  38. data/lib/nitro/html.rb +1 -1
  39. data/lib/nitro/markup.rb +15 -20
  40. data/lib/nitro/scaffold.rb +2 -2
  41. data/lib/nitro/server/appserver.rb +3 -3
  42. data/lib/nitro/server/cluster.rb +2 -2
  43. data/lib/nitro/server/dispatcher.rb +2 -2
  44. data/lib/nitro/server/filters/autologin.rb +1 -1
  45. data/lib/nitro/server/fragment.rb +2 -2
  46. data/lib/nitro/server/handlers.rb +2 -2
  47. data/lib/nitro/server/render.rb +17 -15
  48. data/lib/nitro/server/request.rb +6 -6
  49. data/lib/nitro/server/script.rb +2 -2
  50. data/lib/nitro/server/server.rb +2 -2
  51. data/lib/nitro/server/session.rb +6 -6
  52. data/lib/nitro/server/shaders.rb +2 -2
  53. data/lib/nitro/server/webrick.rb +1 -1
  54. data/lib/nitro/sitemap.rb +2 -2
  55. data/lib/nitro/uri.rb +1 -1
  56. data/lib/nitro/version.rb +7 -5
  57. data/lib/og.rb +95 -129
  58. data/lib/og/backend.rb +47 -46
  59. data/lib/og/backends/mysql.rb +64 -63
  60. data/lib/og/backends/psql.rb +73 -72
  61. data/lib/og/connection.rb +7 -8
  62. data/lib/og/enchant.rb +80 -0
  63. data/lib/og/meta.rb +21 -21
  64. data/lib/og/mock.rb +31 -88
  65. data/lib/og/version.rb +6 -5
  66. data/lib/parts/README +9 -0
  67. data/lib/parts/content.rb +23 -9
  68. data/test/glue/tc_attribute.rb +22 -0
  69. data/test/glue/tc_cache.rb +4 -6
  70. data/test/glue/tc_hash.rb +2 -2
  71. data/test/glue/tc_logger.rb +36 -0
  72. data/test/glue/tc_numbers.rb +2 -2
  73. data/test/glue/tc_property_mixins.rb +35 -4
  74. data/test/glue/tc_strings.rb +32 -32
  75. data/test/glue/tc_validation.rb +186 -0
  76. data/test/nitro/builders/tc_xhtml.rb +38 -0
  77. data/test/nitro/builders/tc_xml.rb +47 -0
  78. data/test/nitro/server/tc_request.rb +2 -2
  79. data/test/nitro/server/tc_session.rb +1 -1
  80. data/test/nitro/tc_sitemap.rb +1 -1
  81. data/test/nitro/ui/tc_pager.rb +1 -10
  82. data/test/tc_og.rb +3 -3
  83. data/vendor/blankslate.rb +53 -0
  84. data/vendor/extensions/_base.rb +153 -0
  85. data/vendor/extensions/_template.rb +36 -0
  86. data/vendor/extensions/all.rb +21 -0
  87. data/vendor/extensions/array.rb +68 -0
  88. data/vendor/extensions/binding.rb +224 -0
  89. data/vendor/extensions/class.rb +50 -0
  90. data/vendor/extensions/continuation.rb +71 -0
  91. data/vendor/extensions/enumerable.rb +250 -0
  92. data/vendor/extensions/hash.rb +23 -0
  93. data/vendor/extensions/io.rb +58 -0
  94. data/vendor/extensions/kernel.rb +42 -0
  95. data/vendor/extensions/module.rb +114 -0
  96. data/vendor/extensions/numeric.rb +230 -0
  97. data/vendor/extensions/object.rb +164 -0
  98. data/vendor/extensions/ostruct.rb +41 -0
  99. data/vendor/extensions/string.rb +316 -0
  100. data/vendor/extensions/symbol.rb +28 -0
  101. metadata +35 -13
  102. data/lib/glue/property.rb.old +0 -307
@@ -0,0 +1,23 @@
1
+ #!/usr/local/bin/ruby -w
2
+ #
3
+ # == extensions/hash.rb
4
+ #
5
+ # Adds methods to the builtin Hash class.
6
+ #
7
+
8
+ require "extensions/_base"
9
+
10
+ #
11
+ # * Hash#select!
12
+ #
13
+ ExtensionsProject.implement(Hash, :select!) do
14
+ class Hash
15
+ #
16
+ # In-place version of Hash#select. (Counterpart to, and opposite of, the
17
+ # built-in #reject!)
18
+ #
19
+ def select!
20
+ reject! { |k,v| not yield(k,v) }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,58 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ #
4
+ # == extensions/io.rb
5
+ #
6
+ # Adds methods to the builtin IO class.
7
+ #
8
+
9
+ require "extensions/_base"
10
+
11
+ # This is Ruby's built-in IO class.
12
+ class IO
13
+ end
14
+
15
+ #
16
+ # * IO.write
17
+ #
18
+ ExtensionsProject.implement(IO, :write, :class) do
19
+ class << IO
20
+ #
21
+ # Writes the given data to the given path and closes the file. This is
22
+ # done in binary mode, complementing <tt>IO.read</tt> in standard Ruby.
23
+ #
24
+ # Returns the number of bytes written.
25
+ #
26
+ def write(path, data)
27
+ File.open(path, "wb") do |file|
28
+ return file.write(data)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ #
35
+ # * IO.writelines
36
+ #
37
+ ExtensionsProject.implement(IO, :writelines, :class) do
38
+ class << IO
39
+ #
40
+ # Writes the given array of data to the given path and closes the file.
41
+ # This is done in binary mode, complementing <tt>IO.readlines</tt> in
42
+ # standard Ruby.
43
+ #
44
+ # Note that +readlines+ (the standard Ruby method) returns an array of lines
45
+ # <em>with newlines intact</em>, whereas +writelines+ uses +puts+, and so
46
+ # appends newlines if necessary. In this small way, +readlines+ and
47
+ # +writelines+ are not exact opposites.
48
+ #
49
+ # Returns +nil+.
50
+ #
51
+ def writelines(path, data)
52
+ File.open(path, "wb") do |file|
53
+ file.puts(data)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,42 @@
1
+ #!/usr/local/bin/ruby -w
2
+ #
3
+ # == extensions/module.rb
4
+ #
5
+ # Adds methods to the builtin Kernel module.
6
+ #
7
+
8
+ require "extensions/_base"
9
+
10
+ ExtensionsProject.implement(Kernel, :require_relative) do
11
+ module Kernel
12
+ #
13
+ # <tt>require_relative</tt> complements the builtin method <tt>require</tt> by allowing
14
+ # you to load a file that is <em>relative to the file containing the require_relative
15
+ # statement</em>.
16
+ #
17
+ # When you use <tt>require</tt> to load a file, you are usually accessing functionality
18
+ # that has been properly installed, and made accessible, in your system. <tt>require</tt>
19
+ # does not offer a good solution for loading files within the project's code. This may
20
+ # be useful during a development phase, for accessing test data, or even for accessing
21
+ # files that are "locked" away inside a project, not intended for outside use.
22
+ #
23
+ # For example, if you have unit test classes in the "test" directory, and data for them
24
+ # under the test "test/data" directory, then you might use a line like this in a test
25
+ # case:
26
+ #
27
+ # require_relative "data/customer_data_1"
28
+ #
29
+ # Since neither "test" nor "test/data" are likely to be in Ruby's library path (and for
30
+ # good reason), a normal <tt>require</tt> won't find them. <tt>require_relative</tt> is
31
+ # a good solution for this particular problem.
32
+ #
33
+ # You may include or omit the extension (<tt>.rb</tt> or <tt>.so</tt>) of the file you
34
+ # are loading.
35
+ #
36
+ # _path_ must respond to <tt>to_str</tt>.
37
+ #
38
+ def require_relative(path)
39
+ require File.join(File.dirname(caller[0]), path.to_str)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,114 @@
1
+ #!/usr/local/bin/ruby -w
2
+ #
3
+ # == extensions/module.rb
4
+ #
5
+ # Adds methods to the builtin Module class.
6
+ #
7
+
8
+ require "extensions/_base"
9
+
10
+ ExtensionsProject.implement(Module, :deep_const_get) do
11
+ class Module
12
+ #
13
+ # Recursively dereferences constants separated by "<tt>::</tt>".
14
+ #
15
+ # _const_ is a Symbol or responds to #to_str, for compatibility with the builtin method
16
+ # <tt>Module#const_get</tt>.
17
+ #
18
+ # Object.const_get("String") # -> String
19
+ # Object.const_get(:String) # -> String
20
+ #
21
+ # Object.deep_const_get("String") # -> String
22
+ # Object.deep_const_get(:String) # -> String
23
+ #
24
+ # Object.deep_const_get("Process::Sys") # -> Process::Sys
25
+ # Object.deep_const_get("Regexp::IGNORECASE") # -> 1
26
+ # Object.deep_const_get("Regexp::MULTILINE") # -> 4
27
+ #
28
+ # require 'test/unit'
29
+ # Test.deep_const_get("Unit::Assertions") # -> Test::Unit::Assertions
30
+ # Test.deep_const_get("::Test::Unit") # -> Test::Unit
31
+ #
32
+ # For resolving classes or modules based on their name, see Module.by_name, which uses this
33
+ # method.
34
+ #
35
+ def deep_const_get(const)
36
+ if Symbol === const
37
+ const = const.to_s
38
+ else
39
+ const = const.to_str.dup
40
+ end
41
+ if const.sub!(/^::/, '')
42
+ base = Object
43
+ else
44
+ base = self
45
+ end
46
+ const.split(/::/).inject(base) { |mod, name| mod.const_get(name) }
47
+ end
48
+ end
49
+ end
50
+
51
+
52
+ ExtensionsProject.implement(Module, :by_name, :class) do
53
+ class Module
54
+ #
55
+ # <em>Note: the following documentation uses "class" because it's more common, but it
56
+ # applies to modules as well.</em>
57
+ #
58
+ # Given the _name_ of a class, returns the class itself (i.e. instance of Class). The
59
+ # dereferencing starts at Object. That is,
60
+ #
61
+ # Class.by_name("String")
62
+ #
63
+ # is equivalent to
64
+ #
65
+ # Object.get_const("String")
66
+ #
67
+ # The parameter _name_ is expected to be a Symbol or String, or at least to respond to
68
+ # <tt>to_str</tt>.
69
+ #
70
+ # An ArgumentError is raised if _name_ does not correspond to an existing class. If _name_
71
+ # is not even a valid class name, the error you'll get is not defined.
72
+ #
73
+ # Examples:
74
+ #
75
+ # Class.by_name("String") # -> String
76
+ # Class.by_name("::String") # -> String
77
+ # Class.by_name("Process::Sys") # -> Process::Sys
78
+ # Class.by_name("GorillaZ") # -> (ArgumentError)
79
+ #
80
+ # Class.by_name("Enumerable") # -> Enumerable
81
+ # Module.by_name("Enumerable") # -> Enumerable
82
+ #
83
+ def Module.by_name(name)
84
+ if Symbol === name
85
+ name = name.to_s
86
+ else
87
+ name = name.to_str
88
+ end
89
+ result = Object.deep_const_get(name)
90
+ if result.is_a? Module
91
+ return result
92
+ else
93
+ raise ArgumentError, "#{name} is not a class or module"
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+
100
+ ExtensionsProject.implement(Module, :basename) do
101
+ class Module
102
+ #
103
+ # Returns the immediate name of the class/module, stripped of any containing classes or
104
+ # modules. Compare Ruby's builtin methods <tt>Module#name</tt> and <tt>File.basename</tt>.
105
+ #
106
+ # Process::Sys.name # -> "Process::Sys"
107
+ # Process::Sys.basename # -> "Sys"
108
+ # String.basename # -> "String"
109
+ #
110
+ def basename
111
+ self.name.sub(/^.*::/, '')
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,230 @@
1
+ #!/usr/local/bin/ruby -w
2
+ #
3
+ # == extensions/integer.rb
4
+ #
5
+ # Adds methods to the builtin Numeric and Integer classes.
6
+ #
7
+
8
+ require "extensions/_base"
9
+
10
+ #
11
+ # * Integer#even?
12
+ #
13
+ ExtensionsProject.implement(Integer, :even?) do
14
+ class Integer
15
+ #
16
+ # Returns true if this integer is even, false otherwise.
17
+ # 14.even? # -> true
18
+ # 15.even? # -> false
19
+ #
20
+ def even?
21
+ self % 2 == 0
22
+ end
23
+ end
24
+ end
25
+
26
+
27
+ #
28
+ # * Integer#odd?
29
+ #
30
+ ExtensionsProject.implement(Integer, :odd?) do
31
+ class Integer
32
+ #
33
+ # Returns true if this integer is odd, false otherwise.
34
+ # -99.odd? # -> true
35
+ # -98.odd? # -> false
36
+ #
37
+ def odd?
38
+ self % 2 == 1
39
+ end
40
+ end
41
+ end
42
+
43
+ #
44
+ # This code arose from discussions with Francis Hwang. Leaving it here for future work.
45
+ #
46
+ # class Numeric
47
+ # def precision_format(nplaces, flag = :pad)
48
+ # format = "%.#{nplaces}f"
49
+ # result = sprintf(format, self)
50
+ # case flag
51
+ # when :pad
52
+ # when :nopad
53
+ # result.sub!(/\.?0*$/, '')
54
+ # else
55
+ # raise ArgumentError, "Invalid value for flag: #{flag.inspect}"
56
+ # end
57
+ # result
58
+ # end
59
+ # end
60
+ #
61
+ # 100.precision_format(2) # -> "100.00"
62
+ # 100.precision_format(2, :nopad) # -> "100"
63
+ # 100.1.precision_format(2) # -> "100.10"
64
+ # 100.1.precision_format(2, :nopad) # -> "100.1"
65
+ # 100.1.precision_format(2, false)
66
+ # # -> "ArgumentError: Invalid value for flag: false"
67
+ #
68
+
69
+
70
+ ExtensionsProject.implement(Numeric, :format_s) do
71
+ #--
72
+ # Copyright � 2003 Austin Ziegler
73
+ #
74
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
75
+ # of this software and associated documentation files (the "Software"), to
76
+ # deal in the Software without restriction, including without limitation the
77
+ # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
78
+ # sell copies of the Software, and to permit persons to whom the Software is
79
+ # furnished to do so, subject to the following conditions:
80
+ #
81
+ # The above copyright notice and this permission notice shall be included in
82
+ # all copies or substantial portions of the Software.
83
+ #
84
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
85
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
86
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
87
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
88
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
89
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
90
+ # IN THE SOFTWARE.
91
+ #++
92
+ class Numeric
93
+ #
94
+ # Provides the base formatting styles for #format_s. See #format_s for
95
+ # more details. Two keys provided that are not supported in the
96
+ # #format_s arguments are:
97
+ #
98
+ # <tt>:style</tt>:: Allows a style to inherit from other styles. Styles
99
+ # will be applied in oldest-first order in the event
100
+ # of multiple inheritance layers.
101
+ # <tt>:id</tt>:: This must be provided on any default style created
102
+ # or provided so as to provide a stop marker so that
103
+ # recursive styles do not result in an infinite loop.
104
+ #
105
+ # This is an implementation detail, not important for users of the class.
106
+ #
107
+ FORMAT_STYLES = {
108
+ :us => { :sep => ',', :dec => '.', :id => :us },
109
+ :usd => { :style => :us, :currency => { :id => "$", :pos => :before }, :id => :usd },
110
+ :eu => { :sep => ' ', :dec => ',', :id => :us },
111
+ :euro => { :style => :eu, :currency => { :id => "�", :pos => :before }, :id => :euro },
112
+ :percent => { :style => :us, :currency => { :id => "%%", :pos => :after }, :id => :percent }
113
+ }
114
+
115
+ #
116
+ # Format a number as a string, using US or European conventions, and
117
+ # allowing for the accounting format of representing negative numbers.
118
+ # Optionally, currency formatting options can be provided.
119
+ #
120
+ # For example:
121
+ # x = -10259.8937
122
+ # x.format_s # => "-10,259.8937"
123
+ # x.format_s(:us) # => "-10,259.8937"
124
+ # x.format_s(:usd) # => "$-10,259.8937"
125
+ # x.format_s(:eu) # => "-10 259,8937"
126
+ # x.format_s(:euro) # => "�-10 259,8937"
127
+ # x.format_s(:us, :acct => true) # => "(10,259.8937)"
128
+ # x.format_s(:eu, :acct => true) # => "(10 259,8937)"
129
+ # x.format_s(:usd, :acct => true) # => "$(10,259.8937)"
130
+ # x.format_s(:euro, :acct => true) # => "�(10 259,8937)"
131
+ # x.format_s(:percent) # => "-10,259.8937%"
132
+ #
133
+ # You may configure several aspects of the formatting by providing keyword
134
+ # arguments after the country and accounting arguments. One example of that
135
+ # is the :acct keyword. A more insane example is:
136
+ #
137
+ # x = -10259.8937
138
+ # x.format_s(:us,
139
+ # :sep => ' ', :dec => ',',
140
+ # :neg => '<%s>', :size => 2,
141
+ # :fd => true) # -> "<1 02 59,89 37>"
142
+ #
143
+ # The keyword parameters are as follows:
144
+ #
145
+ # <tt>:acct</tt>:: If +true+, then use accounting style for negative
146
+ # numbers. This overrides any value for
147
+ # <tt>:neg</tt>.
148
+ # <tt>:sep</tt>:: Default "," for US, " " for Euro. Separate the
149
+ # number groups from each other with this string.
150
+ # <tt>:dec</tt>:: Default "." for US, "," for Euro. Separate the
151
+ # number's integer part from the fractional part
152
+ # with this string.
153
+ # <tt>:neg</tt>:: Default <tt>"-%s"</tt>. The format string used to
154
+ # represent negative numbers. If <tt>:acct</tt> is
155
+ # +true+, this is set to <tt>"(%s)"</tt>.
156
+ # <tt>:size</tt>:: The number of digits per group. Defaults to
157
+ # thousands (3).
158
+ # <tt>:fd</tt>:: Indicates whether the decimal portion of the
159
+ # number should be formatted the same way as the
160
+ # integer portion of the number. ("fd" == "format
161
+ # decimal".) Defaults to +false+.
162
+ # <tt>:currency</tt>:: This is an optional hash with two keys,
163
+ # <tt>:id</tt> and <tt>:pos</tt>. <tt>:id</tt> is
164
+ # the string value of the currency (e.g.,
165
+ # <tt>"$"</tt>, <tt>"�"</tt>, <tt>"USD&nbsp;"</tt>);
166
+ # <tt>:pos</tt> is either <tt>:before</tt> or
167
+ # <tt>:after</tt>, referring to the position of the
168
+ # currency indicator. The default <tt>:pos</tt> is
169
+ # <tt>:before</tt>.
170
+ #
171
+ def format_s(style = :us, configs={})
172
+ style = FORMAT_STYLES[style].dup # Adopt US style by default.
173
+
174
+ # Deal with recursive styles.
175
+ if style[:style]
176
+ styles = []
177
+ s = style
178
+ while s[:style]
179
+ s = FORMAT_STYLES[s[:style]].dup
180
+ styles << s
181
+ break if s[:style] = s[:id]
182
+ end
183
+ styles.reverse_each { |s| style.merge!(s) }
184
+ end
185
+ # Merge the configured style.
186
+ style.merge!(configs)
187
+
188
+ sm = style[:sep] || ','
189
+ dp = style[:dec] || '.'
190
+ if style[:acct]
191
+ fmt = '(%s)'
192
+ else
193
+ fmt = style[:neg] || '-%s'
194
+ end
195
+ sz = style[:size] || 3
196
+ format_decimal = style[:fd]
197
+ ng = (self < 0)
198
+ fmt = "%s" if not ng
199
+
200
+ dec, frac = self.abs.to_s.split(/\./)
201
+
202
+ dec.reverse!
203
+ dec.gsub!(/\d{#{sz}}/) { |m| "#{m}#{sm}" }
204
+ dec.gsub!(/#{sm}$/, '')
205
+ dec.reverse!
206
+
207
+ if format_decimal and not frac.nil?
208
+ frac.gsub!(/\d{#{sz}}/) { |m| "#{m}#{sm}" }
209
+ frac.gsub!(/#{sm}$/, '')
210
+ end
211
+
212
+ if frac.nil?
213
+ val = dec
214
+ else
215
+ val = "#{dec}#{dp}#{frac}"
216
+ end
217
+
218
+ if style[:currency]
219
+ if style[:currency][:pos].nil? or style[:currency][:pos] == :before
220
+ fmt = "#{style[:currency][:id]}#{fmt}"
221
+ elsif style[:currency][:pos] == :after
222
+ fmt = "#{fmt}#{style[:currency][:id]}"
223
+ end
224
+ end
225
+
226
+ fmt % val
227
+ end
228
+ end # class Numeric
229
+ end # ExtensionsProject.implement
230
+