ramaze 0.0.7 → 0.0.8

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