nitro 0.20.0 → 0.21.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 (115) hide show
  1. data/CHANGELOG +752 -543
  2. data/INSTALL +38 -38
  3. data/README +264 -225
  4. data/Rakefile +48 -49
  5. data/bin/nitro +3 -3
  6. data/bin/nitrogen +6 -6
  7. data/doc/AUTHORS +10 -10
  8. data/doc/CHANGELOG.1 +1939 -1939
  9. data/doc/CHANGELOG.2 +954 -954
  10. data/doc/LICENSE +3 -3
  11. data/doc/MIGRATION +28 -0
  12. data/doc/RELEASES +814 -643
  13. data/doc/config.txt +5 -5
  14. data/install.rb +7 -17
  15. data/lib/nitro.rb +38 -9
  16. data/lib/nitro/adapter/cgi.rb +311 -312
  17. data/lib/nitro/adapter/fastcgi.rb +18 -25
  18. data/lib/nitro/adapter/webrick.rb +128 -137
  19. data/lib/nitro/adapter/wee.rb +51 -0
  20. data/lib/nitro/caching.rb +20 -20
  21. data/lib/nitro/caching/actions.rb +43 -43
  22. data/lib/nitro/caching/fragments.rb +46 -46
  23. data/lib/nitro/caching/invalidation.rb +11 -11
  24. data/lib/nitro/caching/output.rb +65 -65
  25. data/lib/nitro/caching/stores.rb +67 -67
  26. data/lib/nitro/compiler.rb +262 -0
  27. data/lib/nitro/compiler/elements.rb +0 -0
  28. data/lib/nitro/compiler/errors.rb +65 -0
  29. data/lib/nitro/compiler/localization.rb +25 -0
  30. data/lib/nitro/compiler/markup.rb +19 -0
  31. data/lib/nitro/compiler/shaders.rb +206 -0
  32. data/lib/nitro/compiler/squeeze.rb +20 -0
  33. data/lib/nitro/compiler/xslt.rb +61 -0
  34. data/lib/nitro/context.rb +87 -88
  35. data/lib/nitro/controller.rb +151 -158
  36. data/lib/nitro/cookie.rb +34 -34
  37. data/lib/nitro/dispatcher.rb +195 -186
  38. data/lib/nitro/element.rb +132 -126
  39. data/lib/nitro/element/java_script.rb +6 -6
  40. data/lib/nitro/flash.rb +66 -66
  41. data/lib/nitro/mail.rb +192 -192
  42. data/lib/nitro/mixin/buffer.rb +66 -0
  43. data/lib/nitro/mixin/debug.rb +16 -16
  44. data/lib/nitro/mixin/form.rb +88 -0
  45. data/lib/nitro/mixin/helper.rb +2 -2
  46. data/lib/nitro/mixin/javascript.rb +108 -108
  47. data/lib/nitro/mixin/markup.rb +144 -0
  48. data/lib/nitro/mixin/pager.rb +202 -202
  49. data/lib/nitro/mixin/rss.rb +67 -0
  50. data/lib/nitro/mixin/table.rb +63 -0
  51. data/lib/nitro/mixin/xhtml.rb +75 -0
  52. data/lib/nitro/mixin/xml.rb +124 -0
  53. data/lib/nitro/render.rb +183 -359
  54. data/lib/nitro/request.rb +140 -140
  55. data/lib/nitro/response.rb +27 -27
  56. data/lib/nitro/routing.rb +21 -21
  57. data/lib/nitro/scaffold.rb +124 -118
  58. data/lib/nitro/server.rb +117 -80
  59. data/lib/nitro/server/runner.rb +341 -0
  60. data/lib/nitro/service.rb +12 -12
  61. data/lib/nitro/service/xmlrpc.rb +22 -22
  62. data/lib/nitro/session.rb +122 -120
  63. data/lib/nitro/session/drb.rb +9 -9
  64. data/lib/nitro/session/drbserver.rb +34 -34
  65. data/lib/nitro/template.rb +171 -155
  66. data/lib/nitro/testing/assertions.rb +90 -90
  67. data/lib/nitro/testing/context.rb +16 -16
  68. data/lib/nitro/testing/testcase.rb +34 -34
  69. data/proto/conf/lhttpd.conf +9 -9
  70. data/proto/public/error.xhtml +75 -75
  71. data/proto/public/index.xhtml +18 -18
  72. data/proto/public/js/behaviour.js +65 -65
  73. data/proto/public/js/controls.js +1 -1
  74. data/proto/public/js/prototype.js +3 -3
  75. data/proto/public/settings.xhtml +61 -61
  76. data/proto/run.rb +1 -5
  77. data/test/nitro/adapter/raw_post1.bin +0 -0
  78. data/test/nitro/adapter/tc_cgi.rb +57 -57
  79. data/test/nitro/adapter/tc_webrick.rb +4 -4
  80. data/test/nitro/mixin/tc_pager.rb +25 -25
  81. data/test/nitro/mixin/tc_rss.rb +24 -0
  82. data/test/nitro/mixin/tc_table.rb +31 -0
  83. data/test/nitro/mixin/tc_xhtml.rb +13 -0
  84. data/test/nitro/tc_caching.rb +10 -10
  85. data/test/nitro/tc_context.rb +8 -8
  86. data/test/nitro/tc_controller.rb +48 -48
  87. data/test/nitro/tc_cookie.rb +6 -6
  88. data/test/nitro/tc_dispatcher.rb +64 -64
  89. data/test/nitro/tc_element.rb +27 -27
  90. data/test/nitro/tc_flash.rb +31 -31
  91. data/test/nitro/tc_mail.rb +63 -63
  92. data/test/nitro/tc_server.rb +26 -26
  93. data/test/nitro/tc_session.rb +9 -9
  94. data/test/nitro/tc_template.rb +19 -19
  95. data/test/public/blog/list.xhtml +1 -1
  96. metadata +31 -37
  97. data/lib/nitro/buffering.rb +0 -45
  98. data/lib/nitro/builder/form.rb +0 -104
  99. data/lib/nitro/builder/rss.rb +0 -104
  100. data/lib/nitro/builder/table.rb +0 -80
  101. data/lib/nitro/builder/xhtml.rb +0 -132
  102. data/lib/nitro/builder/xml.rb +0 -131
  103. data/lib/nitro/conf.rb +0 -36
  104. data/lib/nitro/environment.rb +0 -21
  105. data/lib/nitro/errors.rb +0 -69
  106. data/lib/nitro/localization.rb +0 -153
  107. data/lib/nitro/markup.rb +0 -147
  108. data/lib/nitro/output.rb +0 -24
  109. data/lib/nitro/runner.rb +0 -348
  110. data/lib/nitro/shaders.rb +0 -206
  111. data/test/nitro/builder/tc_rss.rb +0 -23
  112. data/test/nitro/builder/tc_table.rb +0 -30
  113. data/test/nitro/builder/tc_xhtml.rb +0 -39
  114. data/test/nitro/builder/tc_xml.rb +0 -56
  115. data/test/nitro/tc_localization.rb +0 -49
