nitro 0.28.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
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