nitro 0.20.0 → 0.21.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 (115) hide show
  1. data/CHANGELOG +752 -543
  2. data/INSTALL +38 -38
  3. data/README +264 -225
  4. data/Rakefile +48 -49
  5. data/bin/nitro +3 -3
  6. data/bin/nitrogen +6 -6
  7. data/doc/AUTHORS +10 -10
  8. data/doc/CHANGELOG.1 +1939 -1939
  9. data/doc/CHANGELOG.2 +954 -954
  10. data/doc/LICENSE +3 -3
  11. data/doc/MIGRATION +28 -0
  12. data/doc/RELEASES +814 -643
  13. data/doc/config.txt +5 -5
  14. data/install.rb +7 -17
  15. data/lib/nitro.rb +38 -9
  16. data/lib/nitro/adapter/cgi.rb +311 -312
  17. data/lib/nitro/adapter/fastcgi.rb +18 -25
  18. data/lib/nitro/adapter/webrick.rb +128 -137
  19. data/lib/nitro/adapter/wee.rb +51 -0
  20. data/lib/nitro/caching.rb +20 -20
  21. data/lib/nitro/caching/actions.rb +43 -43
  22. data/lib/nitro/caching/fragments.rb +46 -46
  23. data/lib/nitro/caching/invalidation.rb +11 -11
  24. data/lib/nitro/caching/output.rb +65 -65
  25. data/lib/nitro/caching/stores.rb +67 -67
  26. data/lib/nitro/compiler.rb +262 -0
  27. data/lib/nitro/compiler/elements.rb +0 -0
  28. data/lib/nitro/compiler/errors.rb +65 -0
  29. data/lib/nitro/compiler/localization.rb +25 -0
  30. data/lib/nitro/compiler/markup.rb +19 -0
  31. data/lib/nitro/compiler/shaders.rb +206 -0
  32. data/lib/nitro/compiler/squeeze.rb +20 -0
  33. data/lib/nitro/compiler/xslt.rb +61 -0
  34. data/lib/nitro/context.rb +87 -88
  35. data/lib/nitro/controller.rb +151 -158
  36. data/lib/nitro/cookie.rb +34 -34
  37. data/lib/nitro/dispatcher.rb +195 -186
  38. data/lib/nitro/element.rb +132 -126
  39. data/lib/nitro/element/java_script.rb +6 -6
  40. data/lib/nitro/flash.rb +66 -66
  41. data/lib/nitro/mail.rb +192 -192
  42. data/lib/nitro/mixin/buffer.rb +66 -0
  43. data/lib/nitro/mixin/debug.rb +16 -16
  44. data/lib/nitro/mixin/form.rb +88 -0
  45. data/lib/nitro/mixin/helper.rb +2 -2
  46. data/lib/nitro/mixin/javascript.rb +108 -108
  47. data/lib/nitro/mixin/markup.rb +144 -0
  48. data/lib/nitro/mixin/pager.rb +202 -202
  49. data/lib/nitro/mixin/rss.rb +67 -0
  50. data/lib/nitro/mixin/table.rb +63 -0
  51. data/lib/nitro/mixin/xhtml.rb +75 -0
  52. data/lib/nitro/mixin/xml.rb +124 -0
  53. data/lib/nitro/render.rb +183 -359
  54. data/lib/nitro/request.rb +140 -140
  55. data/lib/nitro/response.rb +27 -27
  56. data/lib/nitro/routing.rb +21 -21
  57. data/lib/nitro/scaffold.rb +124 -118
  58. data/lib/nitro/server.rb +117 -80
  59. data/lib/nitro/server/runner.rb +341 -0
  60. data/lib/nitro/service.rb +12 -12
  61. data/lib/nitro/service/xmlrpc.rb +22 -22
  62. data/lib/nitro/session.rb +122 -120
  63. data/lib/nitro/session/drb.rb +9 -9
  64. data/lib/nitro/session/drbserver.rb +34 -34
  65. data/lib/nitro/template.rb +171 -155
  66. data/lib/nitro/testing/assertions.rb +90 -90
  67. data/lib/nitro/testing/context.rb +16 -16
  68. data/lib/nitro/testing/testcase.rb +34 -34
  69. data/proto/conf/lhttpd.conf +9 -9
  70. data/proto/public/error.xhtml +75 -75
  71. data/proto/public/index.xhtml +18 -18
  72. data/proto/public/js/behaviour.js +65 -65
  73. data/proto/public/js/controls.js +1 -1
  74. data/proto/public/js/prototype.js +3 -3
  75. data/proto/public/settings.xhtml +61 -61
  76. data/proto/run.rb +1 -5
  77. data/test/nitro/adapter/raw_post1.bin +0 -0
  78. data/test/nitro/adapter/tc_cgi.rb +57 -57
  79. data/test/nitro/adapter/tc_webrick.rb +4 -4
  80. data/test/nitro/mixin/tc_pager.rb +25 -25
  81. data/test/nitro/mixin/tc_rss.rb +24 -0
  82. data/test/nitro/mixin/tc_table.rb +31 -0
  83. data/test/nitro/mixin/tc_xhtml.rb +13 -0
  84. data/test/nitro/tc_caching.rb +10 -10
  85. data/test/nitro/tc_context.rb +8 -8
  86. data/test/nitro/tc_controller.rb +48 -48
  87. data/test/nitro/tc_cookie.rb +6 -6
  88. data/test/nitro/tc_dispatcher.rb +64 -64
  89. data/test/nitro/tc_element.rb +27 -27
  90. data/test/nitro/tc_flash.rb +31 -31
  91. data/test/nitro/tc_mail.rb +63 -63
  92. data/test/nitro/tc_server.rb +26 -26
  93. data/test/nitro/tc_session.rb +9 -9
  94. data/test/nitro/tc_template.rb +19 -19
  95. data/test/public/blog/list.xhtml +1 -1
  96. metadata +31 -37
  97. data/lib/nitro/buffering.rb +0 -45
  98. data/lib/nitro/builder/form.rb +0 -104
  99. data/lib/nitro/builder/rss.rb +0 -104
  100. data/lib/nitro/builder/table.rb +0 -80
  101. data/lib/nitro/builder/xhtml.rb +0 -132
  102. data/lib/nitro/builder/xml.rb +0 -131
  103. data/lib/nitro/conf.rb +0 -36
  104. data/lib/nitro/environment.rb +0 -21
  105. data/lib/nitro/errors.rb +0 -69
  106. data/lib/nitro/localization.rb +0 -153
  107. data/lib/nitro/markup.rb +0 -147
  108. data/lib/nitro/output.rb +0 -24
  109. data/lib/nitro/runner.rb +0 -348
  110. data/lib/nitro/shaders.rb +0 -206
  111. data/test/nitro/builder/tc_rss.rb +0 -23
  112. data/test/nitro/builder/tc_table.rb +0 -30
  113. data/test/nitro/builder/tc_xhtml.rb +0 -39
  114. data/test/nitro/builder/tc_xml.rb +0 -56
  115. data/test/nitro/tc_localization.rb +0 -49
