nitro 0.28.0 → 0.29.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +382 -0
- data/ProjectInfo +4 -4
- data/README +1 -1
- data/doc/AUTHORS +15 -15
- data/doc/MIGRATION +13 -0
- data/doc/RELEASES +102 -0
- data/lib/glue/sweeper.rb +1 -1
- data/lib/nitro.rb +38 -9
- data/lib/nitro/adapter/acgi.rb +1 -3
- data/lib/nitro/adapter/cgi.rb +1 -1
- data/lib/nitro/adapter/fastcgi.rb +1 -3
- data/lib/nitro/adapter/mongrel.rb +8 -6
- data/lib/nitro/adapter/webrick.rb +1 -2
- data/lib/nitro/cgi.rb +1 -1
- data/lib/nitro/compiler.rb +21 -40
- data/lib/nitro/compiler/elements.rb +72 -32
- data/lib/nitro/compiler/errors.rb +92 -42
- data/lib/nitro/compiler/include.rb +47 -17
- data/lib/nitro/compiler/morphing.rb +1 -3
- data/lib/nitro/compiler/script.rb +2 -2
- data/lib/nitro/context.rb +36 -0
- data/lib/nitro/controller.rb +140 -31
- data/lib/nitro/dispatcher.rb +27 -28
- data/lib/nitro/element.rb +52 -15
- data/lib/nitro/flash.rb +44 -0
- data/lib/nitro/helper/buffer.rb +0 -2
- data/lib/nitro/helper/form.rb +2 -2
- data/lib/nitro/helper/form/controls.rb +14 -3
- data/lib/nitro/helper/pager.rb +1 -1
- data/lib/nitro/helper/table.rb +4 -3
- data/lib/nitro/helper/xml.rb +1 -1
- data/lib/nitro/part.rb +20 -0
- data/lib/nitro/render.rb +44 -5
- data/lib/nitro/router.rb +81 -0
- data/lib/nitro/scaffolding.rb +24 -23
- data/lib/nitro/server.rb +12 -1
- data/lib/nitro/server/runner.rb +12 -0
- data/lib/nitro/session.rb +3 -12
- data/lib/nitro/session/drb.rb +2 -5
- data/lib/nitro/session/file.rb +2 -2
- data/lib/nitro/session/memcached.rb +14 -0
- data/lib/nitro/session/memory.rb +3 -26
- data/lib/nitro/session/og.rb +1 -1
- data/lib/nitro/test/assertions.rb +1 -1
- data/lib/nitro/test/context.rb +8 -2
- data/lib/nitro/test/testcase.rb +16 -7
- data/proto/public/error.xhtml +58 -21
- data/proto/public/js/controls.js +60 -15
- data/proto/public/js/dragdrop.js +105 -16
- data/proto/public/js/effects.js +19 -12
- data/proto/public/js/scriptaculous.js +1 -1
- data/proto/public/js/slider.js +2 -2
- data/proto/public/js/unittest.js +29 -20
- data/proto/public/scaffold/edit.xhtml +1 -1
- data/proto/public/scaffold/index.xhtml +2 -2
- data/proto/public/scaffold/list.xhtml +2 -2
- data/proto/public/scaffold/new.xhtml +1 -1
- data/proto/public/scaffold/search.xhtml +1 -1
- data/src/part/admin/controller.rb +5 -5
- data/src/part/admin/template/index.xhtml +2 -2
- data/test/nitro/compiler/tc_compiler.rb +23 -0
- data/test/nitro/helper/tc_table.rb +35 -0
- data/test/nitro/tc_cgi.rb +1 -1
- data/test/nitro/tc_controller.rb +3 -3
- data/test/nitro/tc_controller_aspect.rb +2 -0
- data/test/nitro/tc_dispatcher.rb +10 -1
- data/test/nitro/tc_flash.rb +14 -0
- data/test/nitro/tc_router.rb +58 -0
- data/test/nitro/tc_session.rb +26 -9
- metadata +13 -12
- data/lib/nitro/routing.rb +0 -41
- data/test/nitro/caching/tc_stores.rb +0 -17
- data/test/nitro/tc_table.rb +0 -66
data/lib/nitro/controller.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'facet/annotation'
|
2
|
+
require 'facet/inheritor'
|
2
3
|
|
3
4
|
require 'glue/aspects'
|
4
5
|
require 'glue/markup'
|
@@ -9,12 +10,31 @@ require 'nitro/scaffolding'
|
|
9
10
|
require 'nitro/caching'
|
10
11
|
require 'nitro/flash'
|
11
12
|
require 'nitro/helper'
|
13
|
+
require 'nitro/compiler'
|
12
14
|
|
13
15
|
module Nitro
|
14
16
|
|
15
17
|
# Include this Mixin to a class to make objects of this class
|
16
18
|
# publishable, ie accessible through a standard web (REST)
|
17
19
|
# interface.
|
20
|
+
#
|
21
|
+
# === Instance variables
|
22
|
+
#
|
23
|
+
# ==== mount_path
|
24
|
+
#
|
25
|
+
# Where the publishable is mounted.
|
26
|
+
#
|
27
|
+
# ==== template_root
|
28
|
+
#
|
29
|
+
# Where to look for templates for this publishable
|
30
|
+
# object / controller. The template root is actually a stack
|
31
|
+
# to implement some form of template root inheritance,
|
32
|
+
# thus allowing for more reusable controllers. Ie you can
|
33
|
+
# 'extend' a controller, and only override the templates
|
34
|
+
# you want to change. The compiler will traverse the
|
35
|
+
# template root stack and use the templates from parent
|
36
|
+
# controllers if they are not overriden.
|
37
|
+
|
18
38
|
|
19
39
|
module Publishable
|
20
40
|
def self.included(base)
|
@@ -25,22 +45,6 @@ module Publishable
|
|
25
45
|
include Glue::Aspects
|
26
46
|
include Flashing
|
27
47
|
include Helpers
|
28
|
-
|
29
|
-
# Override this method as needed. Unless overriden
|
30
|
-
# this method is initialized by the Dispatcher to
|
31
|
-
# point to Template.root/path for each Controller.
|
32
|
-
|
33
|
-
def self.template_root
|
34
|
-
nil
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
# Helper method.
|
40
|
-
|
41
|
-
def template_root
|
42
|
-
self.class.template_root
|
43
|
-
end
|
44
48
|
end
|
45
49
|
|
46
50
|
# Aliases an action
|
@@ -118,25 +122,128 @@ module Publishable
|
|
118
122
|
end
|
119
123
|
end
|
120
124
|
end
|
125
|
+
|
126
|
+
base.module_eval do
|
127
|
+
class << self
|
128
|
+
# Override this method to customize the template_root.
|
129
|
+
# Typically used in controllers defined in reusable Parts.
|
130
|
+
# Call super to include the parent class's customizations.
|
131
|
+
#
|
132
|
+
# def setup_template_root(path)
|
133
|
+
# super
|
134
|
+
# @template_root << "custom/route/#{path}"
|
135
|
+
# @template_root << "another/route/#{path}"
|
136
|
+
# end
|
137
|
+
|
138
|
+
def setup_template_root(path)
|
139
|
+
end
|
140
|
+
|
141
|
+
def mount_at(path)
|
142
|
+
# Store the mount_path (where the controller is mounted).
|
143
|
+
|
144
|
+
@mount_path = path
|
145
|
+
|
146
|
+
# Update template_root. Unshift the PROTO_TEMPLATE_ROOT,
|
147
|
+
# and unshift a template_root relative to the
|
148
|
+
# application template root.
|
149
|
+
|
150
|
+
@template_root = []
|
151
|
+
@template_root << File.join(Compiler::PROTO_TEMPLATE_ROOT, path).gsub(/\/$/, '')
|
152
|
+
setup_template_root(path)
|
153
|
+
@template_root << File.join(Glue::Template.root, path).gsub(/\/$/, '')
|
154
|
+
@template_root.reverse!
|
155
|
+
end
|
156
|
+
alias_method :mount, :mount_at
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
121
161
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
162
|
+
private
|
163
|
+
|
164
|
+
# Cookie helpers.
|
165
|
+
#--
|
166
|
+
# TODO: move elsewhere.
|
167
|
+
#++
|
126
168
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
169
|
+
def cookies
|
170
|
+
@context.cookies
|
171
|
+
end
|
172
|
+
|
173
|
+
def send_cookie(name, value = nil)
|
174
|
+
@context.add_cookie(name, value)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Encode controller, action, params into a valid url.
|
178
|
+
# Automatically respects nice urls and routing.
|
179
|
+
#
|
180
|
+
# Handles parameters either as a hash or as an array.
|
181
|
+
# Use the array method to pass parameters to 'nice' actions.
|
182
|
+
#
|
183
|
+
# Pass Controller, action, and (param_name, param_value)
|
184
|
+
# pairs.
|
185
|
+
#
|
186
|
+
# === Examples
|
187
|
+
#
|
188
|
+
# encode_url ForaController, :post, :title, 'Hello', :body, 'World'
|
189
|
+
# encode_url :post, :title, 'Hello', :body, 'World' # => implies controller == self
|
190
|
+
# encode_url :kick, :oid, 4
|
191
|
+
#--
|
192
|
+
# FIXME: better implementation? optimize this?
|
193
|
+
# TODO: move elsewhere.
|
194
|
+
#++
|
195
|
+
|
196
|
+
def encode_url(*args)
|
197
|
+
if args.first.is_a?(Symbol) or args.first.is_a?(String)
|
198
|
+
# no controller passed, imply controller == self!
|
199
|
+
args.unshift(self.class)
|
200
|
+
end
|
201
|
+
|
202
|
+
# Try to encode using the router.
|
203
|
+
|
204
|
+
if url = context.dispatcher.encode_route(*args)
|
205
|
+
return url
|
206
|
+
end
|
207
|
+
|
208
|
+
# No routing rule, manual encoding.
|
209
|
+
|
210
|
+
controller = args.shift
|
211
|
+
action = args.shift.to_sym
|
212
|
+
|
213
|
+
url = "/#{controller.ann.self.mount_point}/#{action}"
|
214
|
+
|
215
|
+
unless args.empty?
|
216
|
+
if controller.respond_to? action
|
217
|
+
param_count = controller.instance_method(action).arity
|
218
|
+
if param_count != 0
|
219
|
+
param_count.times do
|
220
|
+
args.shift # name
|
221
|
+
url << "/#{CGI.escape(args.shift.to_s)}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
133
225
|
|
134
|
-
|
135
|
-
|
226
|
+
unless args.empty?
|
227
|
+
url << '?'
|
228
|
+
params = []
|
229
|
+
(args.size / 2).times do
|
230
|
+
params << "#{args.shift}=#{args.shift}"
|
231
|
+
end
|
232
|
+
url << params.join(';')
|
136
233
|
end
|
137
234
|
end
|
235
|
+
|
236
|
+
return url
|
138
237
|
end
|
238
|
+
alias_method :R, :encode_url
|
239
|
+
|
240
|
+
# Just like encode_url, but generates an absolute url instead.
|
139
241
|
|
242
|
+
def encode_absolute_url(*args)
|
243
|
+
return "#{request.host_url}#{encode_url(*args)}"
|
244
|
+
end
|
245
|
+
alias_method :RA, :encode_absolute_url
|
246
|
+
|
140
247
|
end
|
141
248
|
|
142
249
|
# The Controller part in the MVC paradigm. The controller's
|
@@ -148,17 +255,19 @@ class Controller
|
|
148
255
|
include Scaffolding
|
149
256
|
include Caching
|
150
257
|
include Helpers
|
151
|
-
helper Markup
|
258
|
+
helper Glue::Markup
|
152
259
|
|
153
260
|
# This callback is called after the Controller is mounted.
|
154
261
|
|
155
262
|
def self.mounted(path)
|
156
263
|
# Resolve aspects.
|
157
|
-
|
264
|
+
|
265
|
+
Glue::Aspects.include_advice_modules(self)
|
158
266
|
|
159
267
|
# The scaffolding code is compiled after the mount, so
|
160
268
|
# that template roots are finalized.
|
161
|
-
|
269
|
+
|
270
|
+
compile_scaffolding_code()
|
162
271
|
end
|
163
272
|
|
164
273
|
end
|
data/lib/nitro/dispatcher.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'nitro/controller'
|
2
2
|
require 'nitro/compiler'
|
3
|
-
require 'nitro/
|
3
|
+
require 'nitro/router'
|
4
4
|
require 'nitro/helper/default'
|
5
5
|
|
6
6
|
module Nitro
|
@@ -63,28 +63,26 @@ class Dispatcher
|
|
63
63
|
# disp.publish '/' => MainController
|
64
64
|
|
65
65
|
def add_controller(controllers)
|
66
|
-
for path,
|
67
|
-
unless (
|
68
|
-
|
66
|
+
for path, klass in controllers
|
67
|
+
unless (klass.ancestors.include?(Controller) or klass.ancestors.include?(Publishable))
|
68
|
+
klass.send :include, Publishable
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
# Automatically mixin controller helpers.
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
unless c.template_root
|
76
|
-
c.module_eval %{
|
77
|
-
def self.template_root
|
78
|
-
"#{Template.root}#{path}".gsub(/\\/$/, '')
|
79
|
-
end
|
80
|
-
}
|
81
|
-
end
|
73
|
+
mixin_auto_helpers(klass)
|
82
74
|
|
83
|
-
#
|
75
|
+
# Customize the class for mounting at the given path.
|
76
|
+
#--
|
77
|
+
# gmosx, TODO: should actually create an instance, thus
|
78
|
+
# allowing mounting the same controller to multiple
|
79
|
+
# paths, plus simplifying the code. This instance will
|
80
|
+
# be dup-ed for each request.
|
81
|
+
#++
|
84
82
|
|
85
|
-
|
83
|
+
klass.mount_at(path)
|
86
84
|
|
87
|
-
|
85
|
+
klass.mounted(path) if klass.respond_to?(:mounted)
|
88
86
|
end
|
89
87
|
|
90
88
|
(@controllers ||= {}).update(controllers)
|
@@ -100,12 +98,12 @@ class Dispatcher
|
|
100
98
|
# default helper 'Helper' and the auto helper
|
101
99
|
# 'XxxControllerHelper' (if it exists) are included.
|
102
100
|
|
103
|
-
def
|
104
|
-
|
101
|
+
def mixin_auto_helpers(klass)
|
102
|
+
klass.helper(Nitro::DefaultHelper)
|
105
103
|
|
106
104
|
begin
|
107
|
-
if helper = Module.by_name("#{
|
108
|
-
|
105
|
+
if helper = Module.by_name("#{klass}Helper")
|
106
|
+
klass.helper(helper)
|
109
107
|
end
|
110
108
|
rescue NameError
|
111
109
|
# The auto helper is not defined.
|
@@ -123,12 +121,7 @@ class Dispatcher
|
|
123
121
|
for m in c.action_methods
|
124
122
|
m = m.to_sym
|
125
123
|
if route = c.ann(m).route and (!route.nil?)
|
126
|
-
|
127
|
-
keys = c.ann(m).params.keys
|
128
|
-
else
|
129
|
-
keys = []
|
130
|
-
end
|
131
|
-
@routes << [route, "#{base}/#{m}", *keys]
|
124
|
+
add_route(route.first, c, m, route.last)
|
132
125
|
end
|
133
126
|
end
|
134
127
|
end
|
@@ -159,7 +152,13 @@ class Dispatcher
|
|
159
152
|
#++
|
160
153
|
|
161
154
|
def dispatch(path, context = nil)
|
162
|
-
|
155
|
+
# Try if the router can directly decode the path.
|
156
|
+
|
157
|
+
klass, action, params = decode_route(path)
|
158
|
+
if klass
|
159
|
+
context.params.update(params)
|
160
|
+
return klass, "#{action}_action", controller.ann.self.mount_point
|
161
|
+
end
|
163
162
|
|
164
163
|
parts = path.split('/')
|
165
164
|
parts.shift # get rid of the leading '/'.
|
data/lib/nitro/element.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'facet/string/capitalized'
|
2
2
|
require 'facet/string/camelize'
|
3
|
-
require 'facet/
|
3
|
+
require 'facet/class/method_name'
|
4
4
|
require 'facet/dir/self/recurse'
|
5
5
|
require 'facet/annotation'
|
6
6
|
|
7
|
-
require 'glue/flexob'
|
8
7
|
require 'glue/configuration'
|
9
8
|
require 'glue/template'
|
10
9
|
|
@@ -57,29 +56,44 @@ module ElementMixin
|
|
57
56
|
|
58
57
|
attr_accessor :_view
|
59
58
|
|
59
|
+
# The id of this element.
|
60
|
+
|
61
|
+
attr_accessor :id
|
62
|
+
|
60
63
|
def initialize(*args)
|
61
64
|
@_children = {}
|
62
65
|
@_text = ''
|
66
|
+
@id = self.class.demodulize.underscore
|
63
67
|
end
|
64
68
|
|
69
|
+
# Prepend this code to the element content.
|
70
|
+
|
65
71
|
def open
|
66
72
|
end
|
67
73
|
|
68
74
|
# If an optional name parameter is passed renders
|
69
75
|
# the content of the named child element.
|
76
|
+
#
|
77
|
+
# eg. #{content :child_element_id}
|
70
78
|
|
71
79
|
def content(cname = nil)
|
72
80
|
if cname
|
73
|
-
@_children[cname]
|
81
|
+
if c = @_children[cname.to_s]
|
82
|
+
c.content
|
83
|
+
else
|
84
|
+
Logger.error "Undefined sub-element '#{cname}' for element '#{self.class}'"
|
85
|
+
end
|
74
86
|
else
|
75
87
|
@_text
|
76
88
|
end
|
77
89
|
end
|
78
90
|
|
91
|
+
# Append this code to the element content.
|
92
|
+
|
79
93
|
def close
|
80
94
|
end
|
81
95
|
|
82
|
-
#
|
96
|
+
# Override this.
|
83
97
|
|
84
98
|
def render
|
85
99
|
"#{open}#{content}#{close}"
|
@@ -95,14 +109,42 @@ module ElementMixin
|
|
95
109
|
end
|
96
110
|
|
97
111
|
def add_child(child)
|
98
|
-
child._parent = self
|
99
|
-
@_children[
|
112
|
+
child._parent = self
|
113
|
+
@_children[child.id] = child
|
100
114
|
end
|
101
115
|
|
102
116
|
alias_method :children, :_children
|
103
117
|
end
|
104
118
|
|
105
119
|
# A programmatically generated element.
|
120
|
+
#
|
121
|
+
# === Usage
|
122
|
+
#
|
123
|
+
# = in the code
|
124
|
+
#
|
125
|
+
# class Page < Nitro::Element
|
126
|
+
# def render
|
127
|
+
# %{
|
128
|
+
# <div id="@id">#{content}</div>
|
129
|
+
# }
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# = in your template
|
134
|
+
#
|
135
|
+
# <Page>hello</Page>
|
136
|
+
#
|
137
|
+
# => <div id="page">hello</div>
|
138
|
+
#
|
139
|
+
# the id is automatically fille with the class name using class.method_name
|
140
|
+
# eg. MyModule::MyPage => my_module__my_page
|
141
|
+
#
|
142
|
+
# you can override the id to use the element multiple times on the page
|
143
|
+
#
|
144
|
+
# == Sub Elements
|
145
|
+
#
|
146
|
+
# Elements can be imbricated. To render the the child element in the parent's template,
|
147
|
+
# use #{content :element_id}
|
106
148
|
#
|
107
149
|
# === Design
|
108
150
|
#
|
@@ -125,10 +167,10 @@ class Element
|
|
125
167
|
setting :auto_extend, :default => true, :doc => 'Allow auto extension of element classes?'
|
126
168
|
|
127
169
|
# The directory where element templates reside. The default
|
128
|
-
# dir is #{Template.root}/element
|
170
|
+
# dir is #{Glue::Template.root}/element
|
129
171
|
|
130
|
-
if File.exist?(File.join(Template.root, 'element'))
|
131
|
-
default_root = File.join(Template.root, 'element')
|
172
|
+
if File.exist?(File.join(Glue::Template.root, 'element'))
|
173
|
+
default_root = File.join(Glue::Template.root, 'element')
|
132
174
|
else
|
133
175
|
default_root = 'element'
|
134
176
|
end
|
@@ -144,11 +186,6 @@ class Element
|
|
144
186
|
setting :template_root, :default => default_root, :doc => 'The directory where element templates reside'
|
145
187
|
|
146
188
|
class << self
|
147
|
-
# Return the name for an Element class.
|
148
|
-
|
149
|
-
def class_to_name(klass)
|
150
|
-
klass.to_s.underscore.to_sym
|
151
|
-
end
|
152
189
|
|
153
190
|
# Compile the element templates into element classes.
|
154
191
|
# Typically called at startup.
|
@@ -156,7 +193,7 @@ class Element
|
|
156
193
|
def compile_template_elements
|
157
194
|
if File.exist? Element.template_root
|
158
195
|
Dir.recurse(Element.template_root) do |filename|
|
159
|
-
if filename =~ /\.#{Template.extension}$/
|
196
|
+
if filename =~ /\.#{Glue::Template.extension}$/
|
160
197
|
name = File.basename(filename).split('.').first.camelize
|
161
198
|
Nitro::Element.module_eval %{
|
162
199
|
class #{name} < Nitro::Element
|