merb-core 0.9.6 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/CHANGELOG +67 -0
  2. data/CONTRIBUTORS +1 -0
  3. data/PUBLIC_CHANGELOG +34 -0
  4. data/Rakefile +7 -4
  5. data/bin/merb +1 -31
  6. data/lib/merb-core.rb +17 -6
  7. data/lib/merb-core/config.rb +0 -7
  8. data/lib/merb-core/controller/abstract_controller.rb +6 -3
  9. data/lib/merb-core/dispatch/cookies.rb +11 -10
  10. data/lib/merb-core/dispatch/dispatcher.rb +11 -5
  11. data/lib/merb-core/dispatch/request.rb +7 -2
  12. data/lib/merb-core/dispatch/session.rb +33 -17
  13. data/lib/merb-core/dispatch/session/container.rb +19 -9
  14. data/lib/merb-core/dispatch/session/cookie.rb +27 -12
  15. data/lib/merb-core/dispatch/session/memcached.rb +47 -27
  16. data/lib/merb-core/dispatch/session/memory.rb +10 -6
  17. data/lib/merb-core/dispatch/session/store_container.rb +25 -20
  18. data/lib/merb-core/test/helpers/request_helper.rb +6 -3
  19. data/lib/merb-core/test/helpers/route_helper.rb +1 -1
  20. data/lib/merb-core/test/matchers/view_matchers.rb +5 -1
  21. data/lib/merb-core/version.rb +1 -1
  22. data/spec/private/dispatch/fixture/log/merb_test.log +144 -0
  23. data/spec/private/router/fixture/log/merb_test.log +16 -0
  24. data/spec/public/controller/controllers/cookies.rb +14 -3
  25. data/spec/public/controller/cookies_spec.rb +53 -10
  26. data/spec/public/controller/url_spec.rb +6 -0
  27. data/spec/public/directory_structure/directory/log/merb_test.log +112 -0
  28. data/spec/public/reloading/directory/log/merb_test.log +16 -0
  29. data/spec/public/request/request_spec.rb +19 -10
  30. data/spec/public/router/fixture/log/merb_test.log +224 -0
  31. data/spec/public/session/controllers/sessions.rb +4 -0
  32. data/spec/public/session/memcached_session_spec.rb +2 -2
  33. data/spec/public/session/multiple_sessions_spec.rb +2 -2
  34. data/spec/public/session/session_spec.rb +15 -0
  35. data/spec/public/test/request_helper_spec.rb +21 -0
  36. data/spec/public/test/route_helper_spec.rb +7 -0
  37. metadata +6 -17
  38. data/lib/merb-core/script.rb +0 -112
