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.
- data/README +41 -2
- data/Rakefile +4 -2
- data/TODO +3 -3
- data/bin/merb +157 -3
- data/doc/rdoc/classes/Hash.html +4 -4
- data/doc/rdoc/classes/Merb.html +2 -12
- data/doc/rdoc/classes/Merb/Controller.html +347 -156
- data/doc/rdoc/classes/Merb/RouteMatcher.html +93 -59
- data/doc/rdoc/classes/MerbHandler.html +117 -107
- data/doc/rdoc/classes/MerbHash.html +64 -58
- data/doc/rdoc/classes/Noroutefound.html +16 -10
- data/doc/rdoc/classes/Object.html +5 -5
- data/doc/rdoc/classes/String.html +28 -16
- data/doc/rdoc/classes/Symbol.html +15 -8
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/README.html +56 -4
- data/doc/rdoc/files/TODO.html +4 -4
- data/doc/rdoc/files/lib/merb/merb_controller_rb.html +1 -1
- data/doc/rdoc/files/lib/merb/merb_handler_rb.html +1 -1
- data/doc/rdoc/files/lib/merb/merb_router_rb.html +1 -1
- data/doc/rdoc/files/lib/merb/merb_utils_rb.html +1 -1
- data/doc/rdoc/files/lib/merb_rb.html +30 -2
- data/doc/rdoc/files/lib/{merb_config_rb.html → merb_tasks_rb.html} +4 -4
- data/doc/rdoc/fr_class_index.html +0 -2
- data/doc/rdoc/fr_file_index.html +1 -3
- data/doc/rdoc/fr_method_index.html +21 -21
- data/examples/app_skeleton/Rakefile +81 -0
- data/examples/app_skeleton/dist/conf/merb_init.rb +15 -0
- data/examples/{skeleton → app_skeleton}/dist/conf/router.rb +5 -7
- data/examples/app_skeleton/scripts/merb_stop +5 -0
- data/examples/app_skeleton/scripts/new_migration +21 -0
- data/examples/{skeleton → app_skeleton}/test/test_helper.rb +0 -0
- data/examples/sample_app/Rakefile +81 -0
- data/examples/sample_app/dist/app/controllers/posts.rb +26 -10
- data/examples/sample_app/dist/app/models/comment.rb +3 -0
- data/examples/sample_app/dist/app/models/post.rb +2 -11
- data/examples/sample_app/dist/app/views/layout/application.rhtml +59 -4
- data/examples/sample_app/dist/app/views/posts/_comments.rhtml +11 -0
- data/examples/sample_app/dist/app/views/posts/comment.merbjs +1 -0
- data/examples/sample_app/dist/app/views/posts/list.rhtml +2 -4
- data/examples/sample_app/dist/app/views/posts/new.rhtml +2 -2
- data/examples/sample_app/dist/app/views/posts/show.rhtml +35 -3
- data/examples/sample_app/dist/conf/merb_init.rb +2 -4
- data/examples/sample_app/dist/conf/router.rb +3 -4
- data/examples/sample_app/dist/public/images/bg.jpg +0 -0
- data/examples/sample_app/dist/public/images/book.gif +0 -0
- data/examples/sample_app/dist/public/images/booksmall.gif +0 -0
- data/examples/sample_app/dist/public/images/greenright.jpg +0 -0
- data/examples/sample_app/dist/public/images/louiecon.gif +0 -0
- data/examples/sample_app/dist/public/images/menu.gif +0 -0
- data/examples/sample_app/dist/public/images/menuleft.gif +0 -0
- data/examples/sample_app/dist/public/images/menuright.gif +0 -0
- data/examples/sample_app/dist/public/images/mountain.jpg +0 -0
- data/examples/sample_app/dist/public/images/n3.jpg +0 -0
- data/examples/sample_app/dist/public/images/nautica.jpg +0 -0
- data/examples/sample_app/dist/public/javascripts/application.js +0 -0
- data/examples/sample_app/dist/public/javascripts/effects.js +975 -0
- data/examples/sample_app/dist/public/javascripts/prototype.js +2264 -0
- data/examples/sample_app/dist/public/stylesheets/merb.css +277 -0
- data/examples/sample_app/dist/schema/migrations/001_add_comments_to_posts.rb +22 -0
- data/examples/sample_app/dist/schema/schema.rb +22 -0
- data/examples/sample_app/log/merb.log +164394 -0
- data/examples/sample_app/script/merb_stop +9 -0
- data/examples/sample_app/script/new_migration +21 -0
- data/lib/merb.rb +7 -4
- data/lib/merb/merb_controller.rb +83 -4
- data/lib/merb/merb_handler.rb +20 -9
- data/lib/merb/merb_router.rb +18 -1
- data/lib/merb/merb_utils.rb +11 -1
- data/lib/merb_tasks.rb +7 -0
- data/lib/tasks/db.rake +53 -0
- metadata +67 -34
- data/doc/rdoc/classes/Merb/Config.html +0 -161
- data/doc/rdoc/classes/Merb/Server.html +0 -288
- data/doc/rdoc/files/lib/merb/merb_daemon_rb.html +0 -113
- data/doc/rdoc/files/lib/merb/noroutefound_rb.html +0 -101
- data/examples/sample_app/dist/app/views/layout/posts.rhtml +0 -6
- data/examples/skeleton/dist/conf/merb_init.rb +0 -21
- data/lib/merb/merb_daemon.rb +0 -91
- data/lib/merb/noroutefound.rb +0 -11
- data/lib/merb_config.rb +0 -21
@@ -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
|
data/lib/merb.rb
CHANGED
@@ -2,12 +2,15 @@ require 'rubygems'
|
|
2
2
|
require 'mongrel'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'erubis'
|
5
|
-
require '
|
6
|
-
|
5
|
+
require 'logger'
|
7
6
|
|
8
7
|
module Merb
|
9
|
-
VERSION='0.0.
|
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.
|
16
|
+
Dir.entries(lib).sort.each {|fn| require File.join(lib, fn) if fn =~ /\.rb$/}
|
data/lib/merb/merb_controller.rb
CHANGED
@@ -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
|
data/lib/merb/merb_handler.rb
CHANGED
@@ -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.
|
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
|
-
|
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 =
|
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
|
-
|
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
|
-
# [
|
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
|
-
|
118
|
-
|
119
|
-
|
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
|
data/lib/merb/merb_router.rb
CHANGED
@@ -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'
|
data/lib/merb/merb_utils.rb
CHANGED
@@ -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)
|
data/lib/merb_tasks.rb
ADDED
data/lib/tasks/db.rake
ADDED
@@ -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
|