ramaze 0.0.7 → 0.0.8

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 (193) hide show
  1. data/Rakefile +52 -19
  2. data/bin/ramaze +19 -6
  3. data/doc/CHANGELOG +33 -0
  4. data/doc/COPYING +1 -1
  5. data/doc/FAQ +92 -0
  6. data/doc/GPL +340 -0
  7. data/doc/INSTALL +34 -0
  8. data/doc/ProjectInfo +53 -0
  9. data/doc/README +187 -110
  10. data/doc/readme_chunks/appendix.txt +13 -0
  11. data/doc/readme_chunks/examples.txt +38 -0
  12. data/doc/readme_chunks/features.txt +82 -0
  13. data/doc/readme_chunks/getting_help.txt +5 -0
  14. data/doc/readme_chunks/getting_started.txt +18 -0
  15. data/doc/readme_chunks/installing.txt +41 -0
  16. data/doc/readme_chunks/introduction.txt +18 -0
  17. data/doc/readme_chunks/principles.txt +41 -0
  18. data/doc/readme_chunks/thanks.txt +59 -0
  19. data/doc/tutorial/todolist.txt +546 -0
  20. data/examples/blog/main.rb +1 -1
  21. data/examples/blog/src/controller.rb +13 -11
  22. data/examples/blog/src/element.rb +11 -6
  23. data/examples/blog/src/model.rb +8 -23
  24. data/examples/blog/template/edit.xhtml +3 -1
  25. data/examples/blog/template/index.xhtml +4 -4
  26. data/examples/caching.rb +4 -4
  27. data/examples/element.rb +10 -7
  28. data/examples/hello.rb +3 -4
  29. data/examples/simple.rb +5 -3
  30. data/examples/templates/template/external.amrita +19 -0
  31. data/examples/templates/template/{external.rmze → external.zmr} +2 -2
  32. data/examples/templates/template_amrita2.rb +48 -0
  33. data/examples/templates/template_erubis.rb +5 -2
  34. data/examples/templates/{template_ramaze.rb → template_ezamar.rb} +13 -7
  35. data/examples/templates/template_haml.rb +4 -1
  36. data/examples/templates/template_liquid.rb +2 -1
  37. data/examples/templates/template_markaby.rb +2 -1
  38. data/examples/todolist/conf/benchmark.yaml +35 -0
  39. data/examples/todolist/conf/debug.yaml +34 -0
  40. data/examples/todolist/conf/live.yaml +33 -0
  41. data/examples/todolist/conf/silent.yaml +31 -0
  42. data/examples/todolist/conf/stage.yaml +33 -0
  43. data/examples/todolist/main.rb +18 -0
  44. data/examples/todolist/public/404.jpg +0 -0
  45. data/examples/todolist/public/css/coderay.css +105 -0
  46. data/examples/todolist/public/css/ramaze_error.css +42 -0
  47. data/{lib/proto → examples/todolist}/public/error.xhtml +0 -0
  48. data/examples/todolist/public/favicon.ico +0 -0
  49. data/examples/todolist/public/js/jquery.js +1923 -0
  50. data/examples/todolist/public/ramaze.png +0 -0
  51. data/examples/todolist/src/controller/main.rb +56 -0
  52. data/examples/todolist/src/element/page.rb +26 -0
  53. data/examples/todolist/src/model.rb +14 -0
  54. data/examples/todolist/template/index.xhtml +17 -0
  55. data/examples/todolist/template/new.xhtml +7 -0
  56. data/examples/todolist/todolist.db +9 -0
  57. data/examples/whywiki/main.rb +3 -8
  58. data/examples/whywiki/template/show.xhtml +4 -0
  59. data/lib/proto/public/error.zmr +77 -0
  60. data/lib/proto/src/controller/main.rb +2 -1
  61. data/lib/proto/src/element/page.rb +2 -1
  62. data/lib/proto/src/model.rb +3 -2
  63. data/lib/ramaze.rb +7 -9
  64. data/lib/ramaze/adapter.rb +51 -0
  65. data/lib/ramaze/adapter/cgi.rb +23 -0
  66. data/lib/ramaze/adapter/fcgi.rb +22 -0
  67. data/lib/ramaze/adapter/mongrel.rb +7 -86
  68. data/lib/ramaze/adapter/webrick.rb +14 -133
  69. data/lib/ramaze/cache/memcached.rb +6 -0
  70. data/lib/ramaze/cache/yaml_store.rb +3 -1
  71. data/lib/ramaze/controller.rb +292 -2
  72. data/lib/ramaze/dispatcher.rb +85 -213
  73. data/lib/ramaze/error.rb +10 -0
  74. data/lib/ramaze/global.rb +8 -0
  75. data/lib/ramaze/helper/aspect.rb +30 -7
  76. data/lib/ramaze/helper/auth.rb +16 -9
  77. data/lib/ramaze/helper/cache.rb +40 -35
  78. data/lib/ramaze/helper/feed.rb +1 -1
  79. data/lib/ramaze/helper/flash.rb +34 -0
  80. data/lib/ramaze/helper/link.rb +8 -2
  81. data/lib/ramaze/helper/openid.rb +63 -0
  82. data/lib/ramaze/helper/redirect.rb +12 -11
  83. data/lib/ramaze/helper/stack.rb +5 -7
  84. data/lib/ramaze/inform.rb +12 -1
  85. data/lib/ramaze/snippets/kernel/aquire.rb +1 -1
  86. data/lib/ramaze/snippets/kernel/{self_method.rb → method.rb} +3 -18
  87. data/lib/ramaze/snippets/kernel/{rescue_require.rb → pretty_inspect.rb} +7 -6
  88. data/lib/ramaze/snippets/method/name.rb +22 -0
  89. data/lib/ramaze/snippets/{kernel → ramaze}/autoreload.rb +0 -0
  90. data/lib/ramaze/snippets/ramaze/caller_info.rb +14 -0
  91. data/lib/ramaze/snippets/{kernel → ramaze}/caller_lines.rb +3 -10
  92. data/lib/ramaze/snippets/rdoc/usage_no_exit.rb +49 -23
  93. data/lib/ramaze/snippets/string/DIVIDE.rb +0 -1
  94. data/lib/ramaze/store/default.rb +58 -2
  95. data/lib/ramaze/store/yaml.rb +161 -0
  96. data/lib/ramaze/template.rb +27 -86
  97. data/lib/ramaze/template/amrita2.rb +14 -19
  98. data/lib/ramaze/template/erubis.rb +15 -38
  99. data/lib/ramaze/template/ezamar.rb +100 -0
  100. data/lib/ramaze/template/ezamar/element.rb +166 -0
  101. data/lib/ramaze/template/ezamar/engine.rb +124 -0
  102. data/lib/ramaze/template/ezamar/morpher.rb +155 -0
  103. data/lib/ramaze/template/haml.rb +16 -43
  104. data/lib/ramaze/template/liquid.rb +11 -51
  105. data/lib/ramaze/template/markaby.rb +44 -42
  106. data/lib/ramaze/tool/mime.rb +18 -0
  107. data/lib/ramaze/tool/mime_types.yaml +615 -0
  108. data/lib/ramaze/trinity/request.rb +20 -196
  109. data/lib/ramaze/trinity/response.rb +4 -33
  110. data/lib/ramaze/trinity/session.rb +150 -72
  111. data/lib/ramaze/version.rb +1 -1
  112. data/spec/adapter_spec.rb +20 -0
  113. data/spec/public/favicon.ico +0 -0
  114. data/spec/public/ramaze.png +0 -0
  115. data/spec/public/test_download.css +141 -0
  116. data/spec/{tc_request.rb → request_tc_helper.rb} +45 -21
  117. data/spec/spec_all.rb +77 -34
  118. data/spec/spec_helper.rb +8 -157
  119. data/spec/spec_helper_context.rb +72 -0
  120. data/spec/spec_helper_requester.rb +52 -0
  121. data/spec/spec_helper_simple_http.rb +433 -0
  122. data/spec/tc_adapter_mongrel.rb +3 -15
  123. data/spec/tc_adapter_webrick.rb +4 -14
  124. data/spec/tc_cache.rb +3 -5
  125. data/spec/tc_controller.rb +22 -12
  126. data/spec/tc_dependencies.rb +13 -0
  127. data/spec/tc_element.rb +8 -7
  128. data/spec/tc_error.rb +13 -7
  129. data/spec/tc_global.rb +16 -18
  130. data/spec/tc_helper_aspect.rb +2 -4
  131. data/spec/tc_helper_auth.rb +15 -14
  132. data/spec/tc_helper_cache.rb +5 -7
  133. data/spec/tc_helper_feed.rb +0 -2
  134. data/spec/tc_helper_flash.rb +103 -0
  135. data/spec/tc_helper_form.rb +4 -6
  136. data/spec/tc_helper_link.rb +1 -3
  137. data/spec/tc_helper_redirect.rb +23 -8
  138. data/spec/tc_helper_stack.rb +31 -15
  139. data/spec/tc_morpher.rb +1 -3
  140. data/spec/tc_params.rb +48 -7
  141. data/spec/tc_request_mongrel.rb +9 -0
  142. data/spec/tc_request_webrick.rb +5 -0
  143. data/spec/tc_session.rb +41 -25
  144. data/spec/tc_store.rb +55 -6
  145. data/spec/tc_store_yaml.rb +71 -0
  146. data/spec/tc_template_amrita2.rb +3 -3
  147. data/spec/tc_template_erubis.rb +2 -3
  148. data/spec/{tc_template_ramaze.rb → tc_template_ezamar.rb} +15 -5
  149. data/spec/tc_template_haml.rb +4 -3
  150. data/spec/tc_template_liquid.rb +3 -4
  151. data/spec/tc_template_markaby.rb +4 -6
  152. data/spec/tc_tidy.rb +1 -3
  153. data/spec/template/amrita2/{data.html → data.amrita} +0 -0
  154. data/spec/template/amrita2/{index.html → index.amrita} +0 -0
  155. data/spec/template/amrita2/{sum.html → sum.amrita} +0 -0
  156. data/spec/template/ezamar/another/long/action.zmr +1 -0
  157. data/spec/template/ezamar/combined.zmr +1 -0
  158. data/spec/template/{ramaze/file_only.rmze → ezamar/file_only.zmr} +0 -0
  159. data/spec/template/{ramaze/index.rmze → ezamar/index.zmr} +0 -0
  160. data/spec/template/{ramaze/nested.rmze → ezamar/nested.zmr} +0 -0
  161. data/spec/template/ezamar/some__long__action.zmr +1 -0
  162. data/spec/template/{ramaze/sum.rmze → ezamar/sum.zmr} +0 -0
  163. metadata +181 -123
  164. data/doc/allison/LICENSE +0 -184
  165. data/doc/allison/README +0 -37
  166. data/doc/allison/allison.css +0 -300
  167. data/doc/allison/allison.gif +0 -0
  168. data/doc/allison/allison.js +0 -307
  169. data/doc/allison/allison.rb +0 -287
  170. data/doc/allison/cache/BODY +0 -588
  171. data/doc/allison/cache/CLASS_INDEX +0 -4
  172. data/doc/allison/cache/CLASS_PAGE +0 -1
  173. data/doc/allison/cache/FILE_INDEX +0 -4
  174. data/doc/allison/cache/FILE_PAGE +0 -1
  175. data/doc/allison/cache/FONTS +0 -1
  176. data/doc/allison/cache/FR_INDEX_BODY +0 -1
  177. data/doc/allison/cache/IMGPATH +0 -1
  178. data/doc/allison/cache/INDEX +0 -1
  179. data/doc/allison/cache/JAVASCRIPT +0 -307
  180. data/doc/allison/cache/METHOD_INDEX +0 -4
  181. data/doc/allison/cache/METHOD_LIST +0 -1
  182. data/doc/allison/cache/SRC_PAGE +0 -1
  183. data/doc/allison/cache/STYLE +0 -322
  184. data/doc/allison/cache/URL +0 -1
  185. data/doc/changes.txt +0 -2021
  186. data/doc/changes.xml +0 -2024
  187. data/lib/ramaze/snippets/kernel/silently.rb +0 -13
  188. data/lib/ramaze/snippets/thread/deadQUESTIONMARK.rb +0 -11
  189. data/lib/ramaze/template/haml/actionview_stub.rb +0 -20
  190. data/lib/ramaze/template/ramaze.rb +0 -177
  191. data/lib/ramaze/template/ramaze/element.rb +0 -166
  192. data/lib/ramaze/template/ramaze/morpher.rb +0 -156
  193. data/spec/tc_test.rb +0 -17