@@ -6,71 +6,72 @@ require 'nitro/render'
6
6
  require 'nitro/scaffold'
7
7
  require 'nitro/caching'
8
8
  require 'nitro/flash'
9
+ require 'nitro/mixin/markup'
9
10
 
10
11
  module Nitro
11
12
 
12
13
  # Encapsulates metadata that describe an action
13
14
  # parameter.
14
15
  #
15
- # [+default+]
16
- # The default value.
16
+ # [+default+]
17
+ # The default value.
17
18
  #
18
- # [+format+]
19
- # The expected format.
19
+ # [+format+]
20
+ # The expected format.
20
21
  #
21
- # [+required+]
22
- # Is this parameter required?
22
+ # [+required+]
23
+ # Is this parameter required?
23
24
 
24
25
  unless const_defined? :ActionParam
25
- ActionParam = Struct.new(:default, :format, :required)
26
+ ActionParam = Struct.new(:default, :format, :required)
26
27
  end
27
28
 
28
29
  # Encapsulates metadata that describe an action.
29
30
 
30
31
  class ActionMeta < Hash
31
32
 
32
- # The arguments of the given method.
33
-
34
- attr_accessor :params
35
-
36
- # Initialize the metadata.
37
-
38
- def initialize(options)
39
- @params = {}
40
- update(options)
41
- end
42
-
43
- # Update the metadata.
44
- #
45
- # [+options+]
46
- # A hash containing the metadata. Options with Symbol
47
- # keys are considered metadata, options with
48
- # String keys are the named parameters for the action.
49
-
50
- def update(options)
51
- options.each do |k, v|
52
- case k
53
- when String
54
- # A key of type String denotes a parameter.
55
- case v
56
- when Regexp
57
- @params[k] = ActionParam.new(nil, v, nil)
58
- when ActionParam
59
- @params[k] = v
60
- else
61
- if v == :required
62
- @params[k] = ActionParam.new(nil, nil, true)
63
- else
64
- @params[k] = ActionParam.new(v, nil, nil)
65
- end
66
- end
67
- when Symbol
68
- self[k] = v
69
- else
70
- raise TypeError.new('The keys must be either Symbols or Strings.')
71
- end
72
- end
73
- end
33
+ # The arguments of the given method.
34
+
35
+ attr_accessor :params
36
+
37
+ # Initialize the metadata.
38
+
39
+ def initialize(options)
40
+ @params = {}
41
+ update(options)
42
+ end
43
+
44
+ # Update the metadata.
45
+ #
46
+ # [+options+]
47
+ # A hash containing the metadata. Options with Symbol
48
+ # keys are considered metadata, options with
49
+ # String keys are the named parameters for the action.
50
+
51
+ def update(options)
52
+ options.each do |k, v|
53
+ case k
54
+ when String
55
+ # A key of type String denotes a parameter.
56
+ case v
57
+ when Regexp
58
+ @params[k] = ActionParam.new(nil, v, nil)
59
+ when ActionParam
60
+ @params[k] = v
61
+ else
62
+ if v == :required
63
+ @params[k] = ActionParam.new(nil, nil, true)
64
+ else
65
+ @params[k] = ActionParam.new(v, nil, nil)
66
+ end
67
+ end
68
+ when Symbol
69
+ self[k] = v
70
+ else
71
+ raise TypeError.new('The keys must be either Symbols or Strings.')
72
+ end
73
+ end
74
+ end
74
75
 
