nitro 0.28.0 → 0.29.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.
- 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
|