@@ -7,20 +7,10 @@ require 'digest/md5'
7
7
 
8
8
  module Ramaze
9
9
 
10
- # This class is used for processing the information coming in from a request
11
- # to the dispatcher, it takes the original request-object, processes it and
12
- # is later available in the controller or as Thread.current[:request]
13
- #
14
- # Please note that the implementation is lacking performance and security
15
- # in favor of simplicity. Hopefully I (or some CGI-guru) will come along
16
- # and implement this properly, until then consider it unsafe, but functional.
17
- #
18
- # Most information you will need is in the #params, which is a compound of
19
- # all the information available from POST, GET, DELETE and PUT.
10
+ # The purpose of this class is to act as a simple wrapper for Rack::Request
11
+ # and provide some convinient methods for our own use.
20
12
 
21
13
  class Request
22
- attr_accessor :request, :post_query, :get_query, :puts_query, :get_query
23
-
24
14
  class << self
25
15
 
26
16
  # get the current request out of Thread.current[:request]
@@ -32,213 +22,47 @@ module Ramaze
32
22
  end
33
23
  end
34
24
 
35
- # create a new instance of Request, takes the original request-object
36
- # and runs #parse_queries to extract/process the information inside
25
+ # create a new instance of Request, takes the original Rack::Request
26
+ # instance
37
27
 
