merb-core 0.9.8 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +33 -0
- data/README +7 -3
- data/Rakefile +3 -3
- data/lib/merb-core.rb +165 -94
- data/lib/merb-core/bootloader.rb +469 -100
- data/lib/merb-core/config.rb +79 -3
- data/lib/merb-core/constants.rb +24 -2
- data/lib/merb-core/controller/abstract_controller.rb +172 -67
- data/lib/merb-core/controller/exceptions.rb +50 -6
- data/lib/merb-core/controller/merb_controller.rb +215 -108
- data/lib/merb-core/controller/mime.rb +36 -12
- data/lib/merb-core/controller/mixins/authentication.rb +52 -7
- data/lib/merb-core/controller/mixins/conditional_get.rb +14 -0
- data/lib/merb-core/controller/mixins/controller.rb +90 -58
- data/lib/merb-core/controller/mixins/render.rb +34 -10
- data/lib/merb-core/controller/mixins/responder.rb +40 -16
- data/lib/merb-core/controller/template.rb +37 -16
- data/lib/merb-core/core_ext/hash.rb +9 -0
- data/lib/merb-core/core_ext/kernel.rb +92 -41
- data/lib/merb-core/dispatch/dispatcher.rb +29 -45
- data/lib/merb-core/dispatch/request.rb +186 -82
- data/lib/merb-core/dispatch/router.rb +141 -53
- data/lib/merb-core/dispatch/router/behavior.rb +296 -139
- data/lib/merb-core/dispatch/router/resources.rb +51 -19
- data/lib/merb-core/dispatch/router/route.rb +76 -23
- data/lib/merb-core/dispatch/session.rb +80 -36
- data/lib/merb-core/dispatch/session/container.rb +31 -15
- data/lib/merb-core/dispatch/session/cookie.rb +51 -22
- data/lib/merb-core/dispatch/session/memcached.rb +10 -6
- data/lib/merb-core/dispatch/session/memory.rb +17 -5
- data/lib/merb-core/dispatch/session/store_container.rb +21 -9
- data/lib/merb-core/dispatch/worker.rb +16 -2
- data/lib/merb-core/gem_ext/erubis.rb +4 -0
- data/lib/merb-core/plugins.rb +13 -0
- data/lib/merb-core/rack.rb +1 -0
- data/lib/merb-core/rack/adapter.rb +1 -0
- data/lib/merb-core/rack/adapter/abstract.rb +95 -17
- data/lib/merb-core/rack/adapter/irb.rb +50 -5
- data/lib/merb-core/rack/application.rb +27 -5
- data/lib/merb-core/rack/handler/mongrel.rb +6 -6
- data/lib/merb-core/rack/helpers.rb +33 -0
- data/lib/merb-core/rack/middleware/conditional_get.rb +1 -1
- data/lib/merb-core/rack/middleware/path_prefix.rb +3 -3
- data/lib/merb-core/rack/middleware/static.rb +11 -7
- data/lib/merb-core/server.rb +134 -69
- data/lib/merb-core/tasks/gem_management.rb +153 -80
- data/lib/merb-core/tasks/merb_rake_helper.rb +12 -4
- data/lib/merb-core/tasks/stats.rake +1 -1
- data/lib/merb-core/test/helpers/mock_request_helper.rb +29 -22
- data/lib/merb-core/test/helpers/request_helper.rb +1 -1
- data/lib/merb-core/test/helpers/route_helper.rb +50 -4
- data/lib/merb-core/test/matchers/request_matchers.rb +2 -36
- data/lib/merb-core/test/matchers/view_matchers.rb +32 -22
- data/lib/merb-core/test/run_specs.rb +6 -5
- data/lib/merb-core/test/test_ext/rspec.rb +6 -19
- data/lib/merb-core/version.rb +1 -1
- metadata +5 -4
@@ -20,6 +20,8 @@ module Merb
|
|
20
20
|
# :member<Hash>:
|
21
21
|
# Special settings and resources related to a specific member of this
|
22
22
|
# resource.
|
23
|
+
# :identify<Symbol|Array>: The method(s) that should be called on the object
|
24
|
+
# before inserting it into an URL.
|
23
25
|
# :keys<Array>:
|
24
26
|
# A list of the keys to be used instead of :id with the resource in the order of the url.
|
25
27
|
# :singular<Symbol>
|
@@ -63,24 +65,40 @@ module Merb
|
|
63
65
|
# r.resources :posts do |posts|
|
64
66
|
# posts.resources :comments
|
65
67
|
# end
|
66
|
-
|
67
|
-
# @public
|
68
|
+
#
|
69
|
+
# @api public
|
68
70
|
def resources(name, *args, &block)
|
69
71
|
name = name.to_s
|
70
72
|
options = extract_options_from_args!(args) || {}
|
73
|
+
match_opts = options.except(*resource_options)
|
74
|
+
options = options.only(*resource_options)
|
71
75
|
singular = options[:singular] ? options[:singular].to_s : Extlib::Inflection.singularize(name)
|
72
|
-
|
73
|
-
|
76
|
+
klass_name = args.first ? args.first.to_s : Extlib::Inflection.classify(singular)
|
77
|
+
klass = Object.full_const_get(klass_name) rescue nil
|
78
|
+
keys = options.delete(:keys) || options.delete(:key)
|
74
79
|
params = { :controller => options.delete(:controller) || name }
|
75
80
|
collection = options.delete(:collection) || {}
|
76
81
|
member = { :edit => :get, :delete => :get }.merge(options.delete(:member) || {})
|
82
|
+
|
83
|
+
# Use the identifier for the class as a default
|
84
|
+
if klass
|
85
|
+
keys ||= options[:identify]
|
86
|
+
keys ||= @identifiers[klass]
|
87
|
+
elsif options[:identify]
|
88
|
+
raise Error, "The constant #{klass_name} does not exist, please specify the constant for this resource"
|
89
|
+
end
|
90
|
+
|
91
|
+
keys = [ keys || :id ].flatten
|
92
|
+
|
77
93
|
|
78
94
|
# Try pulling :namespace out of options for backwards compatibility
|
79
95
|
options[:name_prefix] ||= nil # Don't use a name_prefix if not needed
|
80
96
|
options[:resource_prefix] ||= nil # Don't use a resource_prefix if not needed
|
81
97
|
options[:controller_prefix] ||= options.delete(:namespace)
|
82
98
|
|
83
|
-
|
99
|
+
context = options[:identify]
|
100
|
+
context = klass && options[:identify] ? identify(klass => options.delete(:identify)) : self
|
101
|
+
context.namespace(name, options).to(params) do |resource|
|
84
102
|
root_keys = keys.map { |k| ":#{k}" }.join("/")
|
85
103
|
|
86
104
|
# => index
|
@@ -102,45 +120,48 @@ module Merb
|
|
102
120
|
end
|
103
121
|
|
104
122
|
# => show
|
105
|
-
resource.match("/#{root_keys}(.:format)", :method => :get).to(:action => "show").
|
106
|
-
name(singular).register_resource(
|
123
|
+
resource.match("/#{root_keys}(.:format)", match_opts.merge(:method => :get)).to(:action => "show").
|
124
|
+
name(singular).register_resource(klass_name)
|
107
125
|
|
108
126
|
# => user defined member routes
|
109
127
|
member.each_pair do |action, method|
|
110
128
|
action = action.to_s
|
111
|
-
resource.match("/#{root_keys}/#{action}(.:format)", :method => method).
|
112
|
-
to(:action => "#{action}").name(action, singular).register_resource(
|
129
|
+
resource.match("/#{root_keys}/#{action}(.:format)", match_opts.merge(:method => method)).
|
130
|
+
to(:action => "#{action}").name(action, singular).register_resource(klass_name, action)
|
113
131
|
end
|
114
132
|
|
115
133
|
# => update
|
116
|
-
resource.match("/#{root_keys}(.:format)", :method => :put).
|
134
|
+
resource.match("/#{root_keys}(.:format)", match_opts.merge(:method => :put)).
|
117
135
|
to(:action => "update")
|
118
136
|
|
119
137
|
# => destroy
|
120
|
-
resource.match("/#{root_keys}(.:format)", :method => :delete).
|
138
|
+
resource.match("/#{root_keys}(.:format)", match_opts.merge(:method => :delete)).
|
121
139
|
to(:action => "destroy")
|
122
140
|
|
123
141
|
if block_given?
|
124
142
|
nested_keys = keys.map do |k|
|
125
143
|
k.to_s == "id" ? ":#{singular}_id" : ":#{k}"
|
126
144
|
end.join("/")
|
145
|
+
|
146
|
+
nested_match_opts = match_opts.except(:id)
|
147
|
+
nested_match_opts["#{singular}_id".to_sym] = match_opts[:id] if match_opts[:id]
|
127
148
|
|
128
149
|
# Procs for building the extra collection/member resource routes
|
129
|
-
placeholder = Router.resource_routes[ [@options[:resource_prefix],
|
150
|
+
placeholder = Router.resource_routes[ [@options[:resource_prefix], klass_name].flatten.compact ]
|
130
151
|
builders = {}
|
131
152
|
|
132
153
|
builders[:collection] = lambda do |action, to, method|
|
133
|
-
resource.before(placeholder).match("/#{action}(.:format)", :method => method).
|
154
|
+
resource.before(placeholder).match("/#{action}(.:format)", match_opts.merge(:method => method)).
|
134
155
|
to(:action => to).name(action, name).register_resource(name, action)
|
135
156
|
end
|
136
157
|
|
137
158
|
builders[:member] = lambda do |action, to, method|
|
138
|
-
resource.match("/#{root_keys}/#{action}(.:format)", :method => method).
|
139
|
-
to(:action => to).name(action, singular).register_resource(
|
159
|
+
resource.match("/#{root_keys}/#{action}(.:format)", match_opts.merge(:method => method)).
|
160
|
+
to(:action => to).name(action, singular).register_resource(klass_name, action)
|
140
161
|
end
|
141
162
|
|
142
|
-
resource.options(:name_prefix => singular, :resource_prefix =>
|
143
|
-
match("/#{nested_keys}").resource_block(builders, &block)
|
163
|
+
resource.options(:name_prefix => singular, :resource_prefix => klass_name).
|
164
|
+
match("/#{nested_keys}", nested_match_opts).resource_block(builders, &block)
|
144
165
|
end
|
145
166
|
end # namespace
|
146
167
|
end # resources
|
@@ -196,8 +217,8 @@ module Merb
|
|
196
217
|
# r.resource :account, :namespace => "admin" do |account|
|
197
218
|
# account.resources :preferences, :controller => "settings"
|
198
219
|
# end
|
199
|
-
#
|
200
|
-
# @public
|
220
|
+
#
|
221
|
+
# @api public
|
201
222
|
def resource(name, *args, &block)
|
202
223
|
name = name.to_s
|
203
224
|
options = extract_options_from_args!(args) || {}
|
@@ -210,6 +231,7 @@ module Merb
|
|
210
231
|
|
211
232
|
self.namespace(name, options).to(params) do |resource|
|
212
233
|
# => show
|
234
|
+
|
213
235
|
resource.match("(.:format)", :method => :get).to(:action => "show").
|
214
236
|
name(name).register_resource(name)
|
215
237
|
|
@@ -244,16 +266,23 @@ module Merb
|
|
244
266
|
|
245
267
|
protected
|
246
268
|
|
269
|
+
#api private
|
247
270
|
def register_resource(*key)
|
248
271
|
key = [@options[:resource_prefix], key].flatten.compact
|
249
272
|
@route.resource = key
|
250
273
|
self
|
251
274
|
end
|
252
275
|
|
276
|
+
#api private
|
253
277
|
def resource_block(builders, &block)
|
254
278
|
behavior = ResourceBehavior.new(builders, @proxy, @conditions, @params, @defaults, @identifiers, @options, @blocks)
|
255
279
|
with_behavior_context(behavior, &block)
|
256
280
|
end
|
281
|
+
|
282
|
+
def resource_options
|
283
|
+
[:singular, :keys, :key, :controller, :member, :collection, :identify,
|
284
|
+
:name_prefix, :resource_prefix, :controller_prefix, :namespace, :path]
|
285
|
+
end
|
257
286
|
|
258
287
|
end # Resources
|
259
288
|
|
@@ -264,12 +293,14 @@ module Merb
|
|
264
293
|
# Adding the collection and member methods to behavior
|
265
294
|
class ResourceBehavior < Behavior #:nodoc:
|
266
295
|
|
296
|
+
#api private
|
267
297
|
def initialize(builders, *args)
|
268
298
|
super(*args)
|
269
299
|
@collection = builders[:collection]
|
270
300
|
@member = builders[:member]
|
271
301
|
end
|
272
302
|
|
303
|
+
#api private
|
273
304
|
def collection(action, options = {})
|
274
305
|
action = action.to_s
|
275
306
|
method = options[:method]
|
@@ -277,6 +308,7 @@ module Merb
|
|
277
308
|
@collection[action, to, method]
|
278
309
|
end
|
279
310
|
|
311
|
+
# @api private
|
280
312
|
def member(action, options = {})
|
281
313
|
action = action.to_s
|
282
314
|
method = options[:method]
|
@@ -79,21 +79,48 @@ module Merb
|
|
79
79
|
@name
|
80
80
|
end
|
81
81
|
|
82
|
-
#
|
82
|
+
# Generates the URL for the route given the passed arguments. The
|
83
|
+
# method will first match the anonymous parameters to route params
|
84
|
+
# and will convert all the parameters according to the specifed
|
85
|
+
# object identifiers.
|
86
|
+
#
|
87
|
+
# Then the named parameters are passed to a compiled generation proc.
|
88
|
+
#
|
89
|
+
# ==== Parameters
|
90
|
+
# args<Array>::
|
91
|
+
# The arguments passed to the public #url method with the name
|
92
|
+
# of the route removed. This is an array of the anonymous parameters
|
93
|
+
# followed by a hash containing the named parameters.
|
94
|
+
#
|
95
|
+
# defaults<Hash>::
|
96
|
+
# A hash of parameters to use to generate the route if there are
|
97
|
+
# any missing required parameters. This is usually the parameters
|
98
|
+
# for the current request
|
99
|
+
#
|
100
|
+
# ==== Returns
|
101
|
+
# String:: The generated URL.
|
83
102
|
def generate(args = [], defaults = {})
|
84
103
|
raise GenerationError, "Cannot generate regexp Routes" if regexp?
|
85
104
|
|
86
105
|
params = extract_options_from_args!(args) || { }
|
87
106
|
|
107
|
+
params.each do |k, v|
|
108
|
+
params[k] = identify(v, k)
|
109
|
+
end
|
110
|
+
|
88
111
|
# Support for anonymous params
|
89
112
|
unless args.empty?
|
90
113
|
# First, let's determine which variables are missing
|
91
114
|
variables = @variables - params.keys
|
92
115
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
116
|
+
args.each do |param|
|
117
|
+
raise GenerationError, "The route has #{@variables.length} variables: #{@variables.inspect}" if variables.empty?
|
118
|
+
|
119
|
+
if identifier = identifier_for(param) and identifier.is_a?(Array)
|
120
|
+
identifier.each { |ident| params[variables.shift] = param.send(ident) }
|
121
|
+
else
|
122
|
+
params[variables.shift] ||= identify(param)
|
123
|
+
end
|
97
124
|
end
|
98
125
|
end
|
99
126
|
|
@@ -101,7 +128,47 @@ module Merb
|
|
101
128
|
uri = Merb::Config[:path_prefix] + uri if Merb::Config[:path_prefix]
|
102
129
|
uri
|
103
130
|
end
|
131
|
+
|
132
|
+
# Identifies the object according to the identifiers set while building
|
133
|
+
# the routes. Identifying an object means picking an instance method to
|
134
|
+
# call on the object that will return a string representation of the
|
135
|
+
# object for the route being generated. If the identifier is an array,
|
136
|
+
# then a param_key must be present and match one of the elements of the
|
137
|
+
# identifier array.
|
138
|
+
#
|
139
|
+
# param_keys that end in _id are treated slightly differently in order
|
140
|
+
# to get nested resources to work correctly.
|
141
|
+
def identify(obj, param_key = nil)
|
142
|
+
identifier = identifier_for(obj)
|
143
|
+
if identifier.is_a?(Array)
|
144
|
+
# First check if the param_key exists as an identifier
|
145
|
+
return obj.send(param_key) if identifier.include?(param_key)
|
146
|
+
# If the param_key ends in _id, just return the object id
|
147
|
+
return obj.id if "#{param_key}" =~ /_id$/
|
148
|
+
# Otherwise, raise an error
|
149
|
+
raise GenerationError, "The object #{obj.inspect} cannot be identified with #{identifier.inspect} for #{param_key}"
|
150
|
+
else
|
151
|
+
identifier ? obj.send(identifier) : obj
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns the identifier for the passed object. Built in core ruby classes are
|
156
|
+
# always identified with to_s. The method will return nil in that case (since
|
157
|
+
# to_s is the default for objects that do not have identifiers.)
|
158
|
+
def identifier_for(obj)
|
159
|
+
return if obj.is_a?(String) || obj.is_a?(Symbol) || obj.is_a?(Numeric) ||
|
160
|
+
obj.is_a?(TrueClass) || obj.is_a?(FalseClass) || obj.is_a?(NilClass) ||
|
161
|
+
obj.is_a?(Array) || obj.is_a?(Hash)
|
162
|
+
|
163
|
+
@identifiers.each do |klass, identifier|
|
164
|
+
return identifier if obj.is_a?(klass)
|
165
|
+
end
|
166
|
+
|
167
|
+
return nil
|
168
|
+
end
|
104
169
|
|
170
|
+
# Returns the if statement and return value for for the main
|
171
|
+
# Router.match compiled method.
|
105
172
|
def compiled_statement(first)
|
106
173
|
els_if = first ? ' if ' : ' elsif '
|
107
174
|
|
@@ -110,13 +177,9 @@ module Merb
|
|
110
177
|
|
111
178
|
# First, we need to always return the value of the
|
112
179
|
# deferred block if it explicitly matched the route
|
113
|
-
if @redirects
|
114
|
-
code << " return [#{@index.inspect}, block_result] if request.matched?" << "\n"
|
115
|
-
code << "
|
116
|
-
code << " [#{@index.inspect}, { :url => #{@redirect_url.inspect}, :status => #{@redirect_status.inspect} }]" << "\n"
|
117
|
-
elsif @redirects
|
118
|
-
code << " request.redirects!" << "\n"
|
119
|
-
code << " [#{@index.inspect}, { :url => #{@redirect_url.inspect}, :status => #{@redirect_status.inspect} }]" << "\n"
|
180
|
+
if @redirects
|
181
|
+
code << " return [#{@index.inspect}, block_result] if request.matched?" << "\n" if @deferred_procs.any?
|
182
|
+
code << " [#{@index.inspect}, Merb::Rack::Helpers.redirect(#{@redirect_url.inspect}, :status => #{@redirect_status.inspect})]" << "\n"
|
120
183
|
elsif @deferred_procs.any?
|
121
184
|
code << " [#{@index.inspect}, block_result]" << "\n"
|
122
185
|
else
|
@@ -258,7 +321,7 @@ module Merb
|
|
258
321
|
segments.each_with_index do |segment, i|
|
259
322
|
bits << case
|
260
323
|
when segment.is_a?(String) then segment
|
261
|
-
when segment.is_a?(Symbol) then '#{
|
324
|
+
when segment.is_a?(Symbol) then '#{cached_' + segment.to_s + '}'
|
262
325
|
when segment.is_a?(Array) && segment.any? { |s| !s.is_a?(String) } then "\#{#{@opt_segment_stack.last.shift}}"
|
263
326
|
else ""
|
264
327
|
end
|
@@ -267,16 +330,6 @@ module Merb
|
|
267
330
|
bits
|
268
331
|
end
|
269
332
|
|
270
|
-
def param_for_route(param)
|
271
|
-
case param
|
272
|
-
when String, Symbol, Numeric, TrueClass, FalseClass, NilClass
|
273
|
-
param
|
274
|
-
else
|
275
|
-
_, identifier = @identifiers.find { |klass, _| param.is_a?(klass) }
|
276
|
-
identifier ? param.send(identifier) : param
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
333
|
end
|
281
334
|
|
282
335
|
# === Conditions ===
|
@@ -6,6 +6,8 @@ module Merb
|
|
6
6
|
# Returns stores list constructed from
|
7
7
|
# configured session stores (:session_stores config option)
|
8
8
|
# or default one (:session_store config option).
|
9
|
+
#
|
10
|
+
# @api private
|
9
11
|
def self.session_stores
|
10
12
|
@session_stores ||= begin
|
11
13
|
config_stores = Array(
|
@@ -15,7 +17,7 @@ module Merb
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
end # Config
|
18
|
-
|
20
|
+
|
19
21
|
# The Merb::Session module gets mixed into Merb::SessionContainer to allow
|
20
22
|
# app-level functionality (usually found in app/models/merb/session.rb) for
|
21
23
|
# session.
|
@@ -23,35 +25,37 @@ module Merb
|
|
23
25
|
# You can use this module to implement additional methods to simplify
|
24
26
|
# building wizard-like application components,
|
25
27
|
# authentication frameworks, etc.
|
28
|
+
#
|
29
|
+
# Session configuration options:
|
30
|
+
#
|
31
|
+
# :session_id_key The key by which a session value/id is
|
32
|
+
# retrieved; defaults to _session_id
|
33
|
+
#
|
34
|
+
# :session_expiry When to expire the session cookie;
|
35
|
+
# by defaults session expires when browser quits.
|
36
|
+
#
|
37
|
+
# :session_secret_key A secret string which is used to sign/validate
|
38
|
+
# session data; min. 16 chars
|
39
|
+
#
|
40
|
+
# :default_cookie_domain The default domain to write cookies for.
|
26
41
|
module Session
|
27
42
|
end
|
28
|
-
|
43
|
+
|
29
44
|
# This is mixed into Merb::Controller on framework boot.
|
30
45
|
module SessionMixin
|
31
46
|
# Raised when no suitable session store has been setup.
|
32
47
|
class NoSessionContainer < StandardError; end
|
33
|
-
|
48
|
+
|
34
49
|
# Raised when storing more data than the available space reserved.
|
35
50
|
class SessionOverflow < StandardError; end
|
36
|
-
|
37
|
-
#
|
38
|
-
#
|
39
|
-
# :session_id_key The key by which a session value/id is
|
40
|
-
# retrieved; defaults to _session_id
|
41
|
-
#
|
42
|
-
# :session_expiry When to expire the session cookie;
|
43
|
-
# defaults to 2 weeks
|
44
|
-
#
|
45
|
-
# :session_secret_key A secret string which is used to sign/validate
|
46
|
-
# session data; min. 16 chars
|
47
|
-
#
|
48
|
-
# :default_cookie_domain The default domain to write cookies for.
|
51
|
+
|
52
|
+
# @api private
|
49
53
|
def self.included(base)
|
50
54
|
# Register a callback to finalize sessions - needs to run before the cookie
|
51
55
|
# callback extracts Set-Cookie headers from request.cookies.
|
52
56
|
base._after_dispatch_callbacks.unshift lambda { |c| c.request.finalize_session }
|
53
57
|
end
|
54
|
-
|
58
|
+
|
55
59
|
# ==== Parameters
|
56
60
|
# session_store<String>:: The type of session store to access.
|
57
61
|
#
|
@@ -60,9 +64,9 @@ module Merb
|
|
60
64
|
def session(session_store = nil)
|
61
65
|
request.session(session_store)
|
62
66
|
end
|
63
|
-
|
67
|
+
|
64
68
|
# Module methods
|
65
|
-
|
69
|
+
|
66
70
|
# ==== Returns
|
67
71
|
# String:: A random 32 character string for use as a unique session ID.
|
68
72
|
def rand_uuid
|
@@ -77,59 +81,76 @@ module Merb
|
|
77
81
|
]
|
78
82
|
"%04x%04x%04x%04x%04x%06x%06x" % values
|
79
83
|
end
|
80
|
-
|
84
|
+
|
81
85
|
# Marks this session as needing a new cookie.
|
86
|
+
#
|
87
|
+
# @api private
|
82
88
|
def needs_new_cookie!
|
83
89
|
@_new_cookie = true
|
84
90
|
end
|
85
|
-
|
91
|
+
|
92
|
+
# Does session need new cookie?
|
93
|
+
#
|
94
|
+
# ==== Returns
|
95
|
+
# Boolean:: true if a new cookie is needed, false otherwise.
|
96
|
+
#
|
97
|
+
# @api private
|
86
98
|
def needs_new_cookie?
|
87
99
|
@_new_cookie
|
88
100
|
end
|
89
|
-
|
101
|
+
|
90
102
|
module_function :rand_uuid, :needs_new_cookie!, :needs_new_cookie?
|
91
|
-
|
103
|
+
|
92
104
|
module RequestMixin
|
93
|
-
|
105
|
+
|
106
|
+
# Adds class methods to Merb::Request object.
|
107
|
+
# Sets up repository of session store types.
|
108
|
+
# Sets the session ID key and expiry values.
|
94
109
|
def self.included(base)
|
95
110
|
base.extend ClassMethods
|
96
|
-
|
111
|
+
|
97
112
|
# Keep track of all known session store types.
|
98
113
|
base.cattr_accessor :registered_session_types
|
99
114
|
base.registered_session_types = Dictionary.new
|
100
115
|
base.class_inheritable_accessor :_session_id_key, :_session_secret_key,
|
101
116
|
:_session_expiry
|
102
|
-
|
117
|
+
|
103
118
|
base._session_id_key = Merb::Config[:session_id_key] || '_session_id'
|
104
119
|
base._session_expiry = Merb::Config[:session_expiry] || 0
|
105
120
|
base._session_secret_key = Merb::Config[:session_secret_key]
|
106
121
|
end
|
107
|
-
|
122
|
+
|
108
123
|
module ClassMethods
|
109
|
-
|
124
|
+
|
110
125
|
# ==== Parameters
|
111
126
|
# name<~to_sym>:: Name of the session type to register.
|
112
127
|
# class_name<String>:: The corresponding class name.
|
113
128
|
#
|
114
129
|
# === Notres
|
115
130
|
# This is automatically called when Merb::SessionContainer is subclassed.
|
131
|
+
#
|
132
|
+
# @api private
|
116
133
|
def register_session_type(name, class_name)
|
117
134
|
self.registered_session_types[name.to_sym] = class_name
|
118
135
|
end
|
119
|
-
|
136
|
+
|
120
137
|
end
|
121
|
-
|
138
|
+
|
122
139
|
# The default session store type.
|
140
|
+
#
|
141
|
+
# @api private
|
123
142
|
def default_session_store
|
124
143
|
Merb::Config[:session_store] && Merb::Config[:session_store].to_sym
|
125
144
|
end
|
126
|
-
|
145
|
+
|
127
146
|
# ==== Returns
|
128
147
|
# Hash:: All active session stores by type.
|
148
|
+
#
|
149
|
+
# @api private
|
129
150
|
def session_stores
|
130
151
|
@session_stores ||= {}
|
131
152
|
end
|
132
|
-
|
153
|
+
|
133
154
|
# Returns session container. Merb is able to handle multiple session
|
134
155
|
# stores, hence a parameter to pick it.
|
135
156
|
#
|
@@ -140,6 +161,12 @@ module Merb
|
|
140
161
|
# === Notes
|
141
162
|
# If no suitable session store type is given, it defaults to
|
142
163
|
# cookie-based sessions.
|
164
|
+
#
|
165
|
+
# ==== Returns
|
166
|
+
# SessionContainer::
|
167
|
+
# an instance of a session store extending Merb::SessionContainer.
|
168
|
+
#
|
169
|
+
# @api public
|
143
170
|
def session(session_store = nil)
|
144
171
|
session_store ||= default_session_store
|
145
172
|
if class_name = self.class.registered_session_types[session_store]
|
@@ -154,12 +181,14 @@ module Merb
|
|
154
181
|
raise NoSessionContainer, msg
|
155
182
|
end
|
156
183
|
end
|
157
|
-
|
184
|
+
|
158
185
|
# ==== Parameters
|
159
186
|
# new_session<Merb::SessionContainer>:: A session store instance.
|
160
187
|
#
|
161
188
|
# === Notes
|
162
189
|
# The session is assigned internally by its session_store_type key.
|
190
|
+
#
|
191
|
+
# @api private
|
163
192
|
def session=(new_session)
|
164
193
|
if self.session?(new_session.class.session_store_type)
|
165
194
|
original_session_id = self.session(new_session.class.session_store_type).session_id
|
@@ -169,21 +198,30 @@ module Merb
|
|
169
198
|
end
|
170
199
|
session_stores[new_session.class.session_store_type] = new_session
|
171
200
|
end
|
172
|
-
|
201
|
+
|
173
202
|
# Whether a session has been setup
|
203
|
+
#
|
204
|
+
# ==== Returns
|
205
|
+
# Boolean:: true if the session is part of the session stores configured.
|
206
|
+
#
|
207
|
+
# @api private
|
174
208
|
def session?(session_store = nil)
|
175
209
|
(session_store ? [session_store] : session_stores).any? do |type, store|
|
176
210
|
store.is_a?(Merb::SessionContainer)
|
177
211
|
end
|
178
212
|
end
|
179
|
-
|
213
|
+
|
180
214
|
# Teardown and/or persist the current sessions.
|
215
|
+
#
|
216
|
+
# @api private
|
181
217
|
def finalize_session
|
182
218
|
session_stores.each { |name, store| store.finalize(self) }
|
183
219
|
end
|
184
220
|
alias :finalize_sessions :finalize_session
|
185
|
-
|
221
|
+
|
186
222
|
# Assign default cookie values
|
223
|
+
#
|
224
|
+
# @api private
|
187
225
|
def default_cookies
|
188
226
|
defaults = {}
|
189
227
|
if route && route.allow_fixation? && params.key?(_session_id_key)
|
@@ -198,6 +236,8 @@ module Merb
|
|
198
236
|
# ==== Parameters
|
199
237
|
# value<String>:: The value of the session cookie; either the session id or the actual encoded data.
|
200
238
|
# options<Hash>:: Cookie options like domain, path and expired.
|
239
|
+
#
|
240
|
+
# @api private
|
201
241
|
def set_session_cookie_value(value, options = {})
|
202
242
|
defaults = {}
|
203
243
|
defaults[:expires] = Time.now + _session_expiry if _session_expiry > 0
|
@@ -207,12 +247,16 @@ module Merb
|
|
207
247
|
|
208
248
|
# ==== Returns
|
209
249
|
# String:: The value of the session cookie; either the session id or the actual encoded data.
|
250
|
+
#
|
251
|
+
# @api private
|
210
252
|
def session_cookie_value
|
211
253
|
cookies[_session_id_key]
|
212
254
|
end
|
213
255
|
alias :session_id :session_cookie_value
|
214
256
|
|
215
257
|
# Destroy the session cookie.
|
258
|
+
#
|
259
|
+
# @api private
|
216
260
|
def destroy_session_cookie
|
217
261
|
cookies.delete(_session_id_key)
|
218
262
|
end
|