merb 0.0.4 → 0.0.5

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 (81) hide show
  1. data/README +41 -2
  2. data/Rakefile +4 -2
  3. data/TODO +3 -3
  4. data/bin/merb +157 -3
  5. data/doc/rdoc/classes/Hash.html +4 -4
  6. data/doc/rdoc/classes/Merb.html +2 -12
  7. data/doc/rdoc/classes/Merb/Controller.html +347 -156
  8. data/doc/rdoc/classes/Merb/RouteMatcher.html +93 -59
  9. data/doc/rdoc/classes/MerbHandler.html +117 -107
  10. data/doc/rdoc/classes/MerbHash.html +64 -58
  11. data/doc/rdoc/classes/Noroutefound.html +16 -10
  12. data/doc/rdoc/classes/Object.html +5 -5
  13. data/doc/rdoc/classes/String.html +28 -16
  14. data/doc/rdoc/classes/Symbol.html +15 -8
  15. data/doc/rdoc/created.rid +1 -1
  16. data/doc/rdoc/files/README.html +56 -4
  17. data/doc/rdoc/files/TODO.html +4 -4
  18. data/doc/rdoc/files/lib/merb/merb_controller_rb.html +1 -1
  19. data/doc/rdoc/files/lib/merb/merb_handler_rb.html +1 -1
  20. data/doc/rdoc/files/lib/merb/merb_router_rb.html +1 -1
  21. data/doc/rdoc/files/lib/merb/merb_utils_rb.html +1 -1
  22. data/doc/rdoc/files/lib/merb_rb.html +30 -2
  23. data/doc/rdoc/files/lib/{merb_config_rb.html → merb_tasks_rb.html} +4 -4
  24. data/doc/rdoc/fr_class_index.html +0 -2
  25. data/doc/rdoc/fr_file_index.html +1 -3
  26. data/doc/rdoc/fr_method_index.html +21 -21
  27. data/examples/app_skeleton/Rakefile +81 -0
  28. data/examples/app_skeleton/dist/conf/merb_init.rb +15 -0
  29. data/examples/{skeleton → app_skeleton}/dist/conf/router.rb +5 -7
  30. data/examples/app_skeleton/scripts/merb_stop +5 -0
  31. data/examples/app_skeleton/scripts/new_migration +21 -0
  32. data/examples/{skeleton → app_skeleton}/test/test_helper.rb +0 -0
  33. data/examples/sample_app/Rakefile +81 -0
  34. data/examples/sample_app/dist/app/controllers/posts.rb +26 -10
  35. data/examples/sample_app/dist/app/models/comment.rb +3 -0
  36. data/examples/sample_app/dist/app/models/post.rb +2 -11
  37. data/examples/sample_app/dist/app/views/layout/application.rhtml +59 -4
  38. data/examples/sample_app/dist/app/views/posts/_comments.rhtml +11 -0
  39. data/examples/sample_app/dist/app/views/posts/comment.merbjs +1 -0
  40. data/examples/sample_app/dist/app/views/posts/list.rhtml +2 -4
  41. data/examples/sample_app/dist/app/views/posts/new.rhtml +2 -2
  42. data/examples/sample_app/dist/app/views/posts/show.rhtml +35 -3
  43. data/examples/sample_app/dist/conf/merb_init.rb +2 -4
  44. data/examples/sample_app/dist/conf/router.rb +3 -4
  45. data/examples/sample_app/dist/public/images/bg.jpg +0 -0
  46. data/examples/sample_app/dist/public/images/book.gif +0 -0
  47. data/examples/sample_app/dist/public/images/booksmall.gif +0 -0
  48. data/examples/sample_app/dist/public/images/greenright.jpg +0 -0
  49. data/examples/sample_app/dist/public/images/louiecon.gif +0 -0
  50. data/examples/sample_app/dist/public/images/menu.gif +0 -0
  51. data/examples/sample_app/dist/public/images/menuleft.gif +0 -0
  52. data/examples/sample_app/dist/public/images/menuright.gif +0 -0
  53. data/examples/sample_app/dist/public/images/mountain.jpg +0 -0
  54. data/examples/sample_app/dist/public/images/n3.jpg +0 -0
  55. data/examples/sample_app/dist/public/images/nautica.jpg +0 -0
  56. data/examples/sample_app/dist/public/javascripts/application.js +0 -0
  57. data/examples/sample_app/dist/public/javascripts/effects.js +975 -0
  58. data/examples/sample_app/dist/public/javascripts/prototype.js +2264 -0
  59. data/examples/sample_app/dist/public/stylesheets/merb.css +277 -0
  60. data/examples/sample_app/dist/schema/migrations/001_add_comments_to_posts.rb +22 -0
  61. data/examples/sample_app/dist/schema/schema.rb +22 -0
  62. data/examples/sample_app/log/merb.log +164394 -0
  63. data/examples/sample_app/script/merb_stop +9 -0
  64. data/examples/sample_app/script/new_migration +21 -0
  65. data/lib/merb.rb +7 -4
  66. data/lib/merb/merb_controller.rb +83 -4
  67. data/lib/merb/merb_handler.rb +20 -9
  68. data/lib/merb/merb_router.rb +18 -1
  69. data/lib/merb/merb_utils.rb +11 -1
  70. data/lib/merb_tasks.rb +7 -0
  71. data/lib/tasks/db.rake +53 -0
  72. metadata +67 -34
  73. data/doc/rdoc/classes/Merb/Config.html +0 -161
  74. data/doc/rdoc/classes/Merb/Server.html +0 -288
  75. data/doc/rdoc/files/lib/merb/merb_daemon_rb.html +0 -113
  76. data/doc/rdoc/files/lib/merb/noroutefound_rb.html +0 -101
  77. data/examples/sample_app/dist/app/views/layout/posts.rhtml +0 -6
  78. data/examples/skeleton/dist/conf/merb_init.rb +0 -21
  79. data/lib/merb/merb_daemon.rb +0 -91
  80. data/lib/merb/noroutefound.rb +0 -11
  81. data/lib/merb_config.rb +0 -21
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+ pids = IO.readlines(File.dirname(__FILE__)+"/../log/merb.pid").map{|p| p.to_i}
4
+
5
+ pids.each do |pid|
6
+ puts "killing PID: #{pid}"
7
+ Process.kill(9, pid)
8
+ end
9
+ FileUtils.rm File.dirname(__FILE__)+"/../log/merb.pid"
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'merb'
4
+
5
+ TMPL = <<EOF
6
+ class <%= class_name.snake_case.camel_case %> < ActiveRecord::Migration
7
+ def self.up
8
+ end
9
+
10
+ def self.down
11
+ end
12
+ end
13
+ EOF
14
+
15
+ class_name = ARGV[0]
16
+ highest_migration = Dir[Dir.pwd+'/dist/schema/migrations/*'].map{|f| File.basename(f) =~ /^(\d+)/; $1}.max
17
+ filename = format("%03d_%s", (highest_migration.to_i+1), class_name.snake_case)
18
+
19
+ File.open(Dir.pwd+"/dist/schema/migrations/#{filename}.rb", 'w+') do |file|
20
+ file.write Erubis::Eruby.new(TMPL).result(binding)
21
+ end
@@ -2,12 +2,15 @@ require 'rubygems'
2
2
  require 'mongrel'
