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.
Files changed (54) hide show
  1. data/CHANGELOG +109 -1250
  2. data/INSTALL +3 -2
  3. data/README +5 -4
  4. data/Rakefile +1 -1
  5. data/bin/nitrogen +1 -1
  6. data/doc/AUTHORS +20 -6
  7. data/doc/CHANGELOG.3 +1314 -0
  8. data/doc/RELEASES +90 -0
  9. data/lib/nitro.rb +6 -17
  10. data/lib/nitro/adapter/cgi.rb +11 -0
  11. data/lib/nitro/adapter/webrick.rb +7 -1
  12. data/lib/nitro/caching/stores.rb +4 -6
  13. data/lib/nitro/compiler.rb +1 -1
  14. data/lib/nitro/compiler/errors.rb +1 -1
  15. data/lib/nitro/context.rb +11 -4
  16. data/lib/nitro/controller.rb +0 -3
  17. data/lib/nitro/cookie.rb +4 -3
  18. data/lib/nitro/dispatcher.rb +7 -1
  19. data/lib/nitro/dispatcher/nice.rb +6 -1
  20. data/lib/nitro/element.rb +2 -2
  21. data/lib/nitro/mixin/benchmark.rb +16 -0
  22. data/lib/nitro/mixin/form.rb +171 -55
  23. data/lib/nitro/mixin/rss.rb +1 -1
  24. data/lib/nitro/mixin/xhtml.rb +91 -4
  25. data/lib/nitro/render.rb +25 -6
  26. data/lib/nitro/request.rb +13 -3
  27. data/lib/nitro/scaffold.rb +91 -68
  28. data/lib/nitro/scaffold/relations.rb +54 -0
  29. data/lib/nitro/server.rb +8 -5
  30. data/lib/nitro/server/runner.rb +4 -3
  31. data/lib/nitro/service.rb +3 -1
  32. data/lib/nitro/service/xmlrpc.rb +1 -1
  33. data/lib/nitro/session.rb +69 -51
  34. data/lib/nitro/session/drb.rb +5 -7
  35. data/lib/nitro/session/drbserver.rb +4 -6
  36. data/lib/nitro/session/memory.rb +4 -6
  37. data/lib/part/admin.rb +6 -0
  38. data/lib/part/admin/controller.rb +24 -0
  39. data/lib/part/admin/skin.rb +21 -0
  40. data/lib/part/admin/template/index.xhtml +9 -0
  41. data/proto/public/js/cookies.js +122 -0
  42. data/proto/public/scaffold/edit.xhtml +4 -0
  43. data/proto/public/scaffold/form.xhtml +7 -0
  44. data/proto/public/scaffold/list.xhtml +15 -0
  45. data/proto/public/scaffold/new.xhtml +4 -0
  46. data/proto/public/scaffold/view.xhtml +0 -0
  47. data/proto/script/benchmark +19 -0
  48. data/test/nitro/adapter/tc_cgi.rb +32 -2
  49. data/test/nitro/mixin/tc_xhtml.rb +6 -0
  50. data/test/nitro/tc_dispatcher.rb +0 -17
  51. data/test/nitro/tc_render.rb +58 -0
  52. data/test/nitro/tc_server.rb +2 -0
  53. data/test/nitro/tc_session.rb +16 -0
  54. metadata +104 -85
@@ -1,7 +1,7 @@
1
1
  require 'cgi'
2
2
  require 'rss/0.9'
3
3
 
4
- require 'facet/string/first_char'
4
+ require 'nano/string/first_char'
5
5
 
6
6
  require 'nitro/mixin/markup'
7
7
 
@@ -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
- # [+selected+]
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
- # o.options(:labels => labels, :selected => 1)
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 = (0...labels.size).to_a
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>&nbsp;
92
+ <select id="#{name}.month" name="#{name}.month">
93
+ #{options(:labels => Date::MONTHNAMES, :values => (1..12).to_a, :selected => (date.month+1))}
94
+ </select>&nbsp;
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>&nbsp;
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) + '&nbsp;at&nbsp;' + 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
 
@@ -1,11 +1,9 @@
1
1
  require 'singleton'
2
2
  require 'sync'
3
3
 
4
- require 'facet/string/blank%3F'
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
- url = "#{@context.host_url}/#{url.gsub(/^\//, '')}" unless url =~ /http/
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"
@@ -30,13 +30,14 @@ module Request
30
30
  # The request protocol.
31
31
 
32
32
  def protocol
33
- 443 == port ? 'https://' : 'http://'
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
- 443 == port
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
- "http://#{host}"
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>
@@ -1,14 +1,12 @@
1
- require 'facet/string/singular'
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.to_s.demodulize.underscore.downcase
34
- list_name = options[:list_name] || name.plural
35
- options[:nosuffix] ? suffix = nil : suffix = "_#{name}"
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.module_eval %{
40
- def to_href
41
- "view#{suffix}?oid=\#\{@oid\}"
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>&nbsp;(<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 '/form#{suffix}'
82
+ render 'form#{suffix}'
62
83
  end
63
84
 
64
- def edit#{suffix}
65
- @#{name} = #{klass}[request['oid']]
66
- render '/form#{suffix}'
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
- 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
- }
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 list#{suffix}
96
- @#{list_name} = #{klass}.all('ORDER BY oid')
102
+ def #{name.plural}
103
+ @#{list_name} = #{klass}.all
97
104
  }
98
- unless compiler.template_for_action("list#{suffix}", self.template_root)
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
- o.ul {
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 view#{suffix}
111
- # @#{name} = #{klass}[@context['#{oid}']]
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
- @#{name} = request.fill(#{klass}[oid])
131
+ if oid = request['#{oid}']
132
+ obj = request.fill(#{klass}[oid])
119
133
  else
120
- @#{name} = request.fill(#{klass}.new)
134
+ obj = request.fill(#{klass}.new)
121
135
  end
122
-
123
- @#{name}.save
124
-
125
- redirect 'list#{suffix}'
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 del#{suffix}
129
- #{klass}.delete(@context['#{oid}'])
130
- redirect_referer
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