75
76
  end
76
77
 
@@ -79,103 +80,94 @@ end
79
80
  # interface.
80
81
 
81
82
  module Publishable
82
- def self.append_features(base)
83
-
84
- base.module_eval do
85
- include Render
86
- include Glue::Aspects
87
- include Flashing
88
- include Glue::Helpers
89
-
90
- class << self
91
- attr_accessor :template_root
92
- end
93
-
94
- @template_root = 'public'
95
- end
96
-
97
- # Define metadata for an action. This is a helper
98
- # macro.
99
-
100
- base.module_eval do
101
- def self.action(name, options)
102
- if meta = action_metadata[name]
103
- meta.update(options)
104
- else
105
- action_metadata[name] = ActionMeta.new(options)
106
- end
107
- end
108
- end
109
-
110
- # Aliases an action
111
- #--
112
- # gmosx, FIXME: better implementation needed.
113
- #++
114
-
115
- base.module_eval do
116
- def self.alias_action(new, old)
117
- alias_method new, old
118
- md = action_metadata[old] || ActionMetadata.new
119
- md[:view] = old
120
- action_metadata[new] = md
121
- end
122
- end
123
-
124
- # Return the 'action' methods for this Controller.
125
- # Some dangerous methods from ancestors are removed.
126
- # All private methods are ignored.
127
-
128
- base.module_eval do
129
- def self.action_methods
130
- classes = self.ancestors.reject do |a|
131
- [Object, Kernel, Render, Controller, Caching].include?(a)
132
- end
133
-
134
- classes.delete(PP::ObjectMixin) if defined?(PP::ObjectMixin)
135
-
136
- methods = classes.inject([]) do |action_methods, klass|
137
- action_methods + klass.public_instance_methods(false)
138
- end
139
-
140
- # gmosx: add the default action (leave this?)
141
- # methods << 'index'
142
-
143
- return methods
144
- end
145
- end
146
-
147
- # A hash containing metadata for the action
148
- # methods.
149
-
150
- base.module_eval do
151
- def self.action_metadata
152
- # FIXME: improve this.
153
- @action_metadata ||= {}
154
- @action_metadata
155
- end
156
- end
157
-
158
- base.module_eval do
159
- def initialize(context, base = nil)
160
- super
161
- self.class.template_root ||= 'public'
162
- end
163
- end
164
-
165
- # Use the method_missing hook to compile the actions
166
- # for this controller.
167
-
168
- base.module_eval do
169
- def method_missing(action, *args)
170
- if Rendering.compile_action(self.class, action)
171
- send(action, *args)
172
- else
173
- super
174
- end
175
- end
176
- end
177
-
178
- end
83
+ def self.append_features(base)
84
+
85
+ base.module_eval do
86
+ include Render
87
+ include Glue::Aspects
88
+ include Flashing
89
+ include Glue::Helpers
90
+
91
+ class << self
92
+ attr_accessor :template_root
93
+ end
94
+ end
95
+
96
+ # Define metadata for an action. This is a helper
97
+ # macro.
98
+
99
+ base.module_eval do
100
+ def self.action(name, options)
101
+ if meta = action_metadata[name]
102
+ meta.update(options)
103
+ else
104
+ action_metadata[name] = ActionMeta.new(options)
105
+ end
106
+ end
107
+ end
108
+
109
+ # Aliases an action
110
+ #--
111
+ # gmosx, FIXME: better implementation needed.
112
+ #++
113
+
114
+ base.module_eval do
115
+ def self.alias_action(new, old)
116
+ alias_method new, old
117
+ md = action_metadata[old] || ActionMetadata.new
118
+ md[:view] = old
119
+ action_metadata[new] = md
120
+ end
121
+ end
122
+
123
+ # Return the 'action' methods for this Controller.
124
+ # Some dangerous methods from ancestors are removed.
125
+ # All private methods are ignored.
126
+
127
+ base.module_eval do
128
+ def self.action_methods
129
+ classes = self.ancestors.reject do |a|
130
+ [Object, Kernel, Render, Controller, Caching].include?(a)
131
+ end
132
+
133
+ classes.delete(PP::ObjectMixin) if defined?(PP::ObjectMixin)
134
+
135
+ methods = classes.inject([]) do |action_methods, klass|
136
+ action_methods + klass.public_instance_methods(false)
137
+ end
138
+
139
+ # gmosx: add the default action (leave this?)
140
+ # methods << 'index'
141
+
142
+ return methods
143
+ end
144
+ end
145
+
146
+ # A hash containing metadata for the action
147
+ # methods.
148
+
149
+ base.module_eval do
150
+ def self.action_metadata
151
+ # FIXME: improve this.
152
+ @action_metadata ||= {}
153
+ @action_metadata
154
+ end
155
+ end
156
+
157
+ # Use the method_missing hook to compile the actions
158
+ # for this controller.
159
+
160
+ base.module_eval do
161
+ def method_missing(action, *args)
162
+ if Compiler.new.compile(self.class, action)
163
+ send(action, *args)
164
+ else
165
+ super
166
+ end
167
+ end
168
+ end
169
+
170
+ end
179
171
  end