data/CHANGELOG CHANGED
@@ -1,3 +1,70 @@
1
+ == 0.9.7 "Universe In A Bundle" 2008-09-13
2
+
3
+ * Made the post body available to the routing when testing a request.
4
+ * Better local gems dir detection and end-user feedback
5
+ * Updated PUBLIC_CHANGELOG regarding gem management and merb.thor
6
+ * Fixed compatibility with the *new* bundle logic
7
+ * Made request('/path', {}, {:post_body => 'some XML'}) not setting the post body to nil.
8
+ * Added two specs for setting request.raw_post. Passes for #dispatch_to, fails for #request.
9
+ * You can now use request_to with a post body:
10
+ * It's official: Thor is now a dependency
11
+ * Removed MerbScriptHelper - simplified loading bundled gems - see merb.thor
12
+ * Bug Fix: Cookie headers not being formatted correctly
13
+ * Added request.session.clear! method to clear and destroy the session (including the _session_id cookie itself)
14
+ * Both memcache-client and memcached gems are supported by the session store
15
+ * Added better query param parsing (naive but adequate) for nested params
16
+ * Added specs to make sure blank cookie options aren't used for Set-Cookie
17
+ * Fixed cookie issues in WebKit/Safari browsers
18
+ * Log ControllerExceptions with error level and only ServerErrors with info.
19
+ * Modified absolute_url to handle an object as well as a Hash
20
+ * Touches to new sessions: doc and minor code improvements.
21
+ * More meaningful exception message when no session container is configured.
22
+ * Make it clear how session mixin makes it's way into controller.
23
+ * Leave a note where new sessions doc needs to be improved.
24
+ * Fix smart formatting.
25
+ * Give smart people proper credit when you use their work.
26
+ * Loosen extlib dependency a bit.
27
+ * Meaningful message when Memcached session store can't be loaded because of load error.
28
+ * Remove libxml-ruby and memcache-client dev dependencies.
29
+ * Require memcached gem where memcached session store is defined (it's lazy loaded).
30
+ * Meaninful message when have_xpath matcher is used but libxml-ruby fails to load.
31
+ * Don't blow up when there are no system paths.
32
+
33
+ == 0.9.6 "Therapy session" 2008-09-08
34
+
35
+ * Merge in simple conditional get support at controller level.
36
+ * Merged in new bundling (aka freezer) branch
37
+ * Merged in new-sessions branch
38
+ * Simplify one more clever line.
39
+ * Trenary operator is always hard to read.
40
+ * Added PUBLIC_CHANGELOG note on Language::English::Inflector => English::Inflect
41
+ * Filters with procs created via class methods have identical signatures regardless if they handle content differently or not. So modified add_filter to just append procs to the filter list.
42
+ * Consolidating raw Rakefile commands to merb-core/tasks/merb_rake_helper.rb
43
+ * Update contributors list.
44
+ * Ticket #461 - This simply adds output for what host and port the adapter has started on.
45
+ * More Language::English::Inflect to English::Inflect changes - getting ready for Extlib move soon
46
+ * Language::English::Inflect => English::Inflect name changes
47
+ * Use frozen strings where possible.
48
+ * First pass at adding in CSRF protection in to Rack middleware.
49
+ * Query string of the format "foo=bar&foo=baz" should return params {"foo" => "baz"}
50
+ * Fixed multiple select not honored in params bug
51
+ * Public specs for 'fragment' changes in url.
52
+ * AbstractController now uniformly uses instance_eval for Procs where previously
53
+ * Renamed anchor to fragment along with some minor tweeks to follow rfc2396 better.
54
+ * Add support for :anchor when generating url's. Ex. url(:root, :anchor => :lower_half).
55
+ * Revert "Make Merb::Request#protocol return valid protocol names (http, not http://)."
56
+ * Adds specs for previous commit
57
+ * Fixes display @object, :template => "path/to/foo"
58
+ * Clean up Rakefile.
59
+ * Remove a line of extra code
60
+ * Set cookie expires to nil when session_expiry is set to 0.
61
+ * ConditionalGet refactoring.
62
+ * Fix: ConditionalGet should not return the message body when the status code is 304.
63
+ * Rescue Exception subclasses, not only StandardError subclasses.
64
+ * Make Merb::Request#protocol return valid protocol names (http, not http://).
65
+ * Add :protocol and :host options to absolute_url
66
+ * extends basic authentication a bit to allow usage outside of before filters
67
+
1
68
  == 0.9.5 "Knife and Spoons" 2008-26-08
2
69
  * Add Hpricot to dependencies: provided RSpec matchers depend on it.
3
70
  * Documentation fixes
@@ -63,6 +63,7 @@ Michael Sheakoski
63
63
  Mirko Froehlich
64
64
  Nathan Weizenbaum
65
65
  Oliver Jakubiec
66
+ Paul Barry
66
67
  Paul Boone
67
68
  Paul Carey
68
69
  Ray Morgan
@@ -1,3 +1,37 @@
1
+ 9/13/2008:
2
+ * Merb apps will always give priority to gems that are available locally in
3
+ Merb.root / gems. Because of the specific load order, you will need to use
4
+ bin/merb to load merb-core from local gems, as detailed below. This is also
5
+ the case for bin/merb-gen, bin/rake and bin/spec for example. The added
6
+ advantage is that your app will be completely independent from system gems.
7
+
8
+ * Thor tasks 'merb.thor' have been added for newly generated apps; regenerate
9
+ your app to get them; alternatively these are available on merbivore.com:
10
+
11
+ http://merbivore.com/merb.thor
12
+
13
+ * Release 0.9.6 introduced 'merb-gen scripts' which added script/merb and
14
+ script/merb-gen. However, now that merb.thor provides tasks to manage
15
+ bundled gems, we can directly extract the correct executables. To follow the
16
+ standard convention, these will be installed in ./bin instead of ./script.
17
+
18
+ With merb.thor installed, run the following to get started:
19
+
20
+ $ thor merb:tasks:setup # adds bin/thor, bin/rake etc.
21
+
22
+ As soon as you install other gems using merb.thor you'll have the required
23
+ bin executables available; these are setup so that running them will load
24
+ merb-core from the local gems dir, not from the system-wide rubygems.
25
+
26
+ To get bin/merb and bin/merb-gen for a fresh application, you can use:
27
+
28
+ $ thor merb:stable -a mongrel # install a full merb stack from stable rubygems
29
+
30
+ Alternatively, you can install from the bleeding edge:
31
+
32
+ $ thor merb:edge --install # install a full merb stack from github
33
+ $ thor merb:gems:install mongrel # or ebb, thin...
34
+
1
35
  9/5/2008:
2
36
  * Language::English::Inflector is now English::Inflect - be sure to change your
3
37
  custom inflections in config/init.rb. Additionally, the merb-gen template
data/Rakefile CHANGED
@@ -58,16 +58,19 @@ spec = Gem::Specification.new do |s|
58
58
  s.extra_rdoc_files = %w( README LICENSE TODO )
59
59
 
60
60
  # Dependencies
61
- s.add_dependency "extlib", ">= 0.9.5"
61
+ s.add_dependency "extlib", ">= 0.9.6"
62
62
  s.add_dependency "erubis"
63
63
  s.add_dependency "rake"
64
64
  s.add_dependency "json_pure"
65
65
  s.add_dependency "rspec"
66
66
  s.add_dependency "rack"
67
67
  s.add_dependency "mime-types"
68
- s.add_dependency "hpricot"
69
- s.add_development_dependency "libxml-ruby"
70
- s.add_development_dependency "memcache-client"
68
+ s.add_dependency "hpricot"
69
+ s.add_dependency "thor", ">= 0.9.6"
70
+ # this escalates to "regular" dependencies, comment it out
71
+ # for now. RubyGems need some love.
72
+ #s.add_development_dependency "libxml-ruby"
73
+ #s.add_development_dependency "memcache-client"
71
74
  # Requirements
72
75
  s.requirements << "install the json gem to get faster json parsing"
73
76
  s.required_ruby_version = ">= 1.8.6"
data/bin/merb CHANGED
@@ -1,36 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # See also: merb-gen scripts and script/merb
4
-
5
- # Try to use minigems instead of the fully rubygems library
6
- begin
7
- require 'minigems'
8
- rescue LoadError
9
- require 'rubygems'
10
- end
11
-
12
- # Load script helpers if available - either local or system-wide.
13
- begin
14
- # Figure out the merb root - defaults to the current directory.
15
- root_key = %w[-m --merb-root].detect { |o| ARGV.index(o) }
16
- root = ARGV[ARGV.index(root_key) + 1] if root_key
17
- __DIR__ = root.to_a.empty? ? Dir.getwd : root
18
-
19
- # Piggyback on the merb-core rubygem for initial setup scripts.
20
- # Requiring it doesn't affect the local gem version of merb-core
21
- # we might effectively want to load here after.
22
- if merb_core_dir = Dir[File.join(__DIR__, 'gems', 'gems', 'merb-core-*')].last
23
- require File.join(merb_core_dir, 'lib', 'merb-core', 'script')
24
- else
25
- require 'merb-core/script'
26
- end
27
- # Now setup local gems to be incorporated into the normal loaded gems.
28
- # Unless the option --no-bundle is given, bundled gems are enabled.
29
- include Merb::ScriptHelpers
30
- setup_local_gems!(__DIR__)
31
- rescue LoadError
32
- end
33
-
3
+ require 'rubygems'
34
4
  require 'merb-core'
35
5
 
36
6
  ARGV.push '-H' if ARGV[0] && ARGV[0] =~ /^[^-]/
@@ -1,6 +1,21 @@
1
1
  #---
2
2
  # require 'merb' must happen after Merb::Config is instantiated
3
3
  require 'rubygems'
4
+
5
+ # Add the local gems dir if found within the app root; any dependencies loaded
6
+ # hereafter will try to load from the local gems before loading system gems.
7
+ root_key = %w[-m --merb-root].detect { |o| ARGV.index(o) }
8
+ root = ARGV[ARGV.index(root_key) + 1] if root_key
9
+ root = root.to_a.empty? ? Dir.getwd : root
10
+ if File.directory?(gems_dir = File.join(root, 'gems'))
11
+ $BUNDLE = true; Gem.clear_paths; Gem.path.unshift(gems_dir)
12
+ # Warn if local merb-core is available but not loaded.
13
+ if !($0 =~ /^(\.\/)?bin\/merb$/) &&
14
+ (local_mc = Dir[File.join(gems_dir, 'specifications', 'merb-core-*.gemspec')].last)
15
+ puts "Warning: please use bin/merb to load #{File.basename(local_mc, '.gemspec')} from ./gems"
16
+ end
17
+ end
18
+
4
19
  require 'set'
5
20
  require 'fileutils'
6
21
  require 'socket'
@@ -356,12 +371,8 @@ module Merb
356
371
 
357
372
  Merb.klass_hashes = []
358
373
 
359
- attr_accessor :frozen
360
-
361
374
  # ==== Returns
362
375
  # Boolean:: True if Merb is running as an application with bundled gems.
363
- # Can only be disabled by --no-bundle option on startup (or for Rakefile
364
- # use NO_BUNDLE=true to disable local gems).
365
376
  #
366
377
  # ==== Notes
367
378
  # Bundling required gems makes your application independent from the
@@ -369,7 +380,7 @@ module Merb
369
380
  # framework and gems it uses and very useful when application is run in
370
381
  # some sort of sandbox, for instance, shared hosting with preconfigured gems.
371
382
  def bundled?
372
- ENV.key?("BUNDLE") || Merb::Config[:bundle] || ENV.key?("NO_BUNDLE")
383
+ $BUNDLE || ENV.key?("BUNDLE")
373
384
  end
374
385
 
375
386
  # Load configuration and assign logger.
@@ -577,4 +588,4 @@ require 'merb-core/version'
577
588
  require 'merb-core/controller/mime'
578
589
 
579
590
  # Set the environment if it hasn't already been set.
580
- Merb.environment ||= ENV['MERB_ENV'] || Merb::Config[:environment] || (Merb.testing? ? 'test' : 'development')
591
+ Merb.environment ||= ENV['MERB_ENV'] || Merb::Config[:environment] || (Merb.testing? ? 'test' : 'development')
@@ -116,9 +116,6 @@ module Merb
116
116
  # Environment variables always win
117
117
  options[:environment] = ENV["MERB_ENV"] if ENV["MERB_ENV"]
118
118
 
119
- # Enable bundled gems by default; used by bundled?
120
- options[:bundle] = true
121
-
122
119
  # Build a parser for the command line arguments
123
120
  opts = OptionParser.new do |opts|
124
121
  opts.version = Merb::VERSION
@@ -242,10 +239,6 @@ module Merb
242
239
  options[:verbose] = true
243
240
  end
244
241
 
245
- opts.on("-B", "--[no-]bundle", "Run application using bundled gems. Enabled by default.") do |b|
246
- options[:bundle] = b
247
- end
248
-
249
242
  opts.on("-?", "-H", "--help", "Show this help message") do
250
243
  puts opts
251
244
  exit
@@ -461,11 +461,14 @@ class Merb::AbstractController
461
461
  def absolute_url(name, rparams={})
462
462
  # FIXME: arrgh, why request.protocol returns http://?
463
463
  # :// is not part of protocol name
464
- protocol = rparams.delete(:protocol)
465
- protocol << "://" if protocol
464
+ if rparams.is_a?(Hash)
465
+ protocol = rparams.delete(:protocol)
466
+ protocol << "://" if protocol
467
+ host = rparams.delete(:host)
468
+ end
466
469
 
467
470
  (protocol || request.protocol) +
468
- (rparams.delete(:host) || request.host) +
471
+ (host || request.host) +
469
472
  url(name, rparams)
470
473
  end
471
474
 
@@ -2,9 +2,9 @@ module Merb
2
2
 
3
3
  class Cookies < Mash
4
4
 
5
- def initialize(constructor = {}, cookie_defaults = {})
6
- @_options_lookup = Mash.new
7
- @_cookie_defaults = cookie_defaults
5
+ def initialize(constructor = {})
6
+ @_options_lookup = Mash.new
7
+ @_cookie_defaults = { "domain" => Merb::Controller._default_cookie_domain, "path" => '/' }
8
8
  super constructor
9
9
  end
10
10
 
@@ -50,7 +50,7 @@ module Merb
50
50
  # name<~to_s>:: Name of the cookie to delete.
51
51
  # options<Hash>:: Additional options to pass to +set_cookie+.
52
52
  def delete(name, options = {})
53
- set_cookie(name, "", options.merge(:expires => Time.at(0)))
53
+ set_cookie(name, "", options.merge("expires" => Time.at(0)))
54
54
  end
55
55
 
56
56
  # Generate any necessary headers.
@@ -64,12 +64,13 @@ module Merb
64
64
  # Only set cookies that marked for inclusion in the response header.
65
65
  next unless @_options_lookup[name]
66
66
  options = defaults.merge(@_options_lookup[name])
67
- if (expiry = options[:expires]).respond_to?(:gmtime)
68
- options[:expires] = expiry.gmtime.strftime(Merb::Const::COOKIE_EXPIRATION_FORMAT)
67
+ if (expiry = options["expires"]).respond_to?(:gmtime)
68
+ options["expires"] = expiry.gmtime.strftime(Merb::Const::COOKIE_EXPIRATION_FORMAT)
69
69
  end
70
- secure = options.delete(:secure)
70
+ secure = options.delete("secure")
71
71
  kookie = "#{name}=#{Merb::Request.escape(value)}; "
72
- options.each { |k, v| kookie << "#{k}=#{v}; " }
72
+ # WebKit in particular doens't like empty cookie options - skip them.
73
+ options.each { |k, v| kookie << "#{k}=#{v}; " unless v.blank? }
73
74
  kookie << 'secure' if secure
74
75
  cookies << kookie.rstrip
75
76
  end
@@ -87,7 +88,7 @@ module Merb
87
88
 
88
89
  # Add a callback to enable Set-Cookie headers
89
90
  base._after_dispatch_callbacks << lambda do |c|
90
- headers = c.request.cookies.extract_headers(:domain => c._default_cookie_domain)
91
+ headers = c.request.cookies.extract_headers("domain" => c._default_cookie_domain)
91
92
  c.headers.update(headers)
92
93
  end
93
94
  end
@@ -116,7 +117,7 @@ module Merb
116
117
  def cookies
117
118
  @cookies ||= begin
118
119
  values = self.class.query_parse(@env[Merb::Const::HTTP_COOKIE], ';,')
119
- cookies = Merb::Cookies.new(values, :domain => Merb::Controller._default_cookie_domain, :path => '/')
120
+ cookies = Merb::Cookies.new(values)
120
121
  cookies.update(default_cookies) if respond_to?(:default_cookies)
121
122
  cookies
122
123
  end
@@ -139,12 +139,18 @@ module Merb
139
139
  # Exceptions::
140
140
  # The Merb::Controller that was dispatched to.
141
141
  def dispatch_exception(exception)
142
- Merb.logger.error(Merb.exception(exception))
142
+ if(exception.is_a?(Merb::ControllerExceptions::Base) &&
143
+ !exception.is_a?(Merb::ControllerExceptions::ServerError))
144
+ Merb.logger.info(Merb.exception(exception))
145
+ else
146
+ Merb.logger.error(Merb.exception(exception))
147
+ end
148
+
143
149
  self.exceptions = [exception]
144
-
150
+
145
151
  begin
146
152
  e = exceptions.first
147
-
153
+
148
154
  if action_name = e.action_name
149
155
  dispatch_action(Exceptions, action_name, e.class.status)
150
156
  else
@@ -156,11 +162,11 @@ module Merb
156
162
  else
157
163
  Merb.logger.error("Dispatching #{e.class} raised another error.")
158
164
  Merb.logger.error(Merb.exception(dispatch_issue))
159
-
165
+
160
166
  exceptions.unshift dispatch_issue
161
167
  retry
162
168
  end
163
169
  end
164
170
  end
165
171
  end
166
- end
172
+ end
@@ -652,9 +652,14 @@ module Merb
652
652
  parms[key] = val
653
653
  elsif after == "[]"
654
654
  (parms[key] ||= []) << val
655
- elsif after =~ %r(^\[\])
655
+ elsif after =~ %r(^\[\]\[([^\[\]]+)\]$)
656
+ child_key = $1
656
657
  parms[key] ||= []
657
- parms[key] << normalize_params({}, after, val)
658
+ if parms[key].last.is_a?(Hash) && !parms[key].last.key?(child_key)
659
+ parms[key].last.update(child_key => val)
660
+ else
661
+ parms[key] << { child_key => val }
662
+ end
658
663
  else
659
664
  parms[key] ||= {}
660
665
  parms[key] = normalize_params(parms[key], after, val)
@@ -2,10 +2,10 @@ require 'merb-core/dispatch/session/container'
2
2
  require 'merb-core/dispatch/session/store_container'
3
3
 
4
4
  module Merb
5
-
6
5
  class Config
7
-
8
- # List of all session_stores taken from :session_stores or :session_store
6
+ # Returns stores list constructed from
7
+ # configured session stores (:session_stores config option)
8
+ # or default one (:session_store config option).
9
9
  def self.session_stores
10
10
  @session_stores ||= begin
11
11
  config_stores = Array(
@@ -14,16 +14,20 @@ module Merb
14
14
  config_stores.map { |name| name.to_sym }
15
15
  end
16
16
  end
17
-
18
- end
17
+ end # Config
19
18
 
20
19
  # The Merb::Session module gets mixed into Merb::SessionContainer to allow
21
- # app-level functionality (usually found in app/models/merb/session.rb)
20
+ # app-level functionality (usually found in app/models/merb/session.rb) for
21
+ # session.
22
+ #
23
+ # You can use this module to implement additional methods to simplify
24
+ # building wizard-like application components,
25
+ # authentication frameworks, etc.
22
26
  module Session
23
27
  end
24
28
 
29
+ # This is mixed into Merb::Controller on framework boot.
25
30
  module SessionMixin
26
-
27
31
  # Raised when no suitable session store has been setup.
28
32
  class NoSessionContainer < StandardError; end
29
33
 
@@ -42,7 +46,6 @@ module Merb
42
46
  # session data; min. 16 chars
43
47
  #
44
48
  # :default_cookie_domain The default domain to write cookies for.
45
-
46
49
  def self.included(base)
47
50
  # Register a callback to finalize sessions - needs to run before the cookie
48
51
  # callback extracts Set-Cookie headers from request.cookies.
@@ -54,7 +57,9 @@ module Merb
54
57
  #
55
58
  # ==== Returns
56
59
  # SessionContainer:: The session that was extracted from the request object.
57
- def session(session_store = nil) request.session(session_store) end
60
+ def session(session_store = nil)
61
+ request.session(session_store)
62
+ end
58
63
 
59
64
  # Module methods
60
65
 
@@ -125,6 +130,9 @@ module Merb
125
130
  @session_stores ||= {}
126
131
  end
127
132
 
133
+ # Returns session container. Merb is able to handle multiple session
134
+ # stores, hence a parameter to pick it.
135
+ #
128
136
  # ==== Parameters
129
137
  # session_store<String>:: The type of session store to access,
130
138
  # defaults to default_session_store.
@@ -137,12 +145,13 @@ module Merb
137
145
  if class_name = self.class.registered_session_types[session_store]
138
146
  session_stores[session_store] ||= Object.full_const_get(class_name).setup(self)
139
147
  elsif fallback = self.class.registered_session_types.keys.first
140
- Merb.logger.warn "Session store not found, '#{session_store}'."
141
- Merb.logger.warn "Defaulting to #{fallback} sessions."
148
+ Merb.logger.warn "Session store '#{session_store}' not found. Check your configuration in init file."
149
+ Merb.logger.warn "Falling back to #{fallback} session store."
142
150
  session(fallback)
143
151
  else
144
- Merb.logger.error "Can't use sessions because no session store is available."
145
- raise NoSessionContainer, "No session store configured."
152
+ msg = "No session store set. Set it in init file like this: c[:session_store] = 'activerecord'"
153
+ Merb.logger.error!(msg)
154
+ raise NoSessionContainer, msg
146
155
  end
147
156
  end
148
157
 
@@ -184,12 +193,13 @@ module Merb
184
193
  defaults
185
194
  end
186
195
 
196
+ # Sets session cookie value.
197
+ #
187
198
  # ==== Parameters
188
199
  # value<String>:: The value of the session cookie; either the session id or the actual encoded data.
189
- def set_session_cookie_value(value)
190
- options = {}
191
- options[:expires] = Time.now + _session_expiry
192
- cookies.set_cookie(_session_id_key, value, options)
200
+ # options<Hash>:: Cookie options like domain, path and expired.
201
+ def set_session_cookie_value(value, options = {})
202
+ cookies.set_cookie(_session_id_key, value, { :expires => Time.now + _session_expiry }.merge(options))
193
203
  end
194
204
  alias :set_session_id_cookie :set_session_cookie_value
195
205
 
@@ -199,6 +209,12 @@ module Merb
199
209
  cookies[_session_id_key]
200
210
  end
201
211
  alias :session_id :session_cookie_value
212
+
213
+ # Destroy the session cookie.
214
+ def destroy_session_cookie
215
+ cookies.delete(_session_id_key)
216
+ end
217
+
202
218
  end
203
219
  end
204
220
  end