38
28
  def initialize request = {}
39
29
  @request = request
40
- parse_queries
41
30
  end
42
31
 
43
- # you can access the original @request via this method_missing,
44
- # first it tries to match your method with any of the HTTP parameters
45
- # then, in case that fails, it will relay to @request
46
-
47
- def method_missing meth, *args, &block
48
- if value = @request.params[meth.to_s.upcase] rescue false
49
- value
50
- else
51
- @request.send(meth, *args, &block)
52
- end
53
- end
54
-
55
- # containts all the parameters given, no matter wheter with
56
- # POST, GET, PUT or DELETE
57
- # answers with a hash that is generated from the respective
58
- # _query instance-variables and cached subsequently in @params
59
-
60
- def params
61
- @params ||= [
62
- @get_query, @post_query, @put_query, @delete_query
63
- ].inject({}) do |sum, hash|
64
- sum.merge(hash || {})
65
- end
66
- end
67
-
68
- # this parses stuff like post-requests (very untested)
69
- # and also ?foo=bar stuff (get-query)
70
- # WEBrick uses body as a streaming-object, so we have to #read.
71
- # Mongrel has a normal string as body, we just call to_s in case
72
- # it's no POST
73
-
74
- def parse_queries
75
- case request_method
76
- when 'GET' : process_get
77
- when 'POST' : process_post
78
- when 'PUT' : process_put
79
- when 'DELETE' : process_delete
80
- end
81
- end
82
-
83
- # very naive implementation of POST-body parsing, this won't withstand
84
- # any serious testing or multiple simultanous huge posts...
85
- # However, it just extracts the information inside the @request.body
86
- # and puts it into proper form
87
- # you can access its contents via #post_query
88
-
89
- def process_post
90
- @post_query = {}
91
-
92
- type, boundary = content_type.split(';')
93
-
94
- if type.downcase == 'multipart/form-data' and not boundary.empty?
95
- parse_multipart(body, boundary.split('=').last)
96
- else
97
- post_query = query_parse(body.respond_to?(:read) ? body.read : body)
98
- post_query.each do |key, value|
99
- @post_query[CGI.unescape(key)] = CGI.unescape(value)
100
- end
101
- end
102
- end
103
-
104
- # processing incoming GET, stuffing the results into @get_query,
105
- # you can access the information via #get_query
106
-
107
- def process_get
108
- @get_query = {}
109
-
110
- get_query = query_parse(query_string) rescue {}
111
- get_query.each do |key, value|
112
- @get_query[CGI.unescape(key)] = CGI.unescape(value)
113
- end
114
- end
115
-
116
- # TODO
117
- # - implement and test DELETE
118
-
119
- def process_delete
120
- @delete_query = {}
121
- raise "Implement me"
122
- end
123
-
124
- # again, rather naive, it just gives you the control over what to do
125
- # with the #body but will parse the parameters from the URL
126
-
127
- def process_put
128
- @pust_query = {}
129
-
130
- put_query = query_parse(query_string) rescue {}
131
- put_query.each do |key, value|
132
- @put_query[CGI.unescape(key)] = CGI.unescape(value)
133
- end
134
- end
135
-
136
- # process the parameters passed over the URL, they look like
137
- #
138
- # http://foo.bar/action?eins=one&zwei=two
139
- #
140
- # that would result in #params containing {'eins' => 'one', 'zwei' => 'two'}
141
-
142
- def query_parse str
143
- str = str.split('?').last.to_s rescue ''
144
- hash = CGI.parse(str)
145
- hash.each do |key, values|
146
- key = CGI.unescape(key)
147
- values = values.map{|v| CGI.unescape(v)}
148
- hash[key] = values.size == 1 ? values.first : values
149
- end
150
- hash
151
- end
152
-
153
- # parse multipart-requests, just pass it something that responds to .read
154
- # and a boundary for the parts.
155
- # again a naive implementation without any guarantee against DoS.
156
- #
157
- # TODO:
158
- # - rewrite parsing of multipart
159
- # - chunk through the body and pipe into tempfile
160
- # - look at merb for example of correct parsing
161
-
162
- def parse_multipart(body, boundary)
163
- text = body.read
164
- text.split("--" << boundary).each do |chunk|
165
- header = chunk.split("\r\n\r\n").first
166
- next if (!header or !body) || (header.strip.empty? or chunk.strip.empty?)
167
- head = parse_multipart_head(header)
168
- next if head.empty?
169
- chunk = chunk[(header.size + 4)..-3]
170
- hash = Digest::MD5.hexdigest([head['name'], chunk.size, head.hash].inspect)
171
- filename = File.join(Dir.tmpdir, hash)
172
- File.open(filename, "w+") do |file|
173
- file.print(chunk)
174
- end
175
- @post_query[head['name']] = File.open(filename)
176
- end
177
- body.rewind
178
- end
179
-
180
- # parse the head of the one part of multipart
181
- # you most likely won't have to use this on your own :)
182
-
183
- def parse_multipart_head(string)
184
- string.gsub("\r\n", ";").split(';').inject({}) do |sum, param|
185
- key, value = param.strip.split('=')
186
- sum[key] = value[1..-2] if key and value
187
- sum
188
- end
189
- end
190
-
191
- # like request.params[key]
32
+ # shortcut for request.params[key]
192
33
 