180
172
 
181
173
  # The Controller part in the MVC paradigm. The controller's
@@ -183,18 +175,19 @@ end
183
175
  # contains the Publishable mixin and additional helper mixins.
184
176
 
185
177
  class Controller
186
- include Publishable
187
- include Scaffolding
188
- include Caching
189
-
190
- # The default action.
191
- =begin
192
- def index
193
- print %{
194
- This is the placeholder action is provided as a default for #{self.class.name}.<br />
195
- You probably want to <b>implement your custom action</b> here.
196
- }
197
- end
178
+ include Publishable
179
+ include Scaffolding
180
+ include Caching
181
+ include Markup
182
+
183
+ # The default action.
184
+ =begin
185
+ def index
186
+ print %{
187
+ This is the placeholder action is provided as a default for #{self.class.name}.<br />
188
+ You probably want to <b>implement your custom action</b> here.
189
+ }
190
+ end
198
191
  =end
199
192
  end
200
193
 
data/lib/nitro/cookie.rb CHANGED
@@ -3,40 +3,40 @@ module Nitro
3
3
  # Encapsulates a HTTP Cookie.
4
4
 
5
5
  class Cookie
6
- attr_reader :name
7
- attr_accessor :value, :version
8
- attr_accessor :domain, :path, :secure
9
- attr_accessor :comment, :max_age
10
-
11
- def initialize(name, value)
12
- @name = name
13
- @value = value
14
- @version = 0 # Netscape Cookie
15
- @path = '/' # gmosx: KEEP this!
16
- @domain = @secure = @comment = @max_age =
17
- @expires = @comment_url = @discard = @port = nil
18
- end
19
-
20
- def expires=(t)
21
- @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
22
- end
23
-
24
- def expires
25
- @expires && Time.parse(@expires)
26
- end
27
-
28
- def to_s
29
- ret = ""
30
- ret << @name << "=" << @value
31
- ret << "; " << "Version=" << @version.to_s if @version > 0
32
- ret << "; " << "Domain=" << @domain if @domain
33
- ret << "; " << "Expires=" << @expires if @expires
34
- ret << "; " << "Max-Age=" << @max_age.to_s if @max_age
35
- ret << "; " << "Comment=" << @comment if @comment
36
- ret << "; " << "Path=" << @path if @path
37
- ret << "; " << "Secure" if @secure
38
- ret
39
- end
6
+ attr_reader :name
7
+ attr_accessor :value, :version
8
+ attr_accessor :domain, :path, :secure
9
+ attr_accessor :comment, :max_age
10
+
11
+ def initialize(name, value)
12
+ @name = name
13
+ @value = value
14
+ @version = 0 # Netscape Cookie
15
+ @path = '/' # gmosx: KEEP this!
16
+ @domain = @secure = @comment = @max_age =
17
+ @expires = @comment_url = @discard = @port = nil
18
+ end
19
+
20
+ def expires=(t)
21
+ @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
22
+ end
23
+
24
+ def expires
25
+ @expires && Time.parse(@expires)
26
+ end
27
+
28
+ def to_s
29
+ ret = ""
30
+ ret << @name << "=" << @value
31
+ ret << "; " << "Version=" << @version.to_s if @version > 0
32
+ ret << "; " << "Domain=" << @domain if @domain
33
+ ret << "; " << "Expires=" << @expires if @expires
34
+ ret << "; " << "Max-Age=" << @max_age.to_s if @max_age
35
+ ret << "; " << "Comment=" << @comment if @comment
36
+ ret << "; " << "Path=" << @path if @path
37
+ ret << "; " << "Secure" if @secure
38
+ ret
39
+ end
40
40
 
