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.
Files changed (73) hide show
  1. data/CHANGELOG +382 -0
  2. data/ProjectInfo +4 -4
  3. data/README +1 -1
  4. data/doc/AUTHORS +15 -15
  5. data/doc/MIGRATION +13 -0
  6. data/doc/RELEASES +102 -0
  7. data/lib/glue/sweeper.rb +1 -1
  8. data/lib/nitro.rb +38 -9
  9. data/lib/nitro/adapter/acgi.rb +1 -3
  10. data/lib/nitro/adapter/cgi.rb +1 -1
  11. data/lib/nitro/adapter/fastcgi.rb +1 -3
  12. data/lib/nitro/adapter/mongrel.rb +8 -6
  13. data/lib/nitro/adapter/webrick.rb +1 -2
  14. data/lib/nitro/cgi.rb +1 -1
  15. data/lib/nitro/compiler.rb +21 -40
  16. data/lib/nitro/compiler/elements.rb +72 -32
  17. data/lib/nitro/compiler/errors.rb +92 -42
  18. data/lib/nitro/compiler/include.rb +47 -17
  19. data/lib/nitro/compiler/morphing.rb +1 -3
  20. data/lib/nitro/compiler/script.rb +2 -2
  21. data/lib/nitro/context.rb +36 -0
  22. data/lib/nitro/controller.rb +140 -31
  23. data/lib/nitro/dispatcher.rb +27 -28
  24. data/lib/nitro/element.rb +52 -15
  25. data/lib/nitro/flash.rb +44 -0
  26. data/lib/nitro/helper/buffer.rb +0 -2
  27. data/lib/nitro/helper/form.rb +2 -2
  28. data/lib/nitro/helper/form/controls.rb +14 -3
  29. data/lib/nitro/helper/pager.rb +1 -1
  30. data/lib/nitro/helper/table.rb +4 -3
  31. data/lib/nitro/helper/xml.rb +1 -1
  32. data/lib/nitro/part.rb +20 -0
  33. data/lib/nitro/render.rb +44 -5
  34. data/lib/nitro/router.rb +81 -0
  35. data/lib/nitro/scaffolding.rb +24 -23
  36. data/lib/nitro/server.rb +12 -1
  37. data/lib/nitro/server/runner.rb +12 -0
  38. data/lib/nitro/session.rb +3 -12
  39. data/lib/nitro/session/drb.rb +2 -5
  40. data/lib/nitro/session/file.rb +2 -2
  41. data/lib/nitro/session/memcached.rb +14 -0
  42. data/lib/nitro/session/memory.rb +3 -26
  43. data/lib/nitro/session/og.rb +1 -1
  44. data/lib/nitro/test/assertions.rb +1 -1
  45. data/lib/nitro/test/context.rb +8 -2
  46. data/lib/nitro/test/testcase.rb +16 -7
  47. data/proto/public/error.xhtml +58 -21
  48. data/proto/public/js/controls.js +60 -15
  49. data/proto/public/js/dragdrop.js +105 -16
  50. data/proto/public/js/effects.js +19 -12
  51. data/proto/public/js/scriptaculous.js +1 -1
  52. data/proto/public/js/slider.js +2 -2
  53. data/proto/public/js/unittest.js +29 -20
  54. data/proto/public/scaffold/edit.xhtml +1 -1
  55. data/proto/public/scaffold/index.xhtml +2 -2
  56. data/proto/public/scaffold/list.xhtml +2 -2
  57. data/proto/public/scaffold/new.xhtml +1 -1
  58. data/proto/public/scaffold/search.xhtml +1 -1
  59. data/src/part/admin/controller.rb +5 -5
  60. data/src/part/admin/template/index.xhtml +2 -2
  61. data/test/nitro/compiler/tc_compiler.rb +23 -0
  62. data/test/nitro/helper/tc_table.rb +35 -0
  63. data/test/nitro/tc_cgi.rb +1 -1
  64. data/test/nitro/tc_controller.rb +3 -3
  65. data/test/nitro/tc_controller_aspect.rb +2 -0
  66. data/test/nitro/tc_dispatcher.rb +10 -1
  67. data/test/nitro/tc_flash.rb +14 -0
  68. data/test/nitro/tc_router.rb +58 -0
  69. data/test/nitro/tc_session.rb +26 -9
  70. metadata +13 -12
  71. data/lib/nitro/routing.rb +0 -41
  72. data/test/nitro/caching/tc_stores.rb +0 -17
  73. data/test/nitro/tc_table.rb +0 -66
@@ -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
- # Cookie helpers.
123
- #--
124
- # TODO: move elsewhere.
125
- #++
162
+ private
163
+
164
+ # Cookie helpers.
165
+ #--
166
+ # TODO: move elsewhere.
167
+ #++
126
168
 
127
- base.module_eval do
128
- private
129
-
130
- def cookies
131
- @context.cookies
132
- end
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
- def send_cookie(name, value = nil)
135
- @context.add_cookie(name, value)
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
- Aspects.include_advice_modules(self)
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
- compile_scaffolding_code
269
+
270
+ compile_scaffolding_code()
162
271
  end
163
272
 
164
273
  end
@@ -1,6 +1,6 @@
1
1
  require 'nitro/controller'
2
2
  require 'nitro/compiler'
3
- require 'nitro/routing'
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, c in controllers
67
- unless (c.ancestors.include?(Controller) or c.ancestors.include?(Publishable))
68
- c.send :include, Publishable
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
- auto_mixin(c)
71
+ # Automatically mixin controller helpers.
72
72
 
73
- # Try to setup a template_root if none is defined:
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
- # Keep the mount point as an annotation.
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
- c.ann.self.mount_point = path.gsub(/^\//, '')
83
+ klass.mount_at(path)
86
84
 
87
- c.mounted(path) if c.respond_to?(:mounted)
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 auto_mixin(c)
104
- c.helper(Nitro::DefaultHelper)
101
+ def mixin_auto_helpers(klass)
102
+ klass.helper(Nitro::DefaultHelper)
105
103
 
106
104
  begin
107
- if helper = Module.by_name("#{c}Helper")
108
- c.helper(helper)
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
- unless c.ann(m).params.nil?
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
- path = route(path, context)
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 '/'.
@@ -1,10 +1,9 @@
1
1
  require 'facet/string/capitalized'
2
2
  require 'facet/string/camelize'
3
- require 'facet/string/underscore'
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].content
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
- # Renders the actual content of this Element.
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[Element.class_to_name(child.class)] = child
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