193
34
  def [](key)
194
35
  params[key]
195
36
  end
196
37
 
197
- # like reuqest.params[key] = value
38
+ # shortcut for request.params[key] = value
198
39
 
199
40
  def []=(key, value)
200
41
  params[key] = value
201
42
  end
202
43
 
203
- # request_method == 'GET'
204
- def get?() request_method == 'GET' end
205
- # request_method == 'POST'
206
- def post?() request_method == 'POST' end
207
- # request_method == 'PUT'
208
- def put?() request_method == 'PUT' end
209
- # request_method == 'DELETE'
210
- def delete?() request_method == 'DELETE' end
44
+ # like Hash#values_at
211
45
 
212
- # remote_addr == '127.0.0.1'
213
-
214
- def local?
215
- remote_addr == '127.0.0.1'
46
+ def values_at(*keys)
47
+ keys.map{|key| params[key] }
216
48
  end
217
49
 
218
- # Is the request coming from a local network?
219
-
220
- def local_net?(ip = remote_addr)
221
- bip = ip.split('.').map{ |x| x.to_i }.pack('C4').unpack('N')[0]
222
-
223
- # 127.0.0.1/32 => 2130706433
224
- # 192.168.0.0/16 => 49320
225
- # 172.16.0.0/12 => 2753
226
- # 10.0.0.0/8 => 10
227
-
228
- { 0 => 2130706433, 16 => 49320, 20 => 2753, 24 => 10}.each do |s,c|
229
- return true if (bip >> s) == c
230
- end
50
+ # the referer of the client or '/'
231
51
 