data/lib/nitro/request.rb CHANGED
@@ -1,163 +1,163 @@
1
1
  module Nitro
2
-
2
+
3
3
  # Encapsulates a request. This is an abstract request
4
4
  # typically extended by sub-classes. This module
5
5
  # is included in Context
6
6
 
7
7
  module Request
8
8
 
9
- # The request input stream.
9
+ # The request input stream.
10
10
 
11
- attr_accessor :in
11
+ attr_accessor :in
12
12
 
13
- # The request headers collection. Also called
14
- # the request environment (env).
13
+ # The request headers collection. Also called
14
+ # the request environment (env).
15
15
 
16
- attr_accessor :headers
17
- alias_method :env, :headers
18
- alias_method :env=, :headers=
19
- # for compatibility with cgi.rb
20
- alias_method :env_table, :headers
16
+ attr_accessor :headers
17
+ alias_method :env, :headers
18
+ alias_method :env=, :headers=
19
+ # for compatibility with cgi.rb
20
+ alias_method :env_table, :headers
21
21
 
22
- # The parsed query parameters collection.
22
+ # The parsed query parameters collection.
23
23
 
24
- attr_accessor :params
25
- alias_method :query, :params
24
+ attr_accessor :params
25
+ alias_method :query, :params
26
26
 
27
- # The request cookies.
27
+ # The request cookies.
28
28
 
29
- attr_accessor :cookies
29
+ attr_accessor :cookies
30
30
 
31
- # The request protocol.
31
+ # The request protocol.
32
32
 
33
- def protocol
34
- 443 == port ? 'https://' : 'http://'
35
- end
33
+ def protocol
34
+ 443 == port ? 'https://' : 'http://'
35
+ end
36
36
 
