nitro 0.22.0 → 0.23.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 +109 -1250
- data/INSTALL +3 -2
- data/README +5 -4
- data/Rakefile +1 -1
- data/bin/nitrogen +1 -1
- data/doc/AUTHORS +20 -6
- data/doc/CHANGELOG.3 +1314 -0
- data/doc/RELEASES +90 -0
- data/lib/nitro.rb +6 -17
- data/lib/nitro/adapter/cgi.rb +11 -0
- data/lib/nitro/adapter/webrick.rb +7 -1
- data/lib/nitro/caching/stores.rb +4 -6
- data/lib/nitro/compiler.rb +1 -1
- data/lib/nitro/compiler/errors.rb +1 -1
- data/lib/nitro/context.rb +11 -4
- data/lib/nitro/controller.rb +0 -3
- data/lib/nitro/cookie.rb +4 -3
- data/lib/nitro/dispatcher.rb +7 -1
- data/lib/nitro/dispatcher/nice.rb +6 -1
- data/lib/nitro/element.rb +2 -2
- data/lib/nitro/mixin/benchmark.rb +16 -0
- data/lib/nitro/mixin/form.rb +171 -55
- data/lib/nitro/mixin/rss.rb +1 -1
- data/lib/nitro/mixin/xhtml.rb +91 -4
- data/lib/nitro/render.rb +25 -6
- data/lib/nitro/request.rb +13 -3
- data/lib/nitro/scaffold.rb +91 -68
- data/lib/nitro/scaffold/relations.rb +54 -0
- data/lib/nitro/server.rb +8 -5
- data/lib/nitro/server/runner.rb +4 -3
- data/lib/nitro/service.rb +3 -1
- data/lib/nitro/service/xmlrpc.rb +1 -1
- data/lib/nitro/session.rb +69 -51
- data/lib/nitro/session/drb.rb +5 -7
- data/lib/nitro/session/drbserver.rb +4 -6
- data/lib/nitro/session/memory.rb +4 -6
- data/lib/part/admin.rb +6 -0
- data/lib/part/admin/controller.rb +24 -0
- data/lib/part/admin/skin.rb +21 -0
- data/lib/part/admin/template/index.xhtml +9 -0
- data/proto/public/js/cookies.js +122 -0
- data/proto/public/scaffold/edit.xhtml +4 -0
- data/proto/public/scaffold/form.xhtml +7 -0
- data/proto/public/scaffold/list.xhtml +15 -0
- data/proto/public/scaffold/new.xhtml +4 -0
- data/proto/public/scaffold/view.xhtml +0 -0
- data/proto/script/benchmark +19 -0
- data/test/nitro/adapter/tc_cgi.rb +32 -2
- data/test/nitro/mixin/tc_xhtml.rb +6 -0
- data/test/nitro/tc_dispatcher.rb +0 -17
- data/test/nitro/tc_render.rb +58 -0
- data/test/nitro/tc_server.rb +2 -0
- data/test/nitro/tc_session.rb +16 -0
- metadata +104 -85
data/lib/nitro/mixin/rss.rb
CHANGED
data/lib/nitro/mixin/xhtml.rb
CHANGED
@@ -5,6 +5,8 @@ module Nitro
|
|
5
5
|
|
6
6
|
module XhtmlMixin
|
7
7
|
|
8
|
+
private
|
9
|
+
|
8
10
|
# Render select options. The parameter is a hash of options.
|
9
11
|
#
|
10
12
|
# [+labels+]
|
@@ -13,14 +15,17 @@ module XhtmlMixin
|
|
13
15
|
# [+values+]
|
14
16
|
# The corresponding values.
|
15
17
|
#
|
16
|
-
#
|
18
|
+
# [+labels_values+]
|
19
|
+
# Use when labels == values.
|
20
|
+
#
|
21
|
+
# [+selected+]
|
17
22
|
# The value of the selected option.
|
18
23
|
#
|
19
24
|
# === Examples
|
20
25
|
#
|
21
26
|
# labels = ['Male', 'Female']
|
22
27
|
# o.select(:name => 'sex') {
|
23
|
-
#
|
28
|
+
# o.options(:labels => labels, :selected => 1)
|
24
29
|
# }
|
25
30
|
#
|
26
31
|
# or
|
@@ -29,11 +34,14 @@ module XhtmlMixin
|
|
29
34
|
# #{build :options, :labels => labels, :values => [..], :selected => 1}
|
30
35
|
|
31
36
|
def options(options = {})
|
32
|
-
if labels = options[:labels]
|
37
|
+
if labels = options[:labels] || options[:labels_values]
|
33
38
|
str = ''
|
34
39
|
|
35
40
|
unless values = options[:values]
|
36
|
-
values =
|
41
|
+
if values = options[:labels_values]
|
42
|
+
else
|
43
|
+
values = (0...labels.size).to_a
|
44
|
+
end
|
37
45
|
end
|
38
46
|
|
39
47
|
selected = (options[:selected] || -1).to_i
|
@@ -67,6 +75,85 @@ module XhtmlMixin
|
|
67
75
|
|
68
76
|
return str
|
69
77
|
end
|
78
|
+
|
79
|
+
# Render a date select. Override to customize this.
|
80
|
+
#
|
81
|
+
# === Example
|
82
|
+
#
|
83
|
+
# #{date_select date, :name => 'brithday'}
|
84
|
+
|
85
|
+
def date_select(date, options = {})
|
86
|
+
raise 'No name provided to date_select' unless name = options[:name]
|
87
|
+
date ||= Time.now
|
88
|
+
%{
|
89
|
+
<select id="#{name}.day" name="#{name}.day">
|
90
|
+
#{options(:labels_values => (1..31).to_a, :selected => date.day)}
|
91
|
+
</select>
|
92
|
+
<select id="#{name}.month" name="#{name}.month">
|
93
|
+
#{options(:labels => Date::MONTHNAMES, :values => (1..12).to_a, :selected => (date.month+1))}
|
94
|
+
</select>
|
95
|
+
<select id="#{name}.year" name="#{name}.year">
|
96
|
+
#{options(:labels_values => ((Time.now.year-10)..(Time.now.year+10)).to_a, :selected => date.year)}
|
97
|
+
</select>}
|
98
|
+
end
|
99
|
+
|
100
|
+
# Render a time select. Override to customize this.
|
101
|
+
|
102
|
+
def time_select(time, options = {})
|
103
|
+
raise 'No name provided to time_select' unless name = options[:name]
|
104
|
+
time ||= Time.now
|
105
|
+
%{
|
106
|
+
<select id="#{name}.hour" name="#{name}.hour">
|
107
|
+
#{options(:labels_values => (1..60).to_a, :selected => time.hour)}
|
108
|
+
</select>
|
109
|
+
<select id="#{name}.min" name="#{name}.min">
|
110
|
+
#{options(:labels_values => (1..60).to_a, :selected => time.min)}
|
111
|
+
</select>}
|
112
|
+
end
|
113
|
+
|
114
|
+
# Render a datetime select. Override to customize this.
|
115
|
+
|
116
|
+
def datetime_select(time, options)
|
117
|
+
date_select(time, options) + ' at ' + time_select(time, options)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# gmosx: keep the leading / to be IE friendly.
|
122
|
+
|
123
|
+
def js_popup(options = {})
|
124
|
+
o = {
|
125
|
+
:width => 320,
|
126
|
+
:height => 240,
|
127
|
+
:title => 'Popup',
|
128
|
+
:resizable => false,
|
129
|
+
:scrollbars => false,
|
130
|
+
}.merge(options)
|
131
|
+
|
132
|
+
poptions = (o[:resizable] ? 'resizable=yes' : 'resizable=no')
|
133
|
+
poptions << (o[:scrollbars] ? 'scrollbars=yes' : 'scrollbars=no')
|
134
|
+
|
135
|
+
url = o[:url] || o[:uri]
|
136
|
+
|
137
|
+
%[javascript: var pwl = (screen.width - #{o[:width]}) / 2; var pwt = (screen.height - #{o[:height]}) / 2; window.open('#{url}', '#{o[:title]}', 'width=#{o[:width]},height=#{o[:height]},top='+pwt+',left='+pwl+', #{poptions}'); return false;"]
|
138
|
+
end
|
139
|
+
|
140
|
+
# === Example
|
141
|
+
#
|
142
|
+
# <a href="#" #{onclick_popup 'add-comment', :scrollbars => true}>Hello</a>
|
143
|
+
|
144
|
+
def onclick_popup(options = {})
|
145
|
+
%|onclick="#{js_popup(options)}"|
|
146
|
+
end
|
147
|
+
|
148
|
+
# Emit a link that spawns a popup window.
|
149
|
+
#
|
150
|
+
# === Example
|
151
|
+
#
|
152
|
+
# <a href="#" #{onclick :text => 'Hello', :url => 'add-comment', :scrollbars => true}>Hello</a>
|
153
|
+
|
154
|
+
def popup(options = {})
|
155
|
+
%|<a href="#" #{onclick_popup(options)}>#{options[:text] || 'Popup'}</a>|
|
156
|
+
end
|
70
157
|
|
71
158
|
end
|
72
159
|
|
data/lib/nitro/render.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'singleton'
|
2
2
|
require 'sync'
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'nano/string/blank%3F'
|
5
5
|
|
6
6
|
require 'glue/attribute'
|
7
|
-
require 'glue/misc'
|
8
|
-
require 'glue/object'
|
9
7
|
require 'glue/settings'
|
10
8
|
require 'glue/template'
|
11
9
|
require 'glue/builder'
|
@@ -76,8 +74,15 @@ module Render
|
|
76
74
|
# The name of the currently executing action.
|
77
75
|
|
78
76
|
attr_accessor :action_name
|
77
|
+
|
78
|
+
# The base url for this render.
|
79
|
+
|
80
|
+
attr_accessor :base
|
79
81
|
|
80
82
|
# The name of the current controller.
|
83
|
+
#--
|
84
|
+
# gmosx: Needed for WEE. Will be deprecated.
|
85
|
+
#++
|
81
86
|
|
82
87
|
attr_accessor :controller_name
|
83
88
|
|
@@ -88,17 +93,24 @@ module Render
|
|
88
93
|
|
89
94
|
def initialize(context, base = nil)
|
90
95
|
@request = @response = @context = context
|
91
|
-
@controller_name = base
|
96
|
+
@controller_name = @base = base
|
92
97
|
@out = context.out
|
93
98
|
end
|
94
99
|
|
95
100
|
# Renders the action denoted by path. The path
|
96
101
|
# is resolved by the dispatcher to get the correct
|
97
102
|
# controller.
|
103
|
+
#
|
104
|
+
# Both relative and absolute paths are supported. Relative
|
105
|
+
# paths are converted to absolute by prepending the @base
|
106
|
+
# path of the controller.
|
98
107
|
|
99
108
|
def render(path)
|
109
|
+
# Convert relative paths to absolute paths.
|
110
|
+
path = "#@base/#{path}" unless path =~ /^\//
|
111
|
+
|
100
112
|
Logger.debug "Rendering '#{path}'." if $DBG
|
101
|
-
|
113
|
+
|
102
114
|
klass, action, base = @context.dispatcher.dispatch(path, @context)
|
103
115
|
|
104
116
|
# FIXME:
|
@@ -129,10 +141,17 @@ module Render
|
|
129
141
|
private
|
130
142
|
|
131
143
|
# Send a redirect response.
|
144
|
+
#
|
145
|
+
# If the url starts with '/' it is considered absolute, else
|
146
|
+
# the url is considered relative to the current controller and
|
147
|
+
# the controller base is prepended.
|
132
148
|
|
133
149
|
def redirect(url, status = 303)
|
134
150
|
url = url.to_s
|
135
|
-
|
151
|
+
unless url =~ /^http/
|
152
|
+
url = "#@base/#{url}" unless url =~ /^\//
|
153
|
+
url = "#{@context.host_url}/#{url.gsub(/^\//, '')}"
|
154
|
+
end
|
136
155
|
|
137
156
|
@context.status = status
|
138
157
|
@context.out = "<html><a href=\"#{url}\">#{url}</a>.</html>\n"
|
data/lib/nitro/request.rb
CHANGED
@@ -30,13 +30,14 @@ module Request
|
|
30
30
|
# The request protocol.
|
31
31
|
|
32
32
|
def protocol
|
33
|
-
|
33
|
+
@headers['HTTPS'] == 'on' ? 'https://' : 'http://'
|
34
34
|
end
|
35
35
|
|
36
36
|
# Is this an ssl request?
|
37
37
|
|
38
38
|
def ssl?
|
39
|
-
|
39
|
+
# 443 == port
|
40
|
+
@headers['HTTPS'] == 'on'
|
40
41
|
end
|
41
42
|
|
42
43
|
# The request uri.
|
@@ -208,8 +209,9 @@ module Request
|
|
208
209
|
# The host url.
|
209
210
|
|
210
211
|
def host_url
|
211
|
-
"
|
212
|
+
"#{protocol}#{host}"
|
212
213
|
end
|
214
|
+
alias_method :server_url, :host_url
|
213
215
|
|
214
216
|
# The raw data of the request.
|
215
217
|
# Useful to implement Webservices.
|
@@ -243,8 +245,16 @@ module Request
|
|
243
245
|
def fetch(param, default = nil)
|
244
246
|
@params.fetch(param, default)
|
245
247
|
end
|
248
|
+
|
249
|
+
# Check if a param is available.
|
250
|
+
|
251
|
+
def has_key?(key)
|
252
|
+
@params.has_key?(key)
|
253
|
+
end
|
254
|
+
alias_method :has_param?, :has_key?
|
246
255
|
end
|
247
256
|
|
248
257
|
end
|
249
258
|
|
250
259
|
# * George Moschovitis <gm@navel.gr>
|
260
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@laposte.net>
|
data/lib/nitro/scaffold.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
require '
|
2
|
-
require 'facet/string/demodulize'
|
3
|
-
require 'facet/string/underscore'
|
1
|
+
require 'mega/orm_support'
|
4
2
|
|
5
3
|
require 'nitro/compiler'
|
4
|
+
require 'nitro/mixin/form'
|
6
5
|
|
7
6
|
module Nitro
|
8
7
|
|
9
8
|
# The scaffolder adds default actions to a Controller.
|
10
9
|
#
|
11
|
-
# WARNING: This code is slightly outdated.
|
12
10
|
#--
|
13
11
|
# FIXME: handle controller base in generated routes.
|
14
12
|
# FIXME: better handle templates (check if action exists).
|
@@ -18,9 +16,35 @@ module Scaffolding
|
|
18
16
|
|
19
17
|
def self.append_features(base) # :nodoc:
|
20
18
|
super
|
19
|
+
base.send :include, FormMixin
|
21
20
|
base.extend(ClassMethods)
|
22
21
|
end
|
23
22
|
|
23
|
+
def self.class_to_name(klass)
|
24
|
+
klass.to_s.demodulize.underscore.downcase
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.class_to_list(klass)
|
28
|
+
klass.to_s.demodulize.underscore.downcase.plural
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.compile_scaffold_action(compiler, controller, action, suffix)
|
32
|
+
unless compiler.template_for_action("#{action}#{suffix}", controller.template_root)
|
33
|
+
source = File.read(File.join(Nitro.proto_path, 'public', 'scaffold', "#{action}.xhtml"))
|
34
|
+
template = compiler.transform_template(source)
|
35
|
+
|
36
|
+
yield template
|
37
|
+
|
38
|
+
return %{
|
39
|
+
def #{action}#{suffix}
|
40
|
+
#{template}
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
24
48
|
module ClassMethods
|
25
49
|
|
26
50
|
# Enchant the caller with a number of default methods.
|
@@ -30,108 +54,107 @@ module Scaffolding
|
|
30
54
|
compiler = Compiler.new
|
31
55
|
|
32
56
|
oid = options[:oid] || 'oid'
|
33
|
-
name = options[:name] || klass
|
34
|
-
list_name = options[:
|
35
|
-
options[:nosuffix] ?
|
57
|
+
name = options[:name] || Scaffolding.class_to_name(klass)
|
58
|
+
list_name = options[:plural_name] || name.plural
|
59
|
+
suffix = options[:nosuffix] ? nil : "_#{name}"
|
36
60
|
|
37
61
|
# Add methods to the scaffolded class.
|
38
62
|
|
39
|
-
klass.
|
40
|
-
|
41
|
-
"
|
42
|
-
# "view#{suffix}/\#\{@oid\}"
|
63
|
+
unless klass.respond_to? :to_href
|
64
|
+
klass.send(:define_method, :to_href) do
|
65
|
+
"#{name}/#{@oid}"
|
43
66
|
end
|
44
|
-
|
67
|
+
end
|
68
|
+
|
69
|
+
unless klass.respond_to? :to_edit_link
|
70
|
+
klass.send(:define_method, :to_edit_link) do |base|
|
71
|
+
%{<a href="#{base}/#{name}/#{@oid}">#{self}</a> (<a href="#{base}/edit#{suffix}/#{@oid}">edit</a>, <a href="#{base}/delete#{suffix}/#{@oid}">del</a>)}
|
72
|
+
end
|
73
|
+
end
|
45
74
|
|
46
75
|
# Add methods to the service.
|
47
76
|
|
48
77
|
code = ''
|
49
78
|
|
50
|
-
if options[:index]
|
51
|
-
code << %{
|
52
|
-
def index
|
53
|
-
list#{suffix}
|
54
|
-
end
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
79
|
code << %{
|
59
80
|
def new#{suffix}
|
60
81
|
@#{name} = #{klass}.new
|
61
|
-
render '
|
82
|
+
render 'form#{suffix}'
|
62
83
|
end
|
63
84
|
|
64
|
-
def edit#{suffix}
|
65
|
-
@#{name} = #{klass}[
|
66
|
-
render '
|
85
|
+
def edit#{suffix}(oid)
|
86
|
+
@#{name} = #{klass}[oid]
|
87
|
+
render 'form#{suffix}'
|
88
|
+
end
|
89
|
+
|
90
|
+
def form#{suffix}
|
67
91
|
end
|
68
92
|
}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
-
}
|
93
|
+
|
94
|
+
code << Scaffolding.compile_scaffold_action(compiler, self, 'form', suffix) do |template|
|
95
|
+
template.gsub!(/%name%/, "#{name}")
|
96
|
+
template.gsub!(/%name_plural%/, "#{name.plural}")
|
90
97
|
end
|
91
98
|
|
92
99
|
code << %{
|
93
100
|
# TODO: add pager support here!
|
94
101
|
|
95
|
-
def
|
96
|
-
@#{list_name} = #{klass}.all
|
102
|
+
def #{name.plural}
|
103
|
+
@#{list_name} = #{klass}.all
|
97
104
|
}
|
98
|
-
|
105
|
+
=begin
|
106
|
+
code << Scaffolding.compile_scaffold_action(compiler, self, 'list', suffix) do |template|
|
107
|
+
template.gsub!(/%name%/, "#{name}")
|
108
|
+
template.gsub!(/%list_name%/, "#{list_name}")
|
109
|
+
end
|
110
|
+
=end
|
111
|
+
unless compiler.template_for_action(list_name, self.template_root)
|
112
|
+
source = File.read(File.join(Nitro.proto_path, 'public', 'scaffold', 'list.xhtml'))
|
113
|
+
template = Compiler.new.transform_template(source)
|
114
|
+
|
115
|
+
template.gsub!(/%name%/, "#{name}")
|
116
|
+
template.gsub!(/%list_name%/, "#{list_name}")
|
117
|
+
|
99
118
|
code << %{
|
100
|
-
|
101
|
-
for item in @#{list_name}
|
102
|
-
o.li(item.to_s)
|
103
|
-
end
|
104
|
-
}
|
119
|
+
#{template}
|
105
120
|
}
|
106
121
|
end
|
122
|
+
|
107
123
|
code << %{
|
108
124
|
end
|
109
125
|
|
110
|
-
def
|
111
|
-
|
112
|
-
@#{name} = #{klass}[@#{oid}]
|
126
|
+
def #{name}(oid)
|
127
|
+
@#{name} = #{klass}[oid]
|
113
128
|
end
|
114
|
-
action :view#{suffix}, :route => \%r\{#{@base}/view#{suffix}/(.*)\}, 'oid' => 1
|
115
129
|
|
116
130
|
def save#{suffix}
|
117
|
-
if oid = request['oid']
|
118
|
-
|
131
|
+
if oid = request['#{oid}']
|
132
|
+
obj = request.fill(#{klass}[oid])
|
119
133
|
else
|
120
|
-
|
134
|
+
obj = request.fill(#{klass}.new)
|
121
135
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
136
|
+
unless obj.valid?
|
137
|
+
session[:ERRORS] = obj.errors
|
138
|
+
redirect_to_referer
|
139
|
+
end
|
140
|
+
obj.save
|
141
|
+
redirect '#{name.plural}'
|
126
142
|
end
|
127
143
|
|
128
|
-
def
|
129
|
-
#{klass}.delete(
|
130
|
-
|
144
|
+
def delete#{suffix}(oid)
|
145
|
+
#{klass}.delete(oid)
|
146
|
+
redirect_to_referer
|
131
147
|
end
|
132
|
-
alias_method :delete#{suffix}, :del#{suffix}
|
133
148
|
}
|
134
149
|
|
150
|
+
if options[:index]
|
151
|
+
code << %{
|
152
|
+
alias_action :index, :#{name.plural}
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
# puts '--', klass, '..', code
|
157
|
+
|
135
158
|
class_eval(code)
|
136
159
|
end
|
137
160
|
|