232
- return false
52
+ def referer
53
+ @request.env['HTTP_REFERER'] || '/'
233
54
  end
234
55
 
235
- # check the referer from which the browser came
236
- # '/' if no referer given.
56
+ alias referrer referer
237
57
 
238
- def referer
239
- headers['HTTP_REFERER'] || '/'
58
+ # you can access the original @request via this method_missing,
59
+ # first it tries to match your method with any of the HTTP parameters
60
+ # then, in case that fails, it will relay to @request
61
+
62
+ def method_missing meth, *args, &block
63
+ @request.send(meth, *args, &block)
240
64
  rescue
241
- params['HTTP_REFERER'] || '/'
65
+ @request.env[meth.to_s.upcase]
242
66
  end
243
67
  end
244
68
  end
@@ -2,40 +2,11 @@
2
2
  # All files in this distribution are subject to the terms of the Ruby license.
3
3
 
4
4
  module Ramaze
5
- # The Response given back to the adapter, this is the center of every request
6
- # made.
5
+ # get the current response out of Thread.current[:response]
7
6
  #
8
- # In case you do a custom response, just make sure you implement all three
9
- # properties: #out, #code, #head
10
- # where head has to #respond_to? #[]
11
- #
12
- # code is the status-code ( http://en.wikipedia.org/wiki/List_of_HTTP_status_codes )
13
- # and out should be something very String-like
14
-
15
- class ResponseStruct < Struct
16
-
17
- # get the current response out of Thread.current[:response]
18
- #
19
- # You can call this from everywhere with Ramaze::Response.current
20
-
21
- def current
22
- Thread.current[:response]
23
- end
7
+ # You can call this from everywhere with Ramaze::Response.current
24
8
 
