mack 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|