nitro 0.20.0 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
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