25
- # just #inspect for this class in the format of
26
- # <Response#324543 @code => 200, @head => {'Content-Type'=>'text/html', @out.size => 234>
27
-
28
- def inspect
29
- "<Response##{object_id} @code => #{code}, @head => #{head.inspect}, @out.size => #{out.size}>"
30
- end
31
- alias pretty_inspect inspect
32
-
33
- # same as #head['Content-Type']
34
-
35
- def content_type
36
- head['Content-Type']
37
- end
9
+ def self.Response
10
+ Thread.current[:response]
38
11
  end
39
-
40
- Response = ResponseStruct.new(:out, :code, :head)
41
12
  end
@@ -5,125 +5,203 @@ require 'digest/sha2'
5
5
 
6
6
  module Ramaze
7
7
 
8
- # Session is the object that stores all the session-information of the user.
9
- # It is heavily based on cookies storing the key to the information stored
10
- # on the server.
8
+ # The SessionHash acts as the wrapper for a simple Hash
11
9
  #
12
- # It uses caching as set in Global.cache and tries to set a cookie in case
13
- # there is none set yet.
10
+ # Its purpose is to notify the underlying cache, in which the sessions
11
+ # are stored, about updates.
12
+
13
+ class SessionHash
14
+ def initialize
15
+ @hash = {}
16
+ end
17
+
18
+ # relays all the methods to the @hash and updates the session_cache in
19
+ # Session.current.sessions if anything changes.
20
+
21
+ def method_missing(*args, &block)
22
+ old = @hash.dup
23
+ result = @hash.send(*args, &block)
24
+ unless old == @hash
25
+ Session.current.sessions[Session.current.session_id] = self
26
+ end
27
+ result
28
+ end
29
+
30
+ def inspect
31
+ @hash.inspect
32
+ end
33
+ end
34
+
35
+ # The purpose of Session is to hold key/value pairs like a Hash for a series
36
+ # of # request/response cycles from the same client.
37
+ #
38
+ # The persistence is achieved by setting a cookie with the session_id to
39
+ # the client, which is then passed back and forth until the cookie is either
40
+ # deleted or expires.
14
41
 
