ramaze 0.0.6

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 (174) hide show
  1. data/Rakefile +360 -0
  2. data/bin/ramaze +152 -0
  3. data/doc/CHANGELOG +2021 -0
  4. data/doc/COPYING +56 -0
  5. data/doc/COPYING.ja +51 -0
  6. data/doc/README +275 -0
  7. data/doc/TODO +33 -0
  8. data/doc/allison/LICENSE +184 -0
  9. data/doc/allison/README +37 -0
  10. data/doc/allison/allison.css +300 -0
  11. data/doc/allison/allison.gif +0 -0
  12. data/doc/allison/allison.js +307 -0
  13. data/doc/allison/allison.rb +287 -0
  14. data/doc/allison/cache/BODY +588 -0
  15. data/doc/allison/cache/CLASS_INDEX +4 -0
  16. data/doc/allison/cache/CLASS_PAGE +1 -0
  17. data/doc/allison/cache/FILE_INDEX +4 -0
  18. data/doc/allison/cache/FILE_PAGE +1 -0
  19. data/doc/allison/cache/FONTS +1 -0
  20. data/doc/allison/cache/FR_INDEX_BODY +1 -0
  21. data/doc/allison/cache/IMGPATH +1 -0
  22. data/doc/allison/cache/INDEX +1 -0
  23. data/doc/allison/cache/JAVASCRIPT +307 -0
  24. data/doc/allison/cache/METHOD_INDEX +4 -0
  25. data/doc/allison/cache/METHOD_LIST +1 -0
  26. data/doc/allison/cache/SRC_PAGE +1 -0
  27. data/doc/allison/cache/STYLE +322 -0
  28. data/doc/allison/cache/URL +1 -0
  29. data/examples/blog/main.rb +16 -0
  30. data/examples/blog/public/screen.css +106 -0
  31. data/examples/blog/src/controller.rb +50 -0
  32. data/examples/blog/src/element.rb +53 -0
  33. data/examples/blog/src/model.rb +29 -0
  34. data/examples/blog/template/edit.xhtml +6 -0
  35. data/examples/blog/template/index.xhtml +24 -0
  36. data/examples/blog/template/new.xhtml +5 -0
  37. data/examples/blog/template/view.xhtml +15 -0
  38. data/examples/blog/test/tc_entry.rb +18 -0
  39. data/examples/caching.rb +23 -0
  40. data/examples/element.rb +40 -0
  41. data/examples/hello.rb +23 -0
  42. data/examples/simple.rb +60 -0
  43. data/examples/templates/template/external.haml +21 -0
  44. data/examples/templates/template/external.liquid +28 -0
  45. data/examples/templates/template/external.mab +27 -0
  46. data/examples/templates/template/external.rhtml +29 -0
  47. data/examples/templates/template/external.rmze +24 -0
  48. data/examples/templates/template_erubis.rb +50 -0
  49. data/examples/templates/template_haml.rb +48 -0
  50. data/examples/templates/template_liquid.rb +64 -0
  51. data/examples/templates/template_markaby.rb +52 -0
  52. data/examples/templates/template_ramaze.rb +49 -0
  53. data/examples/whywiki/main.rb +56 -0
  54. data/examples/whywiki/template/edit.xhtml +14 -0
  55. data/examples/whywiki/template/show.xhtml +17 -0
  56. data/lib/proto/conf/benchmark.yaml +35 -0
  57. data/lib/proto/conf/debug.yaml +34 -0
  58. data/lib/proto/conf/live.yaml +33 -0
  59. data/lib/proto/conf/silent.yaml +31 -0
  60. data/lib/proto/conf/stage.yaml +33 -0
  61. data/lib/proto/main.rb +18 -0
  62. data/lib/proto/public/404.jpg +0 -0
  63. data/lib/proto/public/css/coderay.css +105 -0
  64. data/lib/proto/public/css/ramaze_error.css +42 -0
  65. data/lib/proto/public/error.xhtml +74 -0
  66. data/lib/proto/public/favicon.ico +0 -0
  67. data/lib/proto/public/js/jquery.js +1923 -0
  68. data/lib/proto/public/ramaze.png +0 -0
  69. data/lib/proto/src/controller/main.rb +7 -0
  70. data/lib/proto/src/element/page.rb +16 -0
  71. data/lib/proto/src/model.rb +5 -0
  72. data/lib/proto/template/index.xhtml +6 -0
  73. data/lib/ramaze.rb +317 -0
  74. data/lib/ramaze/adapter/mongrel.rb +111 -0
  75. data/lib/ramaze/adapter/webrick.rb +161 -0
  76. data/lib/ramaze/cache.rb +11 -0
  77. data/lib/ramaze/cache/memcached.rb +52 -0
  78. data/lib/ramaze/cache/memory.rb +6 -0
  79. data/lib/ramaze/cache/yaml_store.rb +37 -0
  80. data/lib/ramaze/controller.rb +10 -0
  81. data/lib/ramaze/dispatcher.rb +315 -0
  82. data/lib/ramaze/error.rb +11 -0
  83. data/lib/ramaze/gestalt.rb +108 -0
  84. data/lib/ramaze/global.rb +120 -0
  85. data/lib/ramaze/helper.rb +32 -0
  86. data/lib/ramaze/helper/aspect.rb +189 -0
  87. data/lib/ramaze/helper/auth.rb +120 -0
  88. data/lib/ramaze/helper/cache.rb +52 -0
  89. data/lib/ramaze/helper/feed.rb +135 -0
  90. data/lib/ramaze/helper/form.rb +204 -0
  91. data/lib/ramaze/helper/link.rb +80 -0
  92. data/lib/ramaze/helper/redirect.rb +48 -0
  93. data/lib/ramaze/helper/stack.rb +67 -0
  94. data/lib/ramaze/http_status.rb +66 -0
  95. data/lib/ramaze/inform.rb +166 -0
  96. data/lib/ramaze/snippets.rb +5 -0
  97. data/lib/ramaze/snippets/hash/keys_to_sym.rb +19 -0
  98. data/lib/ramaze/snippets/kernel/aquire.rb +22 -0
  99. data/lib/ramaze/snippets/kernel/autoreload.rb +79 -0
  100. data/lib/ramaze/snippets/kernel/caller_lines.rb +58 -0
  101. data/lib/ramaze/snippets/kernel/constant.rb +24 -0
  102. data/lib/ramaze/snippets/kernel/rescue_require.rb +12 -0
  103. data/lib/ramaze/snippets/kernel/self_method.rb +41 -0
  104. data/lib/ramaze/snippets/kernel/silently.rb +13 -0
  105. data/lib/ramaze/snippets/object/traits.rb +60 -0
  106. data/lib/ramaze/snippets/openstruct/temp.rb +10 -0
  107. data/lib/ramaze/snippets/string/DIVIDE.rb +16 -0
  108. data/lib/ramaze/snippets/string/camel_case.rb +14 -0
  109. data/lib/ramaze/snippets/string/snake_case.rb +12 -0
  110. data/lib/ramaze/snippets/symbol/to_proc.rb +14 -0
  111. data/lib/ramaze/snippets/thread/deadQUESTIONMARK.rb +11 -0
  112. data/lib/ramaze/store/default.rb +48 -0
  113. data/lib/ramaze/template.rb +102 -0
  114. data/lib/ramaze/template/amrita2.rb +40 -0
  115. data/lib/ramaze/template/erubis.rb +58 -0
  116. data/lib/ramaze/template/haml.rb +65 -0
  117. data/lib/ramaze/template/haml/actionview_stub.rb +20 -0
  118. data/lib/ramaze/template/liquid.rb +74 -0
  119. data/lib/ramaze/template/markaby.rb +68 -0
  120. data/lib/ramaze/template/ramaze.rb +177 -0
  121. data/lib/ramaze/template/ramaze/element.rb +166 -0
  122. data/lib/ramaze/template/ramaze/morpher.rb +156 -0
  123. data/lib/ramaze/tool/create.rb +70 -0
  124. data/lib/ramaze/tool/tidy.rb +71 -0
  125. data/lib/ramaze/trinity.rb +38 -0
  126. data/lib/ramaze/trinity/request.rb +244 -0
  127. data/lib/ramaze/trinity/response.rb +41 -0
  128. data/lib/ramaze/trinity/session.rb +129 -0
  129. data/lib/ramaze/version.rb +14 -0
  130. data/spec/spec_all.rb +73 -0
  131. data/spec/spec_helper.rb +215 -0
  132. data/spec/tc_adapter_mongrel.rb +24 -0
  133. data/spec/tc_adapter_webrick.rb +22 -0
  134. data/spec/tc_cache.rb +79 -0
  135. data/spec/tc_controller.rb +39 -0
  136. data/spec/tc_element.rb +100 -0
  137. data/spec/tc_error.rb +23 -0
  138. data/spec/tc_gestalt.rb +90 -0
  139. data/spec/tc_global.rb +46 -0
  140. data/spec/tc_helper_aspect.rb +65 -0
  141. data/spec/tc_helper_auth.rb +61 -0
  142. data/spec/tc_helper_cache.rb +81 -0
  143. data/spec/tc_helper_feed.rb +129 -0
  144. data/spec/tc_helper_form.rb +146 -0
  145. data/spec/tc_helper_link.rb +58 -0
  146. data/spec/tc_helper_redirect.rb +51 -0
  147. data/spec/tc_helper_stack.rb +55 -0
  148. data/spec/tc_morpher.rb +90 -0
  149. data/spec/tc_params.rb +84 -0
  150. data/spec/tc_request.rb +111 -0
  151. data/spec/tc_session.rb +56 -0
  152. data/spec/tc_store.rb +25 -0
  153. data/spec/tc_template_amrita2.rb +34 -0
  154. data/spec/tc_template_erubis.rb +41 -0
  155. data/spec/tc_template_haml.rb +44 -0
  156. data/spec/tc_template_liquid.rb +98 -0
  157. data/spec/tc_template_markaby.rb +74 -0
  158. data/spec/tc_template_ramaze.rb +54 -0
  159. data/spec/tc_tidy.rb +14 -0
  160. data/spec/template/amrita2/data.html +6 -0
  161. data/spec/template/amrita2/index.html +1 -0
  162. data/spec/template/amrita2/sum.html +1 -0
  163. data/spec/template/erubis/sum.rhtml +1 -0
  164. data/spec/template/haml/index.haml +5 -0
  165. data/spec/template/haml/with_vars.haml +4 -0
  166. data/spec/template/liquid/index.liquid +1 -0
  167. data/spec/template/liquid/products.liquid +45 -0
  168. data/spec/template/markaby/external.mab +8 -0
  169. data/spec/template/markaby/sum.mab +1 -0
  170. data/spec/template/ramaze/file_only.rmze +1 -0
  171. data/spec/template/ramaze/index.rmze +1 -0
  172. data/spec/template/ramaze/nested.rmze +1 -0
  173. data/spec/template/ramaze/sum.rmze +1 -0
  174. metadata +317 -0