3
3
  require 'fileutils'
4
4
  require 'erubis'
5
- require 'merb_config'
6
-
5
+ require 'logger'
7
6
 
8
7
  module Merb
9
- VERSION='0.0.4' unless defined?VERSION
8
+ VERSION='0.0.5' unless defined?VERSION
10
9
  end
11
10
 
11
+ MERB_FRAMEWORK_ROOT = File.dirname(__FILE__)
12
+ MERB_ROOT = Merb::Server.config[:merb_root] rescue Dir.pwd
13
+ DIST_ROOT = Merb::Server.config[:dist_root] rescue Dir.pwd+'/dist'
14
+ MERB_LOGGER = Logger.new("#{MERB_ROOT}/log/merb.log")
12
15
  lib = File.join(File.dirname(__FILE__), 'merb')
13
- Dir.foreach(lib) {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
16
+ Dir.entries(lib).sort.each {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
@@ -10,7 +10,10 @@ module Merb
10
10
 
11
11
  attr_accessor :status
12
12
 
13
-
13
+ # parses the http request into params, headers and cookies
14
+ # that you can use in your controller classes. Also handles
15
+ # file uploads by writing a tempfile and passing a reference
16
+ # in params.
14
17
  def initialize(req, env, args, method=(env['REQUEST_METHOD']||"GET")) #:nodoc:
15
18
  env = MerbHash[env.to_hash]
16
19
  puts env.inspect if $DEBUG
@@ -57,31 +60,50 @@ module Merb
57
60
  qs.merge!(query_parse(@in.read))
58
61
  end
59
62
  @cookies, @params = @k.dup, qs.dup.merge(args)
63
+ MERB_LOGGER.info("Params: #{params.inspect}")
60
64
  end
61
65
 
66
+ # redirect to another url It can be like /foo/bar
67
+ # for redirecting within your same app. Or it can
68
+ # be a fully qualified url to another site.
62
69
  def redirect(url)
70
+ MERB_LOGGER.info("Redirecting to: #{url}")
63
71
  @status = 302
64
72
  @headers.merge!({'Location'=> url})
65
73
  return ''
66
74
  end
67
75
 
76
+ # pass in a path to a file and this will set the
77
+ # right headers and let mongrel do its thang and
78
+ # serve the static file directly.
68
79
  def send_file(file)
69
80
  headers['X-SENDFILE'] = file
70
81
  return
71
82
  end
72
83
 
84
+ # accessor for @params. Please use params and
85
+ # never @params directly.
73
86
  def params
74
87
  @params
75
88
  end
76
89
 
90
+ # accessor for @cookies. Please use cookies and
91
+ # never @cookies directly.
77
92
  def cookies
78
93
  @cookies
79
94
  end
80
95
 
96
+ # accessor for @headers. Please use headers and
97
+ # never @headers directly.
81
98
  def headers
82
99
  @headers
83
100
  end
84
101
 
102
+ # parses a query string or the payload of a POST
103
+ # request into the params hash. So for example:
104
+ # /foo?bar=nik&post[title]=heya&post[body]=whatever
105
+ # parses into:
106
+ # {:bar => 'nik', :post => {:title => 'heya', :body => 'whatever}}
85
107
  def query_parse(qs, d = '&;')
86
108
  m = proc {|_,o,n|o.update(n,&m)rescue([*o]<<n)}
87
109
  (qs||'').split(/[#{d}] */n).inject(MerbHash[]) { |h,p|
@@ -91,34 +113,75 @@ module Merb
91
113
  }
92
114
  end
93
115
 
116
+ # does url escaping
94
117
  def escape(s)
95
118
  Mongrel::HttpRequest.escape(s)
96
119
  end
97
120
 
121
+ # does url unescaping
98
122
  def unescape(s)
99
123
  Mongrel::HttpRequest.unescape(s)
100
124
  end
101
125
 
102
-
126
+ # escape text for javascript.
127
+ def escape_js(javascript)
128
+ (javascript || '').gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
129
+ end
130
+ alias :js :escape_js
131
+
132
+ # shortcut to a template path based on name.
103
133
  def template_dir(loc)
104
134
  File.expand_path(Merb::Server.config[:merb_root] + "/dist/app/views/#{loc}")
105
135
  end
106
136
 
137
+ # returns the current method name. Used for
138
+ # auto discovery of which template to render
139
+ # based on the action name.
107
140
  def current_method_name(depth=0)
108
141
  caller[depth] =~ /`(.*)'$/; $1
109
142
  end
110
143
 
144
+ # does a render with no layout. Also sets the
145
+ # content type header to text/javascript and
146
+ # escapes the template for javascript eval on
147
+ # the client
148
+ def render_js(template=current_method_name(1), b=binding)
149
+ headers['Content-Type'] = "text/javascript"
150
+ template = Erubis::Eruby.new(IO.read( template_dir(self.class.name.snake_case) + "/#{template}.merbjs" ))
151
+ template.result(b)
152
+ end
111
153
 
154
+ # set the @layout. Use this right before a render to
155
+ # set the name of the layout to use minus the .rhtml
112
156
  def layout(l)
113
157
  @layout = l
114
158
  end
115
159
 
160
+ def render_nothing(status)
161
+ @status = status
162
+ return ''
163
+ end
164
+
165
+ # renders the action without wrapping it in a layout.
116
166
  def render_no_layout(template=current_method_name(1), b=binding)
117
167
  template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/#{template}.rhtml" ) )
118
168
  template.result(b)
119
- end
169
+ end
170
+
171
+ def partial(template)
172
+ template = Erubis::Eruby.new( IO.read( template_dir(self.class.name.snake_case) + "/_#{template}.rhtml" ) )
173
+ template.result(binding)
174
+ end
120
175
 
176
+ # renders a template based on the current action name
177
+ # you can pass the name of a template if you want to
178
+ # render a template with a different name then then
179
+ # current action name. Wraps the rendered template in
180
+ # the layout. Uses layout/application.rhtml unless
181
+ # there is a layout named after the current controller
182
+ # or @layout has been set to another value.
121
183
  def render(template=current_method_name(1), b=binding)
184
+ MERB_LOGGER.info("Rendering template: #{template_dir(template)}")
122
185
  name = self.class.name.snake_case
123
186
  template = Erubis::Eruby.new( IO.read( template_dir(name) + "/#{template}.rhtml" ) )
124
187
  layout_content = template.result(b)
@@ -132,12 +195,28 @@ module Merb
132
195
  else
133
196
  layout = @layout.to_s
134
197
  end
198
+ MERB_LOGGER.info("With Layout: #{template_dir('layout')}/#{layout}.rhtml")
135
199
  @layout_content = layout_content
136
200
  layout_tmpl = Erubis::Eruby.new( IO.read( template_dir('layout') + "/#{layout}.rhtml" ) )
137
201
  layout_tmpl.result(b)
138
202
  end
139
203
 
140
-
141
204
  end
142
205
 
206
+ end
207
+
208
+ class Noroutefound < Merb::Controller
209
+ # This is the class that handles requests that don't
210
+ # match any defined routes.
211
+
212
+ def method_missing(sym, *args, &blk)
213
+ @status = 404
214
+ "<html><body><h1>No Matching Route</h1></body></html>"
215
+ end
216
+
217
+ def to_s
218
+ @status = 404
219
+ "<html><body><h1>No Matching Route s</h1></body></html>"
220
+ end
221
+
143
222
  end
@@ -4,7 +4,7 @@ class MerbHandler < Mongrel::HttpHandler
4
4
 
5
5
  # take the name of a directory and use that as the doc root or public
6
6
  # directory of your site. This is set to the root of your merb app + '/public'
7
- # by default. See merb_daemon.rb if you want to change this.
7
+ # by default.
8
8
  def initialize(dir, opts = {})
9
9
  @files = Mongrel::DirHandler.new(dir,false)
10
10
  @guard = Sync.new
@@ -29,6 +29,8 @@ class MerbHandler < Mongrel::HttpHandler
29
29
  return
30
30
  end
31
31
 
32
+ MERB_LOGGER.info("Request: PATH_INFO: #{request.params[Mongrel::Const::PATH_INFO]}")
33
+
32
34
  # Rails style page caching. Check the public dir first for
33
35
  # .html pages and serve directly. Otherwise fall back to Merb
34
36
  # routing and request dispatching.
@@ -38,9 +40,12 @@ class MerbHandler < Mongrel::HttpHandler
38
40
 
39
41
  if get_or_head and @files.can_serve(path_info)
40
42
  # File exists as-is so serve it up
43
+ MERB_LOGGER.info("Serving static file: #{path_info}")
44
+
41
45
  @files.process(request,response)
42
46
  elsif get_or_head and @files.can_serve(page_cached)
43
47
  # Possible cached page, serve it up
48
+ MERB_LOGGER.info("Serving static file: #{path_info}")
44
49
  request.params[Mongrel::Const::PATH_INFO] = page_cached
45
50
  @files.process(request,response)
46
51
  else
@@ -49,12 +54,13 @@ class MerbHandler < Mongrel::HttpHandler
49
54
  # params and is outside of the synchronize call so that
50
55
  # multiple file uploads can be done at once.
51
56
  controller, action = handle(request)
52
- p controller, action
57
+ MERB_LOGGER.info("Routing to controller: #{controller.class} action: #{action}")
53
58
  output = nil
54
59
  # synchronize here because this is where ActiveRecord or your db
55
60
  # calls will be run in your controller methods.
56
61
  @guard.synchronize(:EX) {
57
- output = if (controller && controller.kind_of?(Merb::Controller))
62
+ output =
63
+ if (controller && controller.kind_of?(Merb::Controller))
58
64
  if action
59
65
  controller.send(action)
60
66
  else
@@ -67,7 +73,8 @@ class MerbHandler < Mongrel::HttpHandler
67
73
  rescue Exception => e
68
74
  response.start(500) do |head,out|
69
75
  head["Content-Type"] = "text/html"
70
- out << exception(e)
76
+ MERB_LOGGER.info(ex = exception(e))
77
+ out << ex
71
78
  end
72
79
  return
73
80
  end
@@ -89,12 +96,16 @@ class MerbHandler < Mongrel::HttpHandler
89
96
  end
90
97
  end
91
98
 
99
+ controller = nil
100
+
92
101
  if sendfile
102
+ MERB_LOGGER.info("X-SENDFILE: #{sendfile}")
93
103
  # send X-SENDFILE header to mongrel
94
104
  response.send_status(File.size(sendfile))
95
105
  response.send_header
96
106
  response.send_file(sendfile)
97
107
  else
108
+ MERB_LOGGER.info("Response status: #{response.status}\n\n")
98
109
  # render response from successful controller
99
110
  response.send_status(output.length)
100
111
  response.send_header
@@ -107,17 +118,17 @@ class MerbHandler < Mongrel::HttpHandler
107
118
  # and use that in the merb routematcher to determine
108
119
  # which controller and method to run.
109
120
  # returns a 2 element tuple of:
110
- # [controller_object, method]
121
+ # [controller, action]
111
122
  def handle(request)
112
123
  path = request.params[Mongrel::Const::PATH_INFO].sub(/\/+/, '/')
113
124
  path = path[0..-2] if (path[-1] == ?/)
114
125
  route = Merb::RouteMatcher.new.route_request(path)
115
- puts route.inspect if $DEBUG
116
126
  if route
117
- [ instantiate_controller(route[:controller], request.body, request.params,
118
- route.dup.delete_if{|k,v| [:controller, :action].include? k}),
119
- route[:action]]
127
+ MERB_LOGGER.info("No Matching Route!") if route[:controller] == 'Noroutefound'
128
+ [ instantiate_controller(route[:controller], request.body, request.params, route),
129
+ route[:action] ]
120
130
  else
131
+ MERB_LOGGER.info("No Matching Route!")
121
132
  ["<html><body>Error: no route matches!</body></html>", nil]
122
133
  end
123
134
  end
@@ -13,8 +13,10 @@ module Merb
13
13
  class RouteMatcher
14
14
 
15
15
  attr_accessor :sections
16
- @@section_regexp = /(?::([a-z*]+))/.freeze
16
+ @@section_regexp = /(?::([a-z*_]+))/.freeze
17
17
 
18
+ # setup the router and yield it out to
19
+ # add routes to it. Then compile all routes
18
20
  def self.prepare
19
21
  @@routes = Array.new
20
22
  @@compiled_statement = String.new
@@ -22,22 +24,31 @@ module Merb
22
24
  compile_router
23
25
  end
24
26
 
27
+ # init @sections for route segment recognition
25
28
  def initialize
26
29
  @sections = Hash.new
27
30
  end
28
31
 
32
+ # all defined routes in their raw form.
29
33
  def routes
30
34
  @@routes
31
35
  end
32
36
 
37
+ # the final compiled lambda that gets used
38
+ # as the body of the route_request method.
33
39
  def compiled_statement
34
40
  @@compiled_statement
35
41
  end
36
42
 
43
+ # add a route to be compiled
37
44
  def self.add(*route)
38
45
  @@routes << [route[0], (route[1] || {})]
39
46
  end
40
47
 
48
+ # build up a string that defines a lambda
49
+ # that does a case statement on the PATH_INFO
50
+ # against each of the compiled routes in turn.
51
+ # first route that matches wins.
41
52
  def self.compile_router
42
53
  router_lambda = @@routes.inject("lambda{|path| \n case path\n") { |m,r|
43
54
  m << compile(r)
@@ -46,6 +57,12 @@ module Merb
46
57
  define_method(:route_request, &eval(router_lambda))
47
58
  end
48
59
 
60
+ # compile each individual route into a when /.../
61
+ # component of the case statement. Takes /:sections
62
+ # if the route def that start with : and turns them
63
+ # into placeholders for whatever urls match against
64
+ # the route in question. Special case for the default
65
+ # /:controller/:action/:id route.
49
66
  def self.compile(route)
50
67
  raise ArgumentError unless String === route[0]
51
68
  if route[0] == '/:controller/:action/:id'
@@ -1,14 +1,19 @@
1
1
  class String
2
+
3
+ # reloads controller classes on each request if
4
+ # :allow_reloading is set to true in the config
5
+ # file or command line options.
2
6
  def import
3
7
  Merb::Server.config[:allow_reloading] ? load( self.snake_case + '.rb' ) : require( self.snake_case )
4
8
  end
5
9
 
10
+ # "FooBar".snake_case #=> "foo_bar"
6
11
  def snake_case
7
12
  return self unless self =~ %r/[A-Z]/
8
13
  self.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?/).reverse.map{|word| word.reverse.downcase}.join '_'
9
14
  end
10
15
 
11
-
16
+ # "foo_bar".camel_case #=> "FooBar"
12
17
  def camel_case
13
18
  return self if self =~ %r/[A-Z]/ and self !~ %r/_/
14
19
  words = self.strip.split %r/\s*_+\s*/
@@ -19,9 +24,13 @@ class String
19
24
  end
20
25
 
21
26
  class Symbol
27
+
28
+ # faster Symbol#to_s to speed up routing.
22
29
  def to_s
23
30
  @str_rep || (@str_rep = id2name.freeze)
24
31
  end
32
+
33
+ # ["foo", "bar"].map &:reverse #=> ['oof', 'rab']
25
34
  def to_proc
26
35
  Proc.new{|*args| args.shift.__send__(self, *args)}
27
36
  end
@@ -40,6 +49,7 @@ class Hash
40
49
  end
41
50
  end
42
51
 
52
+ # like HashWithIndifferentAccess from ActiveSupport.
43
53
  class MerbHash < Hash
44
54
  def initialize(constructor = {})
45
55
  if constructor.is_a?(Hash)
@@ -0,0 +1,7 @@
1
+ $VERBOSE = nil
2
+
3
+ # Load Merb rakefile extensions
4
+ Dir["#{File.dirname(__FILE__)}/tasks/**/*.rake"].each { |ext| load ext }
5
+
6
+ # Load any custom rakefile extensions
7
+ Dir["./lib/tasks/**/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,53 @@
1
+ taMERB_ROOT = Dir.pwd
2
+
3
+ desc "Setup the Merb Environment by requiring merb and loading your merb_init.rb"
4
+ task :merb_env do
5
+ require 'rubygems'
6
+ require 'merb'
7
+ load MERB_ROOT+'/dist/conf/merb_init.rb'
8
+ end
9
+
10
+ namespace :db do
11
+ desc "Migrate the database through scripts in dist/schema/migrate. Target specific version with VERSION=x"
12
+ task :migrate => :merb_env do
13
+ ActiveRecord::Migrator.migrate("dist/schema/migrations/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
14
+ Rake::Task["db:schema:dump"].invoke
15
+ end
16
+
17
+ namespace :schema do
18
+ desc "Create a db/schema.rb file that can be portably used against any DB supported by AR"
19
+ task :dump => :merb_env do
20
+ require 'active_record/schema_dumper'
21
+ File.open(ENV['SCHEMA'] || "dist/schema/schema.rb", "w") do |file|
22
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
23
+ end
24
+ end
25
+
26
+ desc "Load a schema.rb file into the database"
27
+ task :load => :merb_env do
28
+ file = ENV['SCHEMA'] || "dist/schema/schema.rb"
29
+ load(file)
30
+ end
31
+ end
32
+
33
+ namespace :sessions do
34
+ desc "Creates a sessions table for use with CGI::Session::ActiveRecordStore"
35
+ task :create => :merb_env do
36
+ raise "Task unavailable to this database (no migration support)" unless ActiveRecord::Base.connection.supports_migrations?
37
+ require 'rails_generator'
38
+ require 'rails_generator/scripts/generate'
39
+ Rails::Generator::Scripts::Generate.new.run(["session_migration", ENV["MIGRATION"] || "AddSessions"])
40
+ end
41
+
42
+ desc "Clear the sessions table"
43
+ task :clear => :merb_env do
44
+ session_table = 'session'
45
+ session_table = Inflector.pluralize(session_table) if ActiveRecord::Base.pluralize_table_names
46
+ ActiveRecord::Base.connection.execute "DELETE FROM #{session_table}"
47
+ end
48
+ end
49
+ end
50
+
51
+ def session_table_name
52
+ ActiveRecord::Base.pluralize_table_names ? :sessions : :session
53
+ end