15
42
  class Session
43
+
44
+ # The unique id for the current session which is also passed on to the cookie.
45
+
46
+ attr_accessor :session_id
47
+
48
+ # This variable holds the current SessionFlash
49
+
50
+ attr_accessor :flash
51
+
52
+ # the key used for the cookie
53
+
16
54
  SESSION_KEY = '_ramaze_session_id'
17
- attr_accessor :session
55
+
56
+ trait :finalize => [:finalize_flash]
18
57
 
19
58
  class << self
20
59
 
21
- # Get the current session out of Thread.current[:session]
22
- #
23
- # You can call this from everywhere with Ramaze::Session.current
60
+ # answers with Thread.current[:session] which holds the current session
61
+ # set by the Dispatcher#setup_environment.
24
62
 
25
63
  def current
26
64
  Thread.current[:session]
27
65
  end
28
66
  end
29
67
 
30
- # pass the request-object and it will extract the session-id (which is
31
- # stored in the cookie with the key of SESSION_KEY
68
+ # Initialize a new Session, requires the original Rack::Request instance
69
+ # given to us from Dispatcher#setup_environment.
70
+ #
71
+ # sets @session_id and @session_flash
72
+ #
73
+ # will set Thread.main[:session_cache] to an instance of
74
+ # Ramaze::Global.cache if no cache for the sessions is initialized yet
32
75
 
33
76
  def initialize request
34
- @session_id = parse(request)[SESSION_KEY]
77
+ @session_id = (request.cookies[SESSION_KEY] || random_key)
78
+ @flash = Ramaze::SessionFlash.new
79
+
80
+ unless sessions
81
+ global_cache = Ramaze::Global.cache
82
+
83
+ if global_cache.respond_to?(:new)
84
+ cache = global_cache.new
85
+ else
86
+ cache = constant("::Ramaze::#{global_cache}")
87
+ cache = cache.new if cache.respond_to?(:new)
88
+ end
89
+
90
+ Thread.main[:session_cache] = cache
91
+ end
35
92
  end
36
93
 
37
- # current session_id, will generate a new one based on #hash if no session
38
- # is currently active
94
+ # relay all messages we don't understand to the currently active session
39
95
 
40
- def session_id
41
- @session_id ||= hash
96
+ def method_missing(*args, &block)
97
+ current.send(*args, &block)
42
98
  end
43
99
 
44
- # the current contents of session
100
+ # answers with the currently active session, which is set unless it is
101
+ # existing already, the session itself is an instance of SessionHash
45
102
 
46
103
  def current
47
- sessions[session_id] ||= {}
104
+ sessions[session_id] ||= SessionHash.new
48
105
  end
49
106
 
50
- # all the sessions currently stored, in case there are none yet it will
51
- # set the constant Ramaze::SessionCache and from then on start populating
52
- # it with the sessions. SessionCache is an instance of Global.cache as
53
- # well.
107
+ # shortcut to Thread.main[:session_cache]
54
108
 
55
109
  def sessions
56
- Thread.main[:session_cache] ||= Global.cache.new
110
+ Thread.main[:session_cache]
57
111
  end
58
112
 
59
- # this runs before #parse and will extract the information stored in the cookie
60
- # of the client, in case there is none it will try to set one.
61
- # Unfortunatly we have to go seperate paths here for mongrel and webrick,
62
- # since they do not share the same API.
113
+ # generate a random (and hopefully unique) id for the current session.
63
114
  #