41
41
  end
42
42
 
@@ -10,192 +10,201 @@ module Nitro
10
10
 
11
11
  class Dispatcher
12
12
 
13
- include Router
14
-
15
- unless const_defined?('ROOT')
16
- ROOT = '/'
17
- end
18
-
19
- # The public root directory. The files in this
20
- # directory are published by the web server.
21
-
22
- attr_accessor :public_root
23
-
24
- # The template root directory. By default this
25
- # points to the public root directory to allow
26
- # for PHP/JSP/ASP style programming. But you should
27
- # probably change this to another directory for
28
- # extra security.
29
-
30
- attr_accessor :template_root
31
-
32
- # The controllers map.
33
-
34
- attr_accessor :controllers
35
-
36
- # Create a new Dispatcher.
37
- #
38
- # Input:
39
- #
40
- # [+controllers+]
41
- # Either a hash of controller mappings or a single
42
- # controller that gets mapped to :root.
43
-
44
- def initialize(controllers = nil)
45
- @public_root = 'public'
46
- @template_root = @public_root
47
-
48
- if controllers and controllers.is_a?(Class) and controllers.ancestors.include?(Controller)
49
- controllers = { '/' => controllers }
50
- else
51
- controllers ||= { '/' => SimpleController }
52
- end
53
-
54
- mount(controllers)
55
- end
56
-
57
- # A published object is exposed through a REST interface.
58
- # Only the public non standard methods of the object are
59
- # accessible. Published objects implement the Controller
60
- # part of MVC.
61
- #
62
- # Process the given hash and mount the
63
- # defined classes (controllers).
64
- #
65
- # Input:
66
- #
67
- # [+controllers+]
68
- # A hash representing the mapping of
69
- # mount points to controllers.
70
- #
71
- # === Examples
72
- #
73
- # disp.mount(
74
- # '/' => MainController, # mounts /
75
- # '/users' => UsersController # mounts /users
76
- # )
77
- # disp.publish '/' => MainController
78
-
79
- def add_controller(controllers)
80
- for c in controllers.values
81
- unless (c.ancestors.include?(Controller) or c.ancestors.include?(Publishable))
82
- c.send :include, Publishable
83
- end
84
-
85
- auto_mixin(c)
86
- end
87
-
88
- (@controllers ||= {}).update(controllers)
89
-
90
- update_routes()
91
- end
92
- alias_method :mount, :add_controller
93
- alias_method :publish, :add_controller
94
-
95
- # Call this method to automatically include helpers in the
96
- # Controllers. For each Controller 'XxxController' the
97
- # default helper 'Helper' and the auto mixin
98
- # 'XxxControllerMixin' (if it exists) are included.
99
-
100
- def auto_mixin(c)
101
- c.helper(Helper)
102
-
103
- begin
104
- if helper = Module.by_name("#{c}Mixin")
105
- c.helper(helper)
106
- end
107
- rescue NameError
108
- # The auto helper is not defined.
109
- end
110
- end
111
-
112
- # Update the routes. Typically called after a new
113
- # Controller is mounted.
114
-
115
- def update_routes
116
- @routes = []
117
- @controllers.each do |base, c|
118
- base = '' if base == '/'
119
- c.action_metadata.each do |action, meta|
120
- if route = meta[:route]
121
- @routes << [route, "#{base}/#{action}", *meta.params.keys]
122
- end
123
- end
124
- end
125
- end
126
-
127
- # Processes the path and dispatches to the corresponding
128
- # controller/action pair.
129
- # The base returned contains a trailing '/'.
130
- #
131
- # [+path+]
132
- # The path to dispatch.
133
- #
134
- # [:context]
135
- # The dispatching context.
136
- #
137
- #--
138
- # FIXME: this is a critical method that should be optimized
139
- # watch out for excessive String creation.
140
- #++
141
-
142
- def dispatch(path, context = nil)
143
- path = route(path, context)
144
-
145
- parts = path.split('/')
146
- parts.shift
147
-
148
- case parts.size
149
- when 0
150
- # / -> root.index
151
- base = '/'
152
- klass = controller_class_for(base)
153
- action = 'index'
154
-
155
- when 1
156
- base = "/#{parts[0]}"
157
- if klass = controller_class_for(base)
158
- # controller/ -> controller.index
159
- action = 'index'
160
- else
161
- # action/ -> root.action
162
- base = '/'
163
- klass = controller_class_for(base)
164
- action = parts[0]
165
- end
166
-
167
- else
168
- base = "/#{parts[0]}"
169
- if klass = controller_class_for(base)
170
- # controller/action -> controller.action
171
- parts.shift
172
- action = parts.join('__')
173
- else
174
- # action/ -> root.action
175
- base = '/'
176
- klass = controller_class_for(base)
177
- action = parts.join('__')
178
- end
179
- end
180
-
181
- return klass, "#{action}_action", base
182
- end
183
- alias_method :split_path, :dispatch
184
-
185
- # Get the controller for the given key.
186
- # Also handles reloading of controllers.
187
-
188
- def controller_class_for(key)
189
- klass = @controllers[key]
190
-
191
- if klass and (:full == Rendering.reload)
192
- klass.instance_methods.grep(/(action$)|(template$)/).each do |m|
193
- klass.send(:remove_method, m) rescue nil
194
- end
195
- end
196
-
197
- return klass
198
- end
13
+ include Router
14
+
15
+ # The map.
16
+
17
+ setting :map, :default => { '/' => SimpleController }, :doc => 'The controller map'
18
+
19
+ unless const_defined? :ROOT
20
+ ROOT = '/'
21
+ end
22
+
23
+ # The public root directory. The files in this
24
+ # directory are published by the web server.
25
+
26
+ attr_accessor :public_root
27
+
28
+ # The template root directory. By default this points to the
29
+ # public root directory to allow for PHP/JSP/ASP style
30
+ # programming. But you should probably change this to
31
+ # another directory for extra security.
32
+
33
+ attr_accessor :template_root
34
+
35
+ # The controllers map.
36
+
37
+ attr_accessor :controllers
38
+
39
+ # Create a new Dispatcher.
40
+ #
41
+ # Input:
42
+ #
43
+ # [+controllers+]
44
+ # Either a hash of controller mappings or a single
45
+ # controller that gets mapped to :root.
46
+
47
+ def initialize(controllers = nil)
48
+ @public_root = 'public'
49
+ @template_root = @public_root
50
+
51
+ if controllers and controllers.is_a?(Class) and controllers.ancestors.include?(Controller)
52
+ controllers = { '/' => controllers }
53
+ else
54
+ controllers ||= { '/' => SimpleController }
55
+ end
56
+
57
+ mount(controllers)
58
+ end
59
+
60
+ # A published object is exposed through a REST interface.
61
+ # Only the public non standard methods of the object are
62
+ # accessible. Published objects implement the Controller
63
+ # part of MVC.
64
+ #
65
+ # Process the given hash and mount the
66
+ # defined classes (controllers).
67
+ #
68
+ # Input:
69
+ #
70
+ # [+controllers+]
71
+ # A hash representing the mapping of
72
+ # mount points to controllers.
73
+ #
74
+ # === Examples
75
+ #
76
+ # disp.mount(
77
+ # '/' => MainController, # mounts /
78
+ # '/users' => UsersController # mounts /users
79
+ # )
80
+ # disp.publish '/' => MainController
81
+
82
+ def add_controller(controllers)
83
+ for path, c in controllers
84
+ unless (c.ancestors.include?(Controller) or c.ancestors.include?(Publishable))
85
+ c.send :include, Publishable
86
+ end
87
+
88
+ auto_mixin(c)
89
+
90
+ # Try to setup a template_root if none is defined:
91
+
92
+ unless c.template_root
93
+ c.template_root = "#{Template.root}#{path}".gsub(/\/$/, '')
94
+ end
95
+ end
96
+
97
+ (@controllers ||= {}).update(controllers)
98
+
99
+ update_routes()
100
+ end
101
+ alias_method :mount, :add_controller
102
+ alias_method :publish, :add_controller
103
+ alias_method :map=, :add_controller
104
+
105
+ # Call this method to automatically include helpers in the
106
+ # Controllers. For each Controller 'XxxController' the
107
+ # default helper 'Helper' and the auto mixin
108
+ # 'XxxControllerMixin' (if it exists) are included.
109
+
110
+ def auto_mixin(c)
111
+ c.helper(Helper)
112
+
113
+ begin
114
+ if helper = Module.by_name("#{c}Mixin")
115
+ c.helper(helper)
116
+ end
117
+ rescue NameError
118
+ # The auto helper is not defined.
119
+ end
120
+ end
121
+
122
+ # Update the routes. Typically called after a new
123
+ # Controller is mounted.
124
+
125
+ def update_routes
126
+ @routes = []
127
+ @controllers.each do |base, c|
128
+ base = '' if base == '/'
129
+ c.action_metadata.each do |action, meta|
130
+ if route = meta[:route]
131
+ @routes << [route, "#{base}/#{action}", *meta.params.keys]
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ # Processes the path and dispatches to the corresponding
138
+ # controller/action pair.
139
+ # The base returned contains a trailing '/'.
140
+ #
141
+ # [+path+]
142
+ # The path to dispatch.
143
+ #
144
+ # [:context]
145
+ # The dispatching context.
146
+ #--
147
+ # FIXME: this is a critical method that should be optimized
148
+ # watch out for excessive String creation.
149
+ #++
150
+
151
+ def dispatch(path, context = nil)
152
+ path = route(path, context)
153
+
154
+ parts = path.split('/')
155
+ parts.shift
156
+
157
+ case parts.size
158
+ when 0
159
+ # / -> root.index
160
+ base = '/'
161
+ klass = controller_class_for(base)
162
+ action = 'index'
163
+
164
+ when 1
165
+ base = "/#{parts[0]}"
166
+ if klass = controller_class_for(base)
167
+ # controller/ -> controller.index
168
+ action = 'index'
169
+ else
170
+ # action/ -> root.action
171
+ base = '/'
172
+ klass = controller_class_for(base)
173
+ action = parts[0]
174
+ end
175
+
176
+ else
177
+ base = "/#{parts[0]}"
178
+ if klass = controller_class_for(base)
179
+ # controller/action -> controller.action
180
+ parts.shift
181
+ action = parts.join('__')
182
+ else
183
+ # action/ -> root.action
184
+ base = '/'
185
+ klass = controller_class_for(base)
186
+ action = parts.join('__')
187
+ end
188
+ end
189
+
190
+ return klass, "#{action}_action", base
191
+ end
192
+ alias_method :split_path, :dispatch
193
+
194
+ # Get the controller for the given key.
195
+ # Also handles reloading of controllers.
196
+
197
+ def controller_class_for(key)
198
+ klass = @controllers[key]
199
+
200
+ if klass and Compiler.reload
201
+ klass.instance_methods.grep(/(action$)|(template$)/).each do |m|
202
+ klass.send(:remove_method, m) rescue nil
203
+ end
204
+ end
205
+
206
+ return klass
207
+ end
199
208
 
200
209
  end
201
210