37
- # Is this an ssl request?
37
+ # Is this an ssl request?
38
38
 
39
- def ssl?
40
- 443 == port
41
- end
42
-
43
- # The request uri.
44
-
45
- def uri
46
- @headers['REQUEST_URI']
47
- end
48
-
49
- # The path is the uri without the query string.
39
+ def ssl?
40
+ 443 == port
41
+ end
42
+
43
+ # The request uri.
44
+
45
+ def uri
46
+ @headers['REQUEST_URI']
47
+ end
48
+
49
+ # The path is the uri without the query string.
50
50
 
51
- def path
52
- path = uri ? uri.split('?').first : ''
53
- end
51
+ def path
52
+ path = uri ? uri.split('?').first : ''
53
+ end
54
54
 
55
- def path_info
56
- @headers['PATH_INFO']
57
- end
58
-
59
- # The request query string.
60
-
61
- def query_string
62
- @headers['QUERY_STRING']
63
- end
64
-
65
- # The request method.
66
-
67
- def method
68
- @headers['REQUEST_METHOD'].downcase.intern
69
- end
70
-
71
- # Is this a post method?
72
-
73
- def post?
74
- method == :post
75
- end
76
-
77
- # Return the referer. For the initial page in the
78
- # clickstream there is no referer, set "/" by default.
79
-
80
- def referer
81
- return @headers['HTTP_REFERER'] || '/'
82
- end
83
-
84
- # The content_length
85
-
86
- def content_length
87
- return @headers['CONTENT_LENGTH'].to_i
88
- end
89
-
90
- # The remote IP address. REMOTE_ADDR is the standard
91
- # but will fail if the user is behind a proxy.
92
- # HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR are set by
93
- # proxies so check for these before falling back to
94
- # REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-delimited
95
- # list in the case of multiple chained proxies; the first
96
- # is the originating IP.
97
-
98
- def remote_ip
99
- return @headers['HTTP_CLIENT_IP'] if @headers.include?('HTTP_CLIENT_IP')
100
-
101
- if @headers.include?('HTTP_X_FORWARDED_FOR') then
102
- remote_ips = @headers['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
103
- ip =~ /^unknown$|^(10|172\.16|192\.168)\./i
104
- end
105
-
106
- return remote_ips.first.strip unless remote_ips.empty?
107
- end
108
-
109
- return @headers['REMOTE_ADDR']
110
- end
111
-
112
- # The server port.
113
-
114
- def port
115
- @headers['SERVER_PORT'].to_i
116
- end
117
-
118
- # The server host name.
119
- # Also handles proxy forwarding.
120
-
121
- def host
122
- @headers['HTTP_X_FORWARDED_HOST'] || @headers['HTTP_HOST']
123
- end
124
-
125
- def host_url
126
- "http://#{host}"
127
- end
128
-
129
- # The raw data of the request.
130
- # Useful to implement Webservices.
131
- #--
132
- # FIXME: better name and implementation.
133
- #++
134
-
135
- def raw_body
136
- unless @raw_body
137
- @in.rewind
138
- @raw_body = @in.read(content_length)
139
- end
140
-
141
- @raw_body
142
- end
143
-
144
- # Lookup a query parameter.
145
-
146
- def [](param)
147
- @params[param]
148
- end
149
-
150
- # Set a query parameter.
151
-
152
- def []=(param, value)
153
- @params[param] = value
154
- end
155
-
156
- # Fetch a parameter with default value.
157
-
158
- def fetch(param, default = nil)
159
- @params.fetch(param, default)
160
- end
55
+ def path_info
56
+ @headers['PATH_INFO']
57
+ end
58
+
59
+ # The request query string.
60
+
61
+ def query_string
62
+ @headers['QUERY_STRING']
63
+ end
64
+
65
+ # The request method.
66
+
67
+ def method
68
+ @headers['REQUEST_METHOD'].downcase.intern
69
+ end
70
+
71
+ # Is this a post method?
72
+
73
+ def post?
74
+ method == :post
75
+ end
76
+
77
+ # Return the referer. For the initial page in the
78
+ # clickstream there is no referer, set "/" by default.
79
+
80
+ def referer
81
+ return @headers['HTTP_REFERER'] || '/'
82
+ end
83
+
84
+ # The content_length
85
+
86
+ def content_length
87
+ return @headers['CONTENT_LENGTH'].to_i
88
+ end
89
+
90
+ # The remote IP address. REMOTE_ADDR is the standard
91
+ # but will fail if the user is behind a proxy.
92
+ # HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR are set by
93
+ # proxies so check for these before falling back to
94
+ # REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-delimited
95
+ # list in the case of multiple chained proxies; the first
96
+ # is the originating IP.
97
+
98
+ def remote_ip
99
+ return @headers['HTTP_CLIENT_IP'] if @headers.include?('HTTP_CLIENT_IP')
100
+
101
+ if @headers.include?('HTTP_X_FORWARDED_FOR') then
102
+ remote_ips = @headers['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
103
+ ip =~ /^unknown$|^(10|172\.16|192\.168)\./i
104
+ end
105
+
106
+ return remote_ips.first.strip unless remote_ips.empty?
107
+ end
108
+
109
+ return @headers['REMOTE_ADDR']
110
+ end
111
+
112
+ # The server port.
113
+
114
+ def port
115
+ @headers['SERVER_PORT'].to_i
116
+ end
117
+
118
+ # The server host name.
119
+ # Also handles proxy forwarding.
120
+
121
+ def host
122
+ @headers['HTTP_X_FORWARDED_HOST'] || @headers['HTTP_HOST']
123
+ end
124
+
125
+ def host_url
126
+ "http://#{host}"
127
+ end
128
+
129
+ # The raw data of the request.
130
+ # Useful to implement Webservices.
131
+ #--
132
+ # FIXME: better name and implementation.
133
+ #++
134
+
135
+ def raw_body
136
+ unless @raw_body
137
+ @in.rewind
138
+ @raw_body = @in.read(content_length)
139
+ end
140
+
141
+ @raw_body
142
+ end
143
+
144
+ # Lookup a query parameter.
145
+
146
+ def [](param)
147
+ @params[param]
148
+ end
149
+
150
+ # Set a query parameter.
151
+
152
+ def []=(param, value)
153
+ @params[param] = value
154
+ end
155
+
156
+ # Fetch a parameter with default value.
157
+
158
+ def fetch(param, default = nil)
159
+ @params.fetch(param, default)
160
+ end
161
161
  end
162
162
 
163
163
  end
@@ -1,42 +1,42 @@
1
1
  module Nitro
2
-
2
+
3
3
  # HTTP Response. This module is included in Context.
4
4
 
5
5
  module Response
6
6
 
7
- # The Response status.
7
+ # The Response status.
8
8
 
9
- attr_accessor :status
9
+ attr_accessor :status
10
10
 
11
- # The Response headers.
11
+ # The Response headers.
12
12
 
13
- attr_accessor :response_headers
13
+ attr_accessor :response_headers
14
14
 
15
- # The Response cookies.
15
+ # The Response cookies.
16
16
 
17
- attr_accessor :response_cookies
17
+ attr_accessor :response_cookies
18
18
 
19
- def content_type=(ctype)
20
- @response_headers['Content-Type'] = ctype
21
- end
19
+ def content_type=(ctype)
20
+ @response_headers['Content-Type'] = ctype
21
+ end
22
22
 
23
- # Add a cookie to the response. Better use this
24
- # method to avoid precreating the cookies array
25
- # for every request.
26
- #
27
- # === Examples
28
- #
29
- # add_cookie('nsid', 'gmosx')
30
- # add_cookie(Cookie.new('nsid', 'gmosx')
31
-
32
- def add_cookie(cookie, value = nil)
33
- if value
34
- (@response_cookies ||= []) << Cookie.new(cookie, value)
35
- else
36
- (@response_cookies ||= []) << cookie
37
- end
38
- end
39
- alias_method :send_cookie, :add_cookie
23
+ # Add a cookie to the response. Better use this
24
+ # method to avoid precreating the cookies array
25
+ # for every request.
26
+ #
27
+ # === Examples
28
+ #
29
+ # add_cookie('nsid', 'gmosx')
30
+ # add_cookie(Cookie.new('nsid', 'gmosx')
31
+
32
+ def add_cookie(cookie, value = nil)
33
+ if value
34
+ (@response_cookies ||= []) << Cookie.new(cookie, value)
35
+ else
36
+ (@response_cookies ||= []) << cookie
37
+ end
38
+ end
39
+ alias_method :send_cookie, :add_cookie
40
40
 
41
41
  end
42
42
 
data/lib/nitro/routing.rb CHANGED
@@ -3,28 +3,28 @@ module Nitro
3
3
  # Router mixin. Typically used to generate 'nice' urls.
4
4
  # Nice urls are considered (?) more Search Engine
5
5
  # friendly.
6
-
6
+
7
7
  module Router
8
-
9
- # The route table maps 'nice URLs' to real URLs that
10
- # can be handled by the Dispatcher.
11
-
12
- attr_accessor :routes
13
-
14
- # Apply routing rules to the path.
15
-
16
- def route(path, context)
17
- for rule, real_path, *params in @routes
18
- if md = path.match(rule)
19
- params.each_with_index do |p, idx|
20
- context[p] = md.captures[idx]
21
- end
22
- return real_path
23
- end
24
- end
25
-
26
- return path
27
- end
8
+
9
+ # The route table maps 'nice URLs' to real URLs that
10
+ # can be handled by the Dispatcher.
11
+
12
+ attr_accessor :routes
13
+
14
+ # Apply routing rules to the path.
15
+
16
+ def route(path, context)
17
+ for rule, real_path, *params in @routes
18
+ if md = path.match(rule)
19
+ params.each_with_index do |p, idx|
20
+ context[p] = md.captures[idx]
21
+ end
22
+ return real_path
23
+ end
24
+ end
25
+
26
+ return path
27
+ end
28
28
 
29
29
  end
30
30
 
@@ -2,9 +2,13 @@ require 'facet/string/singular'
2
2
  require 'facet/string/demodulize'
3
3
  require 'facet/string/underscore'
4
4
 
5
+ require 'nitro/compiler'
6
+
5
7
  module Nitro
6
8
 
7
9
  # The scaffolder adds default actions to a Controller.
10
+ #
11
+ # WARNING: This code is slightly outdated.
8
12
  #--
9
13
  # FIXME: handle controller base in generated routes.
10
14
  # FIXME: better handle templates (check if action exists).
@@ -12,124 +16,126 @@ module Nitro
12
16
 
13
17
  module Scaffolding
14
18
 
15
- def self.append_features(base) # :nodoc:
16
- super
17
- base.extend(ClassMethods)
18
- end
19
-
20
- module ClassMethods
21
-
22
- # Enchant the caller with a number of default methods.
23
- # Override the automatically generated methods as needed.
24
-
25
- def scaffold(klass, options = {})
26
-
27
- oid = options[:oid] || 'oid'
28
- name = options[:name] || klass.to_s.demodulize.underscore.downcase
29
- list_name = options[:list_name] || name.plural
30
- options[:nosuffix] ? suffix = nil : suffix = "_#{name}"
31
-
32
- # Add methods to the scaffolded class.
33
-
34
- klass.module_eval %{
35
- def to_href
36
- "view#{suffix}?oid=\#\{@oid\}"
37
- # "view#{suffix}/\#\{@oid\}"
38
- end
39
- }
40
-
41
- # Add methods to the service.
42
-
43
- code = ''
44
-
45
- if options[:index]
46
- code << %{
47
- def index
48
- list#{suffix}
49
- end
50
- }
51
- end
52
-
53
- code << %{
54
- def new#{suffix}
55
- @#{name} = #{klass}.new
56
- render '/form#{suffix}'
57
- end
58
-
59
- def edit#{suffix}
60
- @#{name} = #{klass}[request['oid']]
61
- render '/form#{suffix}'
62
- end
63
- }
64
-
65
- unless Rendering.template_for_action(self.template_root, "form#{suffix}")
66
- code << %{
67
- def form#{suffix}
68
- o << %|
69
- <html>
70
- <form action="save#{suffix}" method="post">
71
- |
72
- if @#{name}.oid
73
- o << %|
74
- <input type="hidden" name="oid" value="\#\{@#{name}\}" />
75
- |
76
- end
77
- o.build_form(@#{name})
78
- o << %|
79
- <input type="submit" value="Save" />
80
- </form>
81
- </html>
82
- |
83
- end
84
- }
85
- end
86
-
87
- code << %{
88
- # TODO: add pager support here!
89
-
90
- def list#{suffix}
91
- @#{list_name} = #{klass}.all('ORDER BY oid')
92
- }
93
- unless Rendering.template_for_action(self.template_root, "list#{suffix}")
94
- code << %{
95
- o.ul {
96
- for item in @#{list_name}
97
- o.li(item.to_s)
98
- end
99
- }
100
- }
101
- end
102
- code << %{
103
- end
104
-
105
- def view#{suffix}
106
- # @#{name} = #{klass}[@context['#{oid}']]
107
- @#{name} = #{klass}[@#{oid}]
108
- end
109
- action :view#{suffix}, :route => \%r\{#{@base}/view#{suffix}/(.*)\}, 'oid' => 1
110
-
111
- def save#{suffix}
112
- if oid = request['oid']
113
- @#{name} = request.fill(#{klass}[oid])
114
- else
115
- @#{name} = request.fill(#{klass}.new)
116
- end
117
-
118
- @#{name}.save
119
-
120
- redirect 'list#{suffix}'
121
- end
122
-
123
- def del#{suffix}
124
- #{klass}.delete(@context['#{oid}'])
125
- end
126
- alias_method :delete#{suffix}, :del#{suffix}
127
- }
128
-
129
- class_eval(code)
130
- end
131
-
132
- end
19
+ def self.append_features(base) # :nodoc:
20
+ super
21
+ base.extend(ClassMethods)
22
+ end
23
+
24
+ module ClassMethods
25
+
26
+ # Enchant the caller with a number of default methods.
27
+ # Override the automatically generated methods as needed.
28
+
29
+ def scaffold(klass, options = {})
30
+ compiler = Compiler.new
31
+
32
+ oid = options[:oid] || 'oid'
33
+ name = options[:name] || klass.to_s.demodulize.underscore.downcase
34
+ list_name = options[:list_name] || name.plural
35
+ options[:nosuffix] ? suffix = nil : suffix = "_#{name}"
36
+
37
+ # Add methods to the scaffolded class.
38
+
39
+ klass.module_eval %{
40
+ def to_href
41
+ "view#{suffix}?oid=\#\{@oid\}"
42
+ # "view#{suffix}/\#\{@oid\}"
43
+ end
44
+ }
45
+
46
+ # Add methods to the service.
47
+
48
+ code = ''
49
+
50
+ if options[:index]
51
+ code << %{
52
+ def index
53
+ list#{suffix}
54
+ end
55
+ }
56
+ end
57
+
58
+ code << %{
59
+ def new#{suffix}
60
+ @#{name} = #{klass}.new
61
+ render '/form#{suffix}'
62
+ end
63
+
64
+ def edit#{suffix}
65
+ @#{name} = #{klass}[request['oid']]
66
+ render '/form#{suffix}'
67
+ end
68
+ }
69
+
70
+ unless compiler.template_for_action("form#{suffix}", self.template_root)
71
+ code << %{
72
+ def form#{suffix}
73
+ o << %|
74
+ <html>
75
+ <form action="save#{suffix}" method="post">
76
+ |
77
+ if @#{name}.oid
78
+ o << %|
79
+ <input type="hidden" name="oid" value="\#\{@#{name}\}" />
80
+ |
81
+ end
82
+ o.build_form(@#{name})
83
+ o << %|
84
+ <input type="submit" value="Save" />
85
+ </form>
86
+ </html>
87
+ |
88
+ end
89
+ }
90
+ end
91
+
92
+ code << %{
93
+ # TODO: add pager support here!
94
+
95
+ def list#{suffix}
96
+ @#{list_name} = #{klass}.all('ORDER BY oid')
97
+ }
98
+ unless compiler.template_for_action("list#{suffix}", self.template_root)
99
+ code << %{
100
+ o.ul {
101
+ for item in @#{list_name}
102
+ o.li(item.to_s)
103
+ end
104
+ }
105
+ }
106
+ end
107
+ code << %{
108
+ end
109
+
110
+ def view#{suffix}
111
+ # @#{name} = #{klass}[@context['#{oid}']]
112
+ @#{name} = #{klass}[@#{oid}]
113
+ end
114
+ action :view#{suffix}, :route => \%r\{#{@base}/view#{suffix}/(.*)\}, 'oid' => 1
115
+
116
+ def save#{suffix}
117
+ if oid = request['oid']
118
+ @#{name} = request.fill(#{klass}[oid])
119
+ else
120
+ @#{name} = request.fill(#{klass}.new)
121
+ end
122
+
123
+ @#{name}.save
124
+
125
+ redirect 'list#{suffix}'
126
+ end
127
+
128
+ def del#{suffix}
129
+ #{klass}.delete(@context['#{oid}'])
130
+ redirect_referer
131
+ end
132
+ alias_method :delete#{suffix}, :del#{suffix}
133
+ }
134
+
135
+ class_eval(code)
136
+ end
137
+
138
+ end
133
139
 
134
140
  end
135
141