@@ -0,0 +1,161 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ require 'cgi'
5
+ require 'webrick'
6
+ require 'benchmark'
7
+
8
+ require 'ramaze/tool/tidy'
9
+
10
+ # for OSX compatibility
11
+ Socket.do_not_reverse_lookup = true
12
+
13
+ module WEBrick
14
+ class HTTPRequest
15
+ attr_accessor :params
16
+
17
+ # request_uri.path
18
+
19
+ def request_path
20
+ request_uri.path
21
+ end
22
+
23
+ # peeraddr.last
24
+
25
+ def remote_addr
26
+ peeraddr.last
27
+ end
28
+
29
+ # request_uri.path
30
+
31
+ def request_path
32
+ request_uri.path
33
+ end
34
+
35
+ # the headers of the current request.
36
+
37
+ def headers
38
+ return @headers if @headers
39
+ @headers = meta_vars.merge(header)
40
+ @headers.each do |key, value|
41
+ @headers.delete(key)
42
+ @headers[key.upcase] = value
43
+ end
44
+ @headers
45
+ end
46
+
47
+ end
48
+ end
49
+
50
+ module Ramaze::Adapter
51
+ class Webrick
52
+ class << self
53
+
54
+ # The method to start a new webrick-adapter on the specified
55
+ # host/ports, will return the first of the started adapters.
56
+ #
57
+ # sets up the default handler for the request/response cycle.
58
+
59
+ def start host, ports
60
+ handler = lambda do |request, response|
61
+ self.new.process(request, response)
62
+ end
63
+
64
+ ports.map{|port| run_server(host, port, handler) }.first
65
+ end
66
+
67
+ # run the actual server on host/port with the handler given.
68
+ # passes WEBrick::HTTPServlet the default options and starts
69
+ # it up in a new Thread, setting Thread.current[:adapter]
70
+
71
+ def run_server host, port, handler
72
+ options = {
73
+ :Port => port,
74
+ :BindAddress => host,
75
+ :Logger => Informer,
76
+ :AccessLog => [
77
+ [Informer, WEBrick::AccessLog::COMMON_LOG_FORMAT],
78
+ [Informer, WEBrick::AccessLog::REFERER_LOG_FORMAT]
79
+ ]
80
+ }
81
+
82
+ server = WEBrick::HTTPServer.new(options)
83
+ server.mount('/', WEBrick::HTTPServlet::ProcHandler.new(handler))
84
+
85
+ Thread.new do
86
+ Thread.current[:task] = :webrick
87
+ Thread.current[:adapter] = server
88
+ server.start
89
+ end
90
+ end
91
+
92
+ # doesn't do anything, for the time being.
93
+
94
+ def stop
95
+ debug "stopping WEBrick"
96
+ end
97
+ end
98
+
99
+ # process a request and give a response, based on the objects
100
+ # WEBrick gives it.
101
+ #
102
+ # if the Global.inform_tags include :benchmark it will run #bench_process,
103
+ # otherwise simply #respond.
104
+
105
+ def process request, response
106
+ if Global.inform_tags.include?(:benchmark)
107
+ bench_process(request, response)
108
+ else
109
+ respond(response, Dispatcher.handle(request, response))
110
+ end
111
+ end
112
+
113
+ # benchmark the current request/respond cycle and output the result
114
+ # via Inform#debug (so make sure you got :debug in your Global.inform_tags
115
+ #
116
+ # It works as a simple wrapper with no other impacts on the rest
117
+ # of the system.
118
+
119
+ def bench_process(request, response)
120
+ time = Benchmark.measure do
121
+ response = respond(response, Dispatcher.handle(request, response))
122
+ end
123
+ debug "#{request} took #{time.real}s"
124
+ response
125
+ end
126
+
127
+ # simply respond to a given request, #set_head and #set_out in the process
128
+ # as well as setting the response-status (which is, in case it is not
129
+ # given, 500, if nothing goes wrong it should be 200 or 302)
130
+
131
+ def respond orig_response, response
132
+ @response = orig_response
133
+ if response
134
+ set_head(response)
135
+ set_out(response)
136
+ @response.status = response.code || STATUS_CODE[:internal_server_error]
137
+ end
138
+ @response
139
+ end
140
+
141
+ # map the respond.head[key] to @response[key]
142
+
143
+ def set_head(response)
144
+ response.head.each do |key, val|
145
+ @response[key] = val
146
+ end
147
+ end
148
+
149
+ # set the body... in case you have Global.tidy = true it will run it
150
+ # through Tool::Tidy.tidy first (if your content-type is text/html)
151
+
152
+ def set_out response
153
+ @response.body =
154
+ if Global.tidy and response.content_type == 'text/html'
155
+ Tool::Tidy.tidy(response.out)
156
+ else
157
+ response.out
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ module Ramaze
5
+ autoload :MemoryCache, "ramaze/cache/memory.rb"
6
+ autoload :YAMLStoreCache, "ramaze/cache/yaml_store.rb"
7
+ autoload :MemcachedCache, "ramaze/cache/memcached.rb"
8
+
9
+ Cache = nil
10
+ SessionCache = nil
11
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+ require 'memcache'
4
+
5
+ module Ramaze
6
+ class MemcachedCache
7
+ def initialize(host = 'localhost', port = '11211', namespace = 'ramaze')
8
+ @cache = MemCache.new("#{host}:#{port}", :namespace => namespace)
9
+ end
10
+
11
+ # please read the documentation of memcache-client for further methods.
12
+ # also, it is highly recommended to install memcache-client_extensions
13
+ # for a bit of speedup and more functionality
14
+ # Some examples:
15
+ #
16
+ # [key] #=> get a key
17
+ # [key] = value #=> set a key
18
+ # delete(key) #=> delete key
19
+ # set_many :x => :y, :n => :m #=> set many key/value pairs
20
+ # get_hash :x, :y #=> return a hash with key/value pairs
21
+ # stats #=> get some statistics about usage
22
+ # namespace #=> get the current namespace
23
+ # namespace = 'ramaze' #=> set a different namespace ('ramaze' is default)
24
+ # flush_all #=> flush the whole cache (deleting all)
25
+ # compression #=> see if compression is true/false
26
+ # compression = false #=> turn off compression (it's by default true)
27
+
28
+ def method_missing(*args, &block)
29
+ @cache.__send__(*args, &block)
30
+ end
31
+
32
+ # out of some reason MemCache sometimes doesn't respond to
33
+ # get_multi, have to investigate this further.
34
+ #
35
+ # for now, i'll just use the dumbed down version and ask it
36
+ # whether it implements this functionality or not.
37
+
38
+ def get_multi(*keys)
39
+ if @cache.respond_to?(:get_multi)
40
+ @cache.get_multi(*keys)
41
+ else
42
+ @cache.get_hash(*keys)
43
+ end
44
+ end
45
+
46
+ # same as get_multi, but returns only the values (in order)
47
+
48
+ def values_at(*keys)
49
+ get_multi(*keys).values_at(*keys)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ module Ramaze
5
+ MemoryCache = Hash
6
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+ require 'yaml/store'
4
+
5
+ module Ramaze
6
+ class YAMLStoreCache
7
+
8
+ # create a new YAML::Store with the given file (which will be created if it
9
+ # is not already there).
10
+
11
+ def initialize(file = 'cache.yaml')
12
+ @cache = YAML::Store.new(file)
13
+ end
14
+
15
+ # return the values for given keys.
16
+
17
+ def values_at(*keys)
18
+ transaction do |y|
19
+ keys.map{|k| y[k]}
20
+ end
21
+ end
22
+
23
+ # just a helper to use transactions.
24
+
25
+ def transaction(&block)
26
+ @cache.transaction(&block)
27
+ end
28
+
29
+ # catch everything else and use a transaction to send it.
30
+
31
+ def method_missing(*args, &block)
32
+ transaction do |y|
33
+ y.send(*args, &block)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ require 'ramaze/helper'
5
+
6
+ module Ramaze
7
+ module Controller
8
+ include Ramaze::Helper
9
+ end
10
+ end
@@ -0,0 +1,315 @@
1
+ # Copyright (c) 2006 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ require 'timeout'
5
+
6
+ module Ramaze
7
+
8
+ # The Dispatcher is the very heart of Ramaze itself.
9
+ #
10
+ # It will take requests and guide the request through all parts of the
11
+ # framework and your application.
12
+ #
13
+ # It is built in a way that lets you easily replace it with something you
14
+ # like better, since i'm very fond of the current implementation you can't
15
+ # find any examples of how this is done exactly yet.
16
+
17
+ module Dispatcher
18
+ class << self
19
+ include Trinity
20
+
21
+ # handle a request/response pair as given by the adapter.
22
+ # has to answer with a response.
23
+ #
24
+ # It is built so it will catch _all_ errors and exceptions
25
+ # thrown during processing of the request and #handle_error if
26
+ # a problem occurs.
27
+
28
+ def handle orig_request, orig_response
29
+ @orig_request, @orig_response = orig_request, orig_response
30
+ respond(orig_response, orig_request)
31
+ rescue Object => exception
32
+ error(exception)
33
+ handle_error(exception)
34
+ end
35
+
36
+ # The handle_error method takes an exception and decides based on that
37
+ # how it is going to respond in case of an error.
38
+ #
39
+ # In future this will become more and more configurable, right now
40
+ # you can provide your own error-method and error.xhtml inside either
41
+ # your trait[:template_root] or trait[:public].
42
+ #
43
+ # As more and more error-classes are being added to Ramaze you will get
44
+ # the ability to define your own response-pages and/or behaviour like
45
+ # automatic redirects.
46
+ #
47
+ # This feature is only available if your Global.error is true, which is
48
+ # the default.
49
+ #
50
+ # Yes, again, webrick _has_ to be really obscure, I searched for half an hour
51
+ # and still have not the faintest idea how request_path is related to
52
+ # request_uri...
53
+ # anyway, the solution might be simple?
54
+
55
+ def handle_error exception
56
+ meth_debug :handle_error, exception
57
+ Thread.current[:exception] = exception
58
+
59
+ case exception
60
+ when nil #Error::NoAction, Error::NoController
61
+ build_response(exception.message, STATUS_CODE[:not_found])
62
+ else
63
+ if Global.error_page
64
+ req = Thread.current[:request]
65
+
66
+ unless ((req.request_uri.path = '/error') rescue false)
67
+ req.request.params['REQUEST_PATH'] = '/error'
68
+ end
69
+
70
+ fill_out
71
+ else
72
+ build_response(exception.message, STATUS_CODE[:internal_server_error])
73
+ end
74
+ end
75
+ end
76
+
77
+ # setup the #setup_environment (Trinity) and start #fill_out
78
+
79
+ def respond orig_response, orig_request
80
+ setup_environment orig_response, orig_request
81
+ fill_out
82
+ end
83
+
84
+ #
85
+
86
+ def build_response out = '', code = STATUS_CODE[:internal_server_error], head = {}
87
+ default_head = {
88
+ 'Content-Type' => 'text/plain',
89
+ }
90
+
91
+ if Global.cookies
92
+ default_head['Set-Cookie'] = session.export
93
+ else
94
+ head.delete('Set-Cookie')
95
+ end
96
+
97
+ head = default_head.merge(head)
98
+
99
+ Response.new(out, code, head)
100
+ end
101
+
102
+
103
+ # Obtain the path requested from the request and search for a static
104
+ # file matching the request, #respond_file is called if it finds one,
105
+ # otherwise the path is given on to #respond_action.
106
+ # Answers with a response
107
+
108
+ def fill_out
109
+ path = request.request_path.squeeze('/')
110
+ info "Request from #{request.remote_addr}: #{path}"
111
+
112
+ the_paths = $:.map{|way| (way/'public'/path) }
113
+ if file = the_paths.find{|way| File.exist?(way) and File.file?(way)}
114
+ respond_file file
115
+ else
116
+ respond_action path
117
+ end
118
+ response
119
+ end
120
+
121
+ # takes a file and sets the response.out to the contents of the file
122
+ # If you are running mongrel as your adapter it will take advantage of
123
+ # mongrels #send_file
124
+
125
+ def respond_file file
126
+ debug "Responding with static file: #{file}"
127
+
128
+ response.head['Content-Type'] = ''
129
+ if @orig_response.respond_to?(:send_file)
130
+ @orig_response.send_file(file)
131
+ else
132
+ response.out = File.read(file)
133
+ end
134
+ end
135
+
136
+ # Takes the path, figures out the controller by asking #resolve_controller
137
+ # for the controller, action and params for the path.
138
+ #
139
+ # Sets the cookies in the response.head if Global.cookies is set
140
+ #
141
+ # finally it runs #handle_controller
142
+
143
+ def respond_action path
144
+ debug "Responding with action: #{path}"
145
+
146
+ controller, action, params = resolve_controller(path)
147
+ catch :respond do
148
+ response.out = handle_controller(controller, action, params)
149
+ end
150
+ end
151
+
152
+ # find out which controller should be used based on the path.
153
+ # it will answer [controller, action, params] or raise an
154
+ #
155
+ # Ramaze::Error::NoController # if no controller is found
156
+ # Ramaze::Error::NoAction # if no action but a controller is found
157
+ #
158
+ # It actually uses #resolve_action on almost every combination of
159
+ # so-called paractions (yet unsplit but possible combination of action
160
+ # and parameters for the action)
161
+ #
162
+ # If your templating is action-less, which means it does not depend on
163
+ # methods on the controller, but rather on templates or just dynamically
164
+ # calculated stuff you can set trait[:actionless] for your templating.
165
+ #
166
+ # Please see the documentation for Ramaze::Template::Amrita2 for an more
167
+ # specific example of how it is used in practice.
168
+ #
169
+ # Further it uses the Global.mapping to look up the controller to be used.
170
+ #
171
+ # Also, the action '/' will be expanded to 'index'
172
+ #
173
+ # Parameters are CGI.unescaped
174
+
175
+ def resolve_controller path
176
+ meth_debug :resolve_controller, path
177
+ track = path.split('/')
178
+ controller = false
179
+ action = false
180
+ tracks = []
181
+
182
+ track.unshift '/'
183
+
184
+ track.each do |atom|
185
+ tracks << (tracks.last.to_s / atom)
186
+ end
187
+
188
+ until controller and action or tracks.empty?
189
+ current = Regexp.escape(tracks.pop.to_s)
190
+ paraction = path.gsub(/^#{current}/, '').split('/').map{|e| CGI.unescape(e)}
191
+ paraction.delete('')
192
+ if controller = Ramaze::Global.mapping[current]
193
+ if controller.ancestral_trait[:actionless] or paraction == ['error']
194
+
195
+ action = paraction.shift
196
+ params = paraction
197
+ action = 'index' if action == nil
198
+ else
199
+ action, params = resolve_action controller, paraction
200
+ end
201
+ end
202
+ end
203
+
204
+ raise Ramaze::Error::NoController, "No Controller found for #{path}" unless controller
205
+ raise Ramaze::Error::NoAction, "No Action found for #{path}" unless action
206
+
207
+ return controller, action, params
208
+ end
209
+
210
+ # Resolve the method to be called and the number of parameters
211
+ # it will receive for a specific class (the controller) given the
212
+ # paraction (like 'foo/bar' => controller.call('foo', 'bar'))
213
+ # in case arity is 1 and a public instance-method named foo is defined.
214
+ #
215
+ # TODO:
216
+ # - find a solution for def x(a = :a) which has arity -1
217
+ # identical to def x(*a) for some odd reason
218
+
219
+ def resolve_action controller, paraction
220
+ meth_debug :resolve_action, controller, paraction
221
+
222
+ meths =
223
+ (controller.ancestors - [Kernel, Object]).inject([]) do |sum, klass|
224
+ sum | (klass.is_a?(Module) ? klass.instance_methods(false) : sum)
225
+ end
226
+
227
+ track = paraction.dup
228
+ tracks = []
229
+ action = false
230
+
231
+ track.each do |atom|
232
+ atom = [tracks.last.to_s, atom]
233
+ atom.delete('')
234
+ tracks << atom.join('__')
235
+ end
236
+
237
+ tracks.unshift 'index'
238
+
239
+ until action or tracks.empty?
240
+ current = tracks.pop
241
+ if meths.include?(current)
242
+ arity = controller.instance_method(current).arity
243
+ params = (paraction - current.split('__'))
244
+
245
+ if params.size == arity
246
+ return current, params
247
+ elsif arity < 0 and arity + params.size >= 0
248
+ return current, params
249
+ elsif arity == -1
250
+ return current, params
251
+ end
252
+ end
253
+ end
254
+ end
255
+
256
+ # Depending on Global.cache_all and Global.cache_actions it will
257
+ # call either #handle_uncached_controller or #handle_cached_controller
258
+ # takes the controller, action and params and just passes them on.
259
+
260
+ def handle_controller controller, action, params
261
+ if Global.cache_all or Global.cache_actions[controller].include?(action.to_s)
262
+ handle_cached_controller(controller, action, *params)
263
+ else
264
+ handle_uncached_controller(controller, action, *params)
265
+ end
266
+ end
267
+
268
+ # Call the class-method handle_request with action and *params, all
269
+ # the controller has to do is to respond with a string.
270
+
271
+ def handle_uncached_controller controller, action, *params
272
+ controller.handle_request(action, *params)
273
+ end
274
+
275
+ # Call the class-method handle_request with action and *params, all
276
+ # the controller has to do is to respond with a string.
277
+ #
278
+ # To add a method for caching you can either use the CacheHelper
279
+ # or directly
280
+ # Global.cache_actions[self] << 'index'
281
+ #
282
+ # Caching is done for [action, params] pairs per controller.
283
+
284
+ def handle_cached_controller controller, action, *params
285
+ Global.cached_actions ||= Global.cache.new
286
+
287
+ key = [action, params].inspect
288
+
289
+ Global.cached_actions[controller] ||= {key => nil}
290
+
291
+ if out = Global.cached_actions[controller][key]
292
+ debug "Using Cached version for #{key.inspect}"
293
+ return out
294
+ end
295
+
296
+ debug "Compiling Action: #{action} #{params.join(', ')}"
297
+ Global.cached_actions[controller][key] =
298
+ handle_uncached_controller(controller, action, *params)
299
+ end
300
+
301
+ # Setup the Trinity (Request, Response, Session) and store them as
302
+ # thread-variables in Thread.current
303
+ # Thread.current[:request] == Request.current
304
+ # Thread.current[:response] == Response.current
305
+ # Thread.current[:session] == Session.current
306
+
307
+ def setup_environment orig_response, orig_request
308
+ this = Thread.current
309
+ this[:request] = Request.new(orig_request)
310
+ this[:session] = Session.new(request)
311
+ this[:response] = build_response('', STATUS_CODE[:ok], 'Content-Type' => 'text/html')
312
+ end
313
+ end
314
+ end
315
+ end