64
- # ARGH, 3 different ways for webrick to hand us cookies!? (and counting)
65
-
66
- def pre_parse request
67
- if Global.adapter == :webrick
68
- # input looks like this:
69
- # "Set-Cookie: _ramaze__session_id=fa8cc88dafcb0973b48d4d65ef57e7d3\r\n"
70
- cookie = request.raw_header.grep(/Set-Cookie/).first rescue ''
71
- cookie = request.post_query.delete('Set-Cookie') if cookie.to_s.empty? and request.post?
72
- cookie = request.header['cookie'] unless cookie
73
- cookie.to_s.gsub(/Set-Cookie: (.*?)\r\n/, '\1')
74
- else
75
- cookie = (request.http_cookie rescue request.http_set_cookie rescue '') || ''
115
+ # It consists of the current time, the current request, the current PID of
116
+ # ruby and object_id of this instance.
117
+ #
118
+ # All this is joined by some calls to Kernel#rand and returned as a
119
+ # Digest::SHA256::hexdigest
120
+
121
+ def random_key
122
+ h = [
123
+ Time.now.to_f.to_s.reverse, rand,
124
+ Thread.current[:request].hash, rand,
125
+ Process.pid, rand,
126
+ object_id, rand
127
+ ].join
128
+ Digest::SHA256.hexdigest(h)
129
+ end
130
+
131
+ # send each element of the trait[:finalize]
132
+ # this is at the moment used for flash_finalize.
133
+
134
+ def finalize
135
+ ancestral_trait[:finalize].each do |meth|
136
+ send meth
76
137
  end
77
138
  end
78
139
 
79
- # parse the cookie and extract all the variables stored in there.
140
+ def inspect
141
+ current.inspect
142
+ end
80
143
 
81
- def parse request
82
- cookie = pre_parse(request)
144
+ private
83
145
 
84
- return cookie if cookie.respond_to?(:to_hash)
146
+ # at the end of a request delete the current[:FLASH] and assign it to
147
+ # current[:FLASH_PREVIOUS]
148
+ #
149
+ # this is needed so flash can iterate over requests
150
+ # and always just keep the current and previous key/value pairs.
85
151
 
86
- cookie.split('; ').inject({}) do |s,v|
87
- key, value = v.split('=')
88
- s.merge key.strip => value
89
- end
90
- rescue => ex
91
- Informer.error ex
92
- {SESSION_KEY => hash}
152
+ def finalize_flash
153
+ old = delete(:FLASH)
154
+ current[:FLASH_PREVIOUS] = old if old
155
+ end
156
+ end
157
+
158
+ # The purpose of this class is to act as a unifier of the previous
159
+ # and current flash.
160
+ #
161
+ # Flash means pairs of keys and values that are held only over one
162
+ # request/response cycle. So you can assign a key/value in the current
163
+ # session and retrieve it in the current and following request.
164
+ #
165
+ # Please see the FlashHelper for details on the usage as you won't need
166
+ # to touch this class at all.
167
+
168
+ class SessionFlash
169
+
170
+ # the current session[:FLASH_PREVIOUS]
171
+
172
+ def previous
173
+ session[:FLASH_PREVIOUS] || {}
93
174
  end
94
175
 
95
- # tries to catch all methods and proxy them to the #current
96
- # this makes it easier to do i.e. hash-manipulation directly
97
- # on #session in the controller.
176
+ # the current session[:FLASH]
98
177
 
99
- def method_missing meth, *args, &block
100
- current.send(meth, *args, &block)
178
+ def current
179
+ session[:FLASH] ||= {}
101
180
  end
102
181
 
103
- # show the contents of the session without key/value of the cookie
182
+ # combined key/value pairs of previous and current
183
+ # current keys overshadow the old ones.
104
184
 
105
- def inspect
106
- tmp = current.clone
107
- tmp.delete SESSION_KEY
108
- tmp.inspect
185
+ def combined
186
+ previous.merge(current)
109
187
  end
110
188
 
111
- # show only the key/value of the cookie, useful for debugging.
189
+ def [](key)
190
+ combined[key]
191
+ end
112
192
 
113
- def export
114
- "#{SESSION_KEY}=#{session_id}"
193
+ def []=(key, value)
194
+ current[key] = value
115
195
  end
116
196
 
117
- # generate an unique #hash for the current session
197
+ def inspect
198
+ combined.inspect
199
+ end
118
200
 
119
- def hash
120
- h = [
121
- Time.now.to_f.to_s.reverse, rand,
122
- Thread.current[:request].hash, rand,
123
- Process.pid, rand,
124
- object_id, rand
125
- ].join
126
- Digest::SHA512.hexdigest(h)
201
+ private
202
+
203
+ def session
204
+ Ramaze::Session.current
127
205
  end
128
206
  end
129
207
  end