mack 0.0.3
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 +43 -0
- data/bin/mack +60 -0
- data/bin/templates/Rakefile.template +6 -0
- data/bin/templates/app/controllers/default_controller.rb.template +7 -0
- data/bin/templates/app/helpers/application_helper.rb.template +2 -0
- data/bin/templates/app/views/default/index.html.erb.template +3 -0
- data/bin/templates/app/views/layouts/application.html.erb.template +15 -0
- data/bin/templates/config/app_config/default.yml.template +7 -0
- data/bin/templates/config/app_config/development.yml.template +0 -0
- data/bin/templates/config/app_config/production.yml.template +0 -0
- data/bin/templates/config/app_config/test.yml.template +0 -0
- data/bin/templates/config/boot.rb.template +6 -0
- data/bin/templates/config/database.yml.template +20 -0
- data/bin/templates/config/routes.rb.template +7 -0
- data/bin/templates/config/thin.ru.template +1 -0
- data/bin/templates/config/thin.yml.template +8 -0
- data/bin/templates/public/favicon.ico +0 -0
- data/bin/templates/public/stylesheets/scaffold.css.template +74 -0
- data/core_extensions/hash.rb +9 -0
- data/core_extensions/module.rb +29 -0
- data/core_extensions/nil.rb +8 -0
- data/core_extensions/object.rb +9 -0
- data/core_extensions/string.rb +28 -0
- data/errors/errors.rb +79 -0
- data/initialize/configuration.rb +99 -0
- data/initialize/configure_logging.rb +24 -0
- data/initialize/configure_orm_support.rb +23 -0
- data/initialize/console.rb +13 -0
- data/initialize/initializer.rb +88 -0
- data/initialize/server/simple_server.rb +21 -0
- data/lib/utils/html.rb +88 -0
- data/lib/utils/server.rb +27 -0
- data/mack.rb +124 -0
- data/mack_tasks.rb +16 -0
- data/routing/route_map.rb +268 -0
- data/routing/urls.rb +54 -0
- data/sea_level/controller_base.rb +293 -0
- data/sea_level/cookie_jar.rb +67 -0
- data/sea_level/filter.rb +63 -0
- data/sea_level/helpers/view_helpers/html_helpers.rb +33 -0
- data/sea_level/helpers/view_helpers/orm_helpers.rb +72 -0
- data/sea_level/request.rb +83 -0
- data/sea_level/response.rb +6 -0
- data/sea_level/session.rb +33 -0
- data/sea_level/view_binder.rb +101 -0
- data/tasks/cachetastic_tasks.rake +69 -0
- data/tasks/log_tasks.rake +9 -0
- data/tasks/mack_tasks.rake +15 -0
- data/tasks/rake_helpers.rb +24 -0
- data/tasks/rake_rules.rake +19 -0
- data/tasks/script_tasks.rake +44 -0
- data/tasks/test_tasks.rake +7 -0
- data/test_extensions/test_assertions.rb +47 -0
- data/test_extensions/test_helpers.rb +84 -0
- metadata +173 -0
data/mack_tasks.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
# Requires all rake tasks that ship with the Mack framework.
|
5
|
+
[File.join(File.dirname(__FILE__)), File.join(FileUtils.pwd, "lib")].each do |dir|
|
6
|
+
begin
|
7
|
+
require File.join(dir, "tasks", "rake_helpers.rb")
|
8
|
+
rescue Exception => e
|
9
|
+
end
|
10
|
+
files = Dir.glob(File.join(dir, "tasks", "*.rake"))
|
11
|
+
files.each do |f|
|
12
|
+
unless f == __FILE__
|
13
|
+
load f
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
module Mack
|
3
|
+
|
4
|
+
module Routes
|
5
|
+
|
6
|
+
# This method yields up Mack::Routes::RouteMap and allows for the creation of routes in the system.
|
7
|
+
#
|
8
|
+
# See Mack::Routes::RouteMap for more information.
|
9
|
+
def self.build
|
10
|
+
yield Mack::Routes::RouteMap.instance
|
11
|
+
Mack::Routes::Urls.include_safely_into(Mack::Controller::Base, Mack::ViewBinder, Test::Unit::TestCase)
|
12
|
+
# puts "Finished compiling routes: #{Mack::Routes::RouteMap.instance.routes_list.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Routes are the back bone of the Mack framework. They are used to map incoming urls to controllers.
|
16
|
+
#
|
17
|
+
# === Defining Routes:
|
18
|
+
# Example:
|
19
|
+
# Mack::Routes.build do |r|
|
20
|
+
#
|
21
|
+
# # Connects "/" to the HomeController and the '/' action.
|
22
|
+
# r.connect "/", :controller => :home
|
23
|
+
#
|
24
|
+
# # Connects "/foo", to the HomeController and the 'foo' action.
|
25
|
+
# r.connect "/foo", :controller => :home, :action => :foo
|
26
|
+
#
|
27
|
+
# # Connects "/blog" to the BlogController and the 'list' action.
|
28
|
+
# r.connect "/blog", :controller => :blog, :action => :list
|
29
|
+
#
|
30
|
+
# # Connects "/blog/:id" to the BlogController and the 'index' action.
|
31
|
+
# # It will also take the second half of the url and map it to a parameter called :id.
|
32
|
+
# # Example:
|
33
|
+
# # '/blog/1' # => goes to the BlogController, 'index' action, and has an :id parameter == 1.
|
34
|
+
# r.connect "/blog/:id", :controller => :blog, :action => :index
|
35
|
+
#
|
36
|
+
# # Connects "/blog/create" to the BlogController and the 'create' action.
|
37
|
+
# # It also insists that the HTTP method be 'post'. If it's not 'post' it will not match this route.
|
38
|
+
# r.connect "/blog/create", :controller => :blog, :action => :create, :method => :post
|
39
|
+
#
|
40
|
+
# # Connects "/comment/destroy/:id" to the CommentController and the 'destroy' action.
|
41
|
+
# # It also insists that the HTTP method be 'delete'. If it's not 'delete' it will not match this route.
|
42
|
+
# # It will also create an :id parameter.
|
43
|
+
# r.connect "/comment/destroy/:id", :controller => :comment, :action => :destroy, :method => :delete
|
44
|
+
#
|
45
|
+
# # This will create 'RESTful' routes. Unlike Rails, it doesn't generate a mixture of singular/plural
|
46
|
+
# # routes. It uses whatever you pass in to it. This will also create named routes in Mack::Routes::Urls.
|
47
|
+
# # Examples:
|
48
|
+
# # '/users' # => {:controller => 'users', :action => :index, :method => :get}
|
49
|
+
# # # => users_index_url
|
50
|
+
# # # => users_index_full_url
|
51
|
+
# # '/users' # => {:controller => 'users', :action => :create, :method => :post}
|
52
|
+
# # # => users_create_url
|
53
|
+
# # # => users_create_full_url
|
54
|
+
# # '/users/new' # => {:controller => 'users', :action => :new, :method => :get}
|
55
|
+
# # # => users_new_url
|
56
|
+
# # # => users_new_full_url
|
57
|
+
# # '/users/:id' # => {:controller => 'users', :action => :show, :method => :get}
|
58
|
+
# # # => users_show_url
|
59
|
+
# # # => users_show_full_url
|
60
|
+
# # '/users/:id/:edit # => {:controller => 'users', :action => :edit, :method => :get}
|
61
|
+
# # # => users_edit_url
|
62
|
+
# # # => users_edit_full_url
|
63
|
+
# # '/users/:id/update # => {:controller => 'users', :action => :update, :method => :post}
|
64
|
+
# # # => users_update_url
|
65
|
+
# # # => users_update_full_url
|
66
|
+
# # '/users/:id # => {:controller => 'users', :action => :delete, :method => :delete}
|
67
|
+
# # # => users_delete_url
|
68
|
+
# # # => users_delete_full_url
|
69
|
+
# r.resource :users
|
70
|
+
#
|
71
|
+
# # This will redirect '/old_users/show/:id' to '/users/:id' with a status of 302, 'Moved Temporarily'.
|
72
|
+
# # Examples:
|
73
|
+
# # '/old_users/show/1' # => '/users/1' (status of 302)
|
74
|
+
# # '/old_users/show/1?foo=bar' # => '/users/1?foo=bar' (status of 302)
|
75
|
+
# r.connect '/old_users/show/:id', :redirect_to => "/users/:id"
|
76
|
+
#
|
77
|
+
# # This will redirect '/old_blog' to '/blog' with a status of 301, 'Moved Permanently'.
|
78
|
+
# # Examples:
|
79
|
+
# # '/old_blog' # => '/blog' (status of 301)
|
80
|
+
# # '/old_blog?foo=bar' # => '/blogfoo=bar' (status of 301)
|
81
|
+
# r.connect '/old_blog', :redirect_to => "/blog", :status => 301
|
82
|
+
#
|
83
|
+
# # Connects "/comment/update/:id" to the CommentController and the 'update' action.
|
84
|
+
# # It will also create an :id parameter.
|
85
|
+
# # It will also create a named route in Mack::Routes::Urls called, 'c_u_url'.
|
86
|
+
# # In a controller or a view this: c_u_url(:id => 1) would return '/comment/update/1'.
|
87
|
+
# # It will also create a named route in Mack::Routes::Urls called, 'c_u_full_url'.
|
88
|
+
# # In a controller or a view this: c_u_full_url(:id => 1) would return 'http://example.org/comment/update/1'.
|
89
|
+
# r.c_u "/comment/update/:id", {:controller => :comment, :action => :update}
|
90
|
+
#
|
91
|
+
# # This creates 'Rails' style routes.
|
92
|
+
# # Any requests that come in that aren't found by explicit routes, will fall into these routes.
|
93
|
+
# # '/:controller/:action'
|
94
|
+
# # '/:controller/:action/:id'
|
95
|
+
# #
|
96
|
+
# # Example:
|
97
|
+
# # '/comment/show/1' # => Goes to CommentController, the 'show' action, with a parameter of 1.
|
98
|
+
# r.defaults
|
99
|
+
#
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# === Named Routes:
|
103
|
+
# Mack::Routes.build do |r|
|
104
|
+
# r.resource :users
|
105
|
+
# end
|
106
|
+
# See above in 'Defining Routes' to see what fully gets created when you map a resource, but let's look
|
107
|
+
# at the named route stuff that gets generated. In particular let's look at one example:
|
108
|
+
# '/users/:id' # => {:controller => 'users', :action => :show, :method => :get}
|
109
|
+
# # => users_show_url
|
110
|
+
# # => users_show_full_url
|
111
|
+
# The following can be used in controllers, views, and tests:
|
112
|
+
# users_show_url(:id => 1) # => '/users/1'
|
113
|
+
# # The following can only be used when there is a @request (Mack::Request) instance variable around:
|
114
|
+
# users_show_full_url(:id => 1) # => 'http://example.org/users/1'
|
115
|
+
#
|
116
|
+
# Mack::Routes.build do |r|
|
117
|
+
# r.hello_world "/", :controller => :home_page, :action => :hello
|
118
|
+
# end
|
119
|
+
# This will give you the following two methods:
|
120
|
+
# hello_world_url # => "/"
|
121
|
+
# hello_world_full_url # => "http://example.org/"
|
122
|
+
# These methods act just like the ones created when you use the resource method.
|
123
|
+
class RouteMap
|
124
|
+
include Singleton
|
125
|
+
|
126
|
+
def initialize # :nodoc:
|
127
|
+
@routes_list = []
|
128
|
+
end
|
129
|
+
|
130
|
+
# Creates 'Rails' style default mappings:
|
131
|
+
# "/:controller/:action/:id"
|
132
|
+
# "/:controller/:action"
|
133
|
+
# These get created for each of the 4 HTTP verbs.
|
134
|
+
def defaults
|
135
|
+
[:get, :post, :put, :delete].each do |verb|
|
136
|
+
connect("/:controller/:action/:id", :method => verb)
|
137
|
+
connect("/:controller/:action", :method => verb)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Connects a url pattern to a controller, an action, and an HTTP verb.
|
142
|
+
def connect(pattern, options = {})
|
143
|
+
# set the default options:
|
144
|
+
options = {:action => :index, :method => :get}.merge(options)
|
145
|
+
meth = options[:method].to_sym
|
146
|
+
# if the pattern doesn't start with /, then add it.
|
147
|
+
pattern = "/" << pattern unless pattern.match(/^\//)
|
148
|
+
pt = pattern.downcase
|
149
|
+
route = Route.new(pt, regex_from_pattern(pt), meth, options)
|
150
|
+
routes_list << route
|
151
|
+
return route
|
152
|
+
end
|
153
|
+
|
154
|
+
# Sets up mappings and named routes for a resource.
|
155
|
+
def resource(controller)
|
156
|
+
connect_with_named_route("#{controller}_index", "/#{controller}", {:controller => controller, :action => :index, :method => :get})
|
157
|
+
connect_with_named_route("#{controller}_create", "/#{controller}", {:controller => controller, :action => :create, :method => :post})
|
158
|
+
connect_with_named_route("#{controller}_new", "/#{controller}/new", {:controller => controller, :action => :new, :method => :get})
|
159
|
+
connect_with_named_route("#{controller}_show", "/#{controller}/:id", {:controller => controller, :action => :show, :method => :get})
|
160
|
+
connect_with_named_route("#{controller}_edit", "/#{controller}/:id/edit", {:controller => controller, :action => :edit, :method => :get})
|
161
|
+
connect_with_named_route("#{controller}_update", "/#{controller}/:id/update", {:controller => controller, :action => :update, :method => :post})
|
162
|
+
connect_with_named_route("#{controller}_delete", "/#{controller}/:id", {:controller => controller, :action => :delete, :method => :delete})
|
163
|
+
end
|
164
|
+
|
165
|
+
def method_missing(sym, *args) # :nodoc:
|
166
|
+
connect_with_named_route(sym, args.first, args.last)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Pass in a request and it will try and give you back a Hash representing the
|
170
|
+
# options for that route. IE: controller, action, verb, etc...
|
171
|
+
# If there are embedded options they added to the Hash. These parameters are
|
172
|
+
# also added to the 'params' object in the request.
|
173
|
+
# If the route can not be found a Mack::Errors::UndefinedRoute exception is raised.
|
174
|
+
def get_route_from_request(req)
|
175
|
+
pattern = req.path_info.downcase
|
176
|
+
unless pattern == "/"
|
177
|
+
pattern.chop! if pattern.match(/\/$/)
|
178
|
+
end
|
179
|
+
meth = (req.params("_method") || req.request_method.downcase).to_sym
|
180
|
+
begin
|
181
|
+
routes_list.each do |route|
|
182
|
+
if pattern.match(route.regex_pattern) && route.method == meth
|
183
|
+
r = route
|
184
|
+
opts = r.options_with_embedded_parameters(pattern)
|
185
|
+
req.merge_params(opts)
|
186
|
+
return opts
|
187
|
+
end
|
188
|
+
end
|
189
|
+
rescue Exception => e
|
190
|
+
raise e
|
191
|
+
end
|
192
|
+
# Can't find the route!
|
193
|
+
raise Mack::Errors::UndefinedRoute.new(req)
|
194
|
+
end # get
|
195
|
+
|
196
|
+
private
|
197
|
+
attr_reader :routes_list # :nodoc:
|
198
|
+
|
199
|
+
def connect_with_named_route(n_route, pattern, options = {})
|
200
|
+
route = connect(pattern, options)
|
201
|
+
Mack::Routes::Urls.class_eval %{
|
202
|
+
def #{n_route}_url(options = {})
|
203
|
+
url_for_pattern("#{route.original_pattern}", options)
|
204
|
+
end
|
205
|
+
|
206
|
+
def #{n_route}_full_url(options = {})
|
207
|
+
u = #{n_route}_url(options)
|
208
|
+
"\#{@request.full_host}\#{u}"
|
209
|
+
end
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
def regex_from_pattern(pattern)
|
214
|
+
pattern.chop! if pattern.match(/\/$/)
|
215
|
+
segs = []
|
216
|
+
sections = pattern.split("/")
|
217
|
+
sections.each_with_index do |sec, ind|
|
218
|
+
if sec.match(/\A:/)
|
219
|
+
segs << "[^/]+"
|
220
|
+
else
|
221
|
+
segs << sec
|
222
|
+
end
|
223
|
+
end
|
224
|
+
s = segs.join("/")
|
225
|
+
s = "/" if s.blank?
|
226
|
+
rx = /^#{s}$/
|
227
|
+
rx
|
228
|
+
end # regex_from_pattern
|
229
|
+
|
230
|
+
class Route # :nodoc:
|
231
|
+
attr_accessor :regex_pattern
|
232
|
+
attr_accessor :method
|
233
|
+
attr_accessor :original_pattern
|
234
|
+
attr_accessor :options
|
235
|
+
attr_accessor :embedded_parameters
|
236
|
+
|
237
|
+
def initialize(original_pattern, regex_pattern, method, options)
|
238
|
+
self.original_pattern = original_pattern
|
239
|
+
self.regex_pattern = regex_pattern
|
240
|
+
self.method = method
|
241
|
+
self.options = options
|
242
|
+
self.embedded_parameters = []
|
243
|
+
# find out where the embedded_parameters are:
|
244
|
+
original_pattern.split("/").each_with_index do |seg, ind|
|
245
|
+
if seg.match(/^:/)
|
246
|
+
self.embedded_parameters[ind] = seg[1..seg.length].to_sym
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def options_with_embedded_parameters(uri)
|
252
|
+
opts = self.options
|
253
|
+
split_uri = uri.split("/")
|
254
|
+
self.embedded_parameters.each_with_index do |val, ind|
|
255
|
+
unless val.nil?
|
256
|
+
opts[val] = split_uri[ind]
|
257
|
+
end
|
258
|
+
end
|
259
|
+
opts
|
260
|
+
end
|
261
|
+
|
262
|
+
end # Route
|
263
|
+
|
264
|
+
end # RouteMap
|
265
|
+
|
266
|
+
end # Routes
|
267
|
+
|
268
|
+
end # Mack
|
data/routing/urls.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Mack
|
2
|
+
module Routes
|
3
|
+
# This module is the repository for named_routes. See Mack::Routes::RouteMap for more information.
|
4
|
+
module Urls
|
5
|
+
|
6
|
+
# Takes a url pattern and merges it with the options to hopefully produce a well formed url.
|
7
|
+
# Query string parameters will get escaped.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# url_for_pattern("/:controller/:action/:id", {:controller => :blog, :action => :show, :id => 1})
|
11
|
+
# # => "/blog/show/1
|
12
|
+
# url_for_pattern("/:controller/:action/:id", {:controller => :blog, :action => :show})
|
13
|
+
# # => "/blog/show/:id"
|
14
|
+
# url_for_pattern("/blog/:id", {:id => 1})
|
15
|
+
# # => "/blog/1
|
16
|
+
# url_for_pattern("/blog/:id", {:id => 1, :n_id => 2})
|
17
|
+
# # => "/blog/1?n_id=2
|
18
|
+
def url_for_pattern(url, options = {})
|
19
|
+
u = url.dup
|
20
|
+
unused_params = []
|
21
|
+
options.each_pair do |k, v|
|
22
|
+
vp = Rack::Utils.escape(v.to_param)
|
23
|
+
if u.gsub!(":#{k}", vp).nil?
|
24
|
+
unused_params << "#{Rack::Utils.escape(k)}=#{vp}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
unless unused_params.empty?
|
28
|
+
u << "?" << unused_params.sort.join("&")
|
29
|
+
end
|
30
|
+
u
|
31
|
+
end
|
32
|
+
|
33
|
+
# Builds a simple HTML page to be rendered when a redirect occurs.
|
34
|
+
# Hopefully no one sees the HTML, but in case the browser won't do the
|
35
|
+
# redirect it's nice to let people know what's happening.
|
36
|
+
def redirect_html(original_path, new_path, status)
|
37
|
+
%{
|
38
|
+
<!DOCTYPE HTML PUBLIC
|
39
|
+
"-//IETF//DTD HTML 2.0//EN">
|
40
|
+
<html>
|
41
|
+
<head>
|
42
|
+
<title>#{status} Found</title>
|
43
|
+
</head>
|
44
|
+
<body>
|
45
|
+
<h1>Found</h1>
|
46
|
+
<p>The document has moved <a href="#{new_path}">here</a>.</p>
|
47
|
+
</body>
|
48
|
+
</html>
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
end # Urls
|
53
|
+
end # Routes
|
54
|
+
end # Mack
|
@@ -0,0 +1,293 @@
|
|
1
|
+
require 'erb'
|
2
|
+
module Mack
|
3
|
+
module Controller # :nodoc:
|
4
|
+
# All controllers in a Mack application have to extend this class. I'll be honest, if they don't extend this class
|
5
|
+
# then, well, things just won't work very well!
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
# class MyAwesomeController < Mack::Controller::Base
|
9
|
+
# def index
|
10
|
+
# render(:text => "Hello World!")
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
class Base
|
14
|
+
|
15
|
+
# See Mack::Request for more information.
|
16
|
+
attr_reader :request
|
17
|
+
# See Mack::Response for more information.
|
18
|
+
attr_reader :response
|
19
|
+
# The 'underscore' version of the controller requested. Example: 'my_awesome_controller'
|
20
|
+
attr_reader :controller_name
|
21
|
+
# The name of the action being requested.
|
22
|
+
attr_reader :action_name
|
23
|
+
# See Mack::CookieJar for more information.
|
24
|
+
attr_reader :cookies
|
25
|
+
|
26
|
+
def initialize(request, response, cookies)
|
27
|
+
@request = request
|
28
|
+
@response = response
|
29
|
+
@render_options = {}
|
30
|
+
@render_performed = false
|
31
|
+
@controller_name = params(:controller)
|
32
|
+
@action_name = params(:action)
|
33
|
+
@cookies = cookies
|
34
|
+
end
|
35
|
+
|
36
|
+
# Gives access to all the parameters for this request.
|
37
|
+
def params(key)
|
38
|
+
self.request.params(key)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Gives access to the session. See Mack::Session for more information.
|
42
|
+
def session
|
43
|
+
self.request.session
|
44
|
+
end
|
45
|
+
|
46
|
+
# This does the heavy lifting for controllers. It calls the action, and then completes the rendering
|
47
|
+
# of the action to a String to send back to Rack.
|
48
|
+
def run
|
49
|
+
run_filters(:before)
|
50
|
+
# check to see if this controller responds to this action.
|
51
|
+
# only run public methods!
|
52
|
+
if self.public_methods.include?(self.action_name)
|
53
|
+
# call the action and capture the results to a variable.
|
54
|
+
@result_of_action_called = self.send(self.action_name)
|
55
|
+
else
|
56
|
+
# there is no action on this controller, so call the render method
|
57
|
+
# which will check the view directory and run action.html.erb if it exists.
|
58
|
+
render(:action => self.action_name)
|
59
|
+
end
|
60
|
+
run_filters(:after)
|
61
|
+
# do the work of rendering.
|
62
|
+
@final_rendered_action = complete_layout_render(complete_action_render)
|
63
|
+
run_filters(:after_render)
|
64
|
+
@final_rendered_action
|
65
|
+
end
|
66
|
+
|
67
|
+
# This method can be called from within an action. This 'registers' the render that you
|
68
|
+
# would like to happen once the action is completed.
|
69
|
+
#
|
70
|
+
# It's important to note that calling render in an action does NOT end the processing of
|
71
|
+
# the action. The action will continue to process unless you explicity put 'return' before the
|
72
|
+
# render call.
|
73
|
+
#
|
74
|
+
# If you call render twice in an action then a Mack::Errors::DoubleRender error will be thrown.
|
75
|
+
#
|
76
|
+
# An implicit render will happen if one is not specified in the action.
|
77
|
+
#
|
78
|
+
# Only :action and :text will get layouts wrapped around them.
|
79
|
+
#
|
80
|
+
# Examples:
|
81
|
+
# class MyAwesomeController < Mack::Controller::Base
|
82
|
+
# # This will render the text 'Hello World!' to the screen.
|
83
|
+
# def index
|
84
|
+
# render(:text => "Hello World!")
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# # This will render MACK_ROOT/views/my_awesome_controller/foo.html.erb
|
88
|
+
# def show
|
89
|
+
# render(:action => :foo)
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# # This will raise a Mack::Errors::DoubleRender error.
|
93
|
+
# def edit
|
94
|
+
# render(:text => "Hello World!")
|
95
|
+
# render(:action => :foo)
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# # This will render MACK_ROOT/views/my_awesome_controller/delete.html.erb
|
99
|
+
# def delete
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# # This will render the text 'Hello World!' to the screen. Assuming that
|
103
|
+
# # there is no file: MACK_ROOT/views/my_awesome_controller/update.html.erb
|
104
|
+
# # The reason for this is if the view for the action doesn't exist, and the
|
105
|
+
# # last thing returned from the action is a String, that string will be returned.
|
106
|
+
# def update
|
107
|
+
# "Hello World!"
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# # This will raise a Mack::Errors::InvalidRenderType error. Assuming that
|
111
|
+
# # there is no file: MACK_ROOT/views/my_awesome_controller/create.html.erb
|
112
|
+
# def create
|
113
|
+
# @user = User.find(1)
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# # This will raise a Errno::ENOENT error. Assuming that
|
117
|
+
# # there is no file: MACK_ROOT/views/my_awesome_controller/bar.html.erb
|
118
|
+
# def bar
|
119
|
+
# render(:action => "bar")
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# # This will render a file from the public directory. Files served from the
|
123
|
+
# # public directory do NOT get layouts. The default file extension for files
|
124
|
+
# # served from the public directory is .html. This can be overridden with the
|
125
|
+
# # :ext => ".<ext>" option.
|
126
|
+
# def show_public_file
|
127
|
+
# render(:public => "my/files/foo")
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# # This will render a file from the public directory. Files served from the
|
131
|
+
# # public directory do NOT get layouts. The default file extension for files
|
132
|
+
# # served from the public directory is .html. This can be overridden with the
|
133
|
+
# # :ext => ".<ext>" option.
|
134
|
+
# def show_public_xml_file
|
135
|
+
# render(:public => "my/files/foo", :ext => ".xml")
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# # This will render a partial. In this case it will look for:
|
139
|
+
# # MACK_ROOT/views/my_awesome_controller/_latest_news.html.erb
|
140
|
+
# # Partials do NOT get wrapped in layouts.
|
141
|
+
# def latest_news
|
142
|
+
# render(:partial => :latest_news)
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# # This will render a partial. In this case it will look for:
|
146
|
+
# # MACK_ROOT/views/some_other/_old_news.html.erb
|
147
|
+
# # Partials do NOT get wrapped in layouts.
|
148
|
+
# def latest_news
|
149
|
+
# render(:partial => "some_other/old_news")
|
150
|
+
# end
|
151
|
+
# end
|
152
|
+
def render(options = {:action => self.action_name})
|
153
|
+
raise Mack::Errors::DoubleRender.new if render_performed?
|
154
|
+
unless options[:action] || options[:text]
|
155
|
+
options = {:layout => false}.merge(options)
|
156
|
+
end
|
157
|
+
@render_options = options
|
158
|
+
@render_performed = true
|
159
|
+
end
|
160
|
+
|
161
|
+
# This will redirect the request to the specified url. A default status of
|
162
|
+
# 302, Moved Temporarily, is set if no status is specified. A simple HTML
|
163
|
+
# page is rendered in case the redirect does not occur.
|
164
|
+
def redirect_to(url, status = 302)
|
165
|
+
response.status = status
|
166
|
+
response[:location] = url
|
167
|
+
render(:text => redirect_html(request.path_info, url, status))
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns true/false depending on whether the render action has been called yet.
|
171
|
+
def render_performed?
|
172
|
+
@render_performed
|
173
|
+
end
|
174
|
+
|
175
|
+
# Gives access to the MACK_DEFAULT_LOGGER.
|
176
|
+
def logger
|
177
|
+
MACK_DEFAULT_LOGGER
|
178
|
+
end
|
179
|
+
|
180
|
+
private
|
181
|
+
|
182
|
+
def run_filters(type)
|
183
|
+
filters = self.class.controller_filters[type]
|
184
|
+
return true if filters.empty?
|
185
|
+
filters.each do |filter|
|
186
|
+
if filter.run?(self.action_name.to_sym)
|
187
|
+
r = self.send(filter.filter_method)
|
188
|
+
raise Mack::Errors::FilterChainHalted.new(filter.filter_method) unless r
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def complete_layout_render(action_content)
|
194
|
+
@content_for_layout = action_content
|
195
|
+
# if @render_options[:action] || @render_options[:text]
|
196
|
+
# only action and text should get a layout.
|
197
|
+
# if a layout is specified, use that:
|
198
|
+
# i use has_key? here because we want people
|
199
|
+
# to be able to override layout with nil/false.
|
200
|
+
if @render_options.has_key?(:layout)
|
201
|
+
if @render_options[:layout]
|
202
|
+
return Mack::ViewBinder.new(self).render(@render_options.merge({:action => "layouts/#{@render_options[:layout]}"}))
|
203
|
+
else
|
204
|
+
# someone has specified NO layout via nil/false
|
205
|
+
return @content_for_layout
|
206
|
+
end
|
207
|
+
else layout
|
208
|
+
# use the layout specified by the layout method
|
209
|
+
return Mack::ViewBinder.new(self).render(@render_options.merge({:action => "layouts/#{layout}"}))
|
210
|
+
end
|
211
|
+
# end
|
212
|
+
@content_for_layout
|
213
|
+
end
|
214
|
+
|
215
|
+
def complete_action_render
|
216
|
+
if render_performed?
|
217
|
+
return Mack::ViewBinder.new(self, @render_options).render(@render_options)
|
218
|
+
else
|
219
|
+
begin
|
220
|
+
# try action.html.erb
|
221
|
+
return Mack::ViewBinder.new(self).render({:action => self.action_name})
|
222
|
+
rescue Exception => e
|
223
|
+
if @result_of_action_called.is_a?(String)
|
224
|
+
@render_options[:text] = @result_of_action_called
|
225
|
+
return Mack::ViewBinder.new(self).render(@render_options)
|
226
|
+
end
|
227
|
+
raise e
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end # complete_action_render
|
231
|
+
|
232
|
+
def layout
|
233
|
+
:application
|
234
|
+
end
|
235
|
+
|
236
|
+
public
|
237
|
+
class << self
|
238
|
+
|
239
|
+
def before_filter(meth, options = {})
|
240
|
+
add_filter(:before, meth, options)
|
241
|
+
end
|
242
|
+
|
243
|
+
def after_filter(meth, options = {})
|
244
|
+
add_filter(:after, meth, options)
|
245
|
+
end
|
246
|
+
|
247
|
+
def after_render_filter(meth, options = {})
|
248
|
+
add_filter(:after_render, meth, options)
|
249
|
+
end
|
250
|
+
|
251
|
+
def add_filter(type, meth, options)
|
252
|
+
controller_filters[type.to_sym] << Mack::Controller::Filter.new(meth, options)
|
253
|
+
end
|
254
|
+
|
255
|
+
def controller_filters
|
256
|
+
@controller_filters = {:before => [], :after => [], :after_render => []} unless @controller_filters
|
257
|
+
@controller_filters
|
258
|
+
end
|
259
|
+
|
260
|
+
# Sets a layout to be used by a particular controller.
|
261
|
+
#
|
262
|
+
# Example:
|
263
|
+
# class MyAwesomeController < Mack::Controller::Base
|
264
|
+
# # Sets all actions to use: "#{MACK_ROOT}/app/views/layouts/dark.html.erb" as they're layout.
|
265
|
+
# layout :dark
|
266
|
+
#
|
267
|
+
# def index
|
268
|
+
# # Sets this action to use: "#{MACK_ROOT}/app/views/layouts/bright.html.erb" as it's layout.
|
269
|
+
# render(:text => "Welcome...", :layout => :bright)
|
270
|
+
# end
|
271
|
+
#
|
272
|
+
# def index
|
273
|
+
# # This will no use a layout.
|
274
|
+
# render(:text => "Welcome...", :layout => false)
|
275
|
+
# end
|
276
|
+
# end
|
277
|
+
#
|
278
|
+
# The default layout is "#{MACK_ROOT}/app/views/layouts/application.html.erb".
|
279
|
+
#
|
280
|
+
# If a layout is specified, and it doesn't exist a Mack::Errors::UnknownLayout error will be raised.
|
281
|
+
def layout(lay)
|
282
|
+
self.class_eval do
|
283
|
+
define_method(:layout) do
|
284
|
+
lay
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end # layout
|
288
|
+
|
289
|
+
end # class << self
|
290
|
+
|
291
|
+
end # Base
|
292
|
+
end # Controller
|
293
|
+
end # Mack
|