actionpack 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG +604 -0
- data/MIT-LICENSE +21 -0
- data/README +418 -0
- data/RUNNING_UNIT_TESTS +14 -0
- data/examples/.htaccess +24 -0
- data/examples/address_book/index.rhtml +33 -0
- data/examples/address_book/layout.rhtml +8 -0
- data/examples/address_book_controller.cgi +9 -0
- data/examples/address_book_controller.fcgi +6 -0
- data/examples/address_book_controller.rb +52 -0
- data/examples/address_book_controller.rbx +4 -0
- data/examples/benchmark.rb +52 -0
- data/examples/benchmark_with_ar.fcgi +89 -0
- data/examples/blog_controller.cgi +53 -0
- data/examples/debate/index.rhtml +14 -0
- data/examples/debate/new_topic.rhtml +22 -0
- data/examples/debate/topic.rhtml +32 -0
- data/examples/debate_controller.cgi +57 -0
- data/install.rb +93 -0
- data/lib/action_controller.rb +47 -0
- data/lib/action_controller/assertions/action_pack_assertions.rb +166 -0
- data/lib/action_controller/assertions/active_record_assertions.rb +65 -0
- data/lib/action_controller/base.rb +626 -0
- data/lib/action_controller/benchmarking.rb +49 -0
- data/lib/action_controller/cgi_ext/cgi_ext.rb +43 -0
- data/lib/action_controller/cgi_ext/cgi_methods.rb +91 -0
- data/lib/action_controller/cgi_process.rb +123 -0
- data/lib/action_controller/filters.rb +279 -0
- data/lib/action_controller/flash.rb +65 -0
- data/lib/action_controller/layout.rb +143 -0
- data/lib/action_controller/request.rb +92 -0
- data/lib/action_controller/rescue.rb +94 -0
- data/lib/action_controller/response.rb +15 -0
- data/lib/action_controller/scaffolding.rb +183 -0
- data/lib/action_controller/session/active_record_store.rb +72 -0
- data/lib/action_controller/session/drb_server.rb +9 -0
- data/lib/action_controller/session/drb_store.rb +31 -0
- data/lib/action_controller/support/class_attribute_accessors.rb +57 -0
- data/lib/action_controller/support/class_inheritable_attributes.rb +37 -0
- data/lib/action_controller/support/clean_logger.rb +10 -0
- data/lib/action_controller/support/cookie_performance_fix.rb +121 -0
- data/lib/action_controller/support/inflector.rb +70 -0
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +28 -0
- data/lib/action_controller/templates/rescues/diagnostics.rhtml +22 -0
- data/lib/action_controller/templates/rescues/layout.rhtml +29 -0
- data/lib/action_controller/templates/rescues/missing_template.rhtml +2 -0
- data/lib/action_controller/templates/rescues/template_error.rhtml +26 -0
- data/lib/action_controller/templates/rescues/unknown_action.rhtml +2 -0
- data/lib/action_controller/templates/scaffolds/edit.rhtml +6 -0
- data/lib/action_controller/templates/scaffolds/layout.rhtml +29 -0
- data/lib/action_controller/templates/scaffolds/list.rhtml +24 -0
- data/lib/action_controller/templates/scaffolds/new.rhtml +5 -0
- data/lib/action_controller/templates/scaffolds/show.rhtml +9 -0
- data/lib/action_controller/test_process.rb +194 -0
- data/lib/action_controller/url_rewriter.rb +153 -0
- data/lib/action_view.rb +40 -0
- data/lib/action_view/base.rb +253 -0
- data/lib/action_view/helpers/active_record_helper.rb +171 -0
- data/lib/action_view/helpers/date_helper.rb +223 -0
- data/lib/action_view/helpers/debug_helper.rb +17 -0
- data/lib/action_view/helpers/form_helper.rb +176 -0
- data/lib/action_view/helpers/form_options_helper.rb +169 -0
- data/lib/action_view/helpers/tag_helper.rb +59 -0
- data/lib/action_view/helpers/text_helper.rb +129 -0
- data/lib/action_view/helpers/url_helper.rb +72 -0
- data/lib/action_view/partials.rb +61 -0
- data/lib/action_view/template_error.rb +84 -0
- data/lib/action_view/vendor/builder.rb +13 -0
- data/lib/action_view/vendor/builder/blankslate.rb +21 -0
- data/lib/action_view/vendor/builder/xmlbase.rb +143 -0
- data/lib/action_view/vendor/builder/xmlevents.rb +63 -0
- data/lib/action_view/vendor/builder/xmlmarkup.rb +288 -0
- data/rakefile +105 -0
- data/test/abstract_unit.rb +9 -0
- data/test/controller/action_pack_assertions_test.rb +295 -0
- data/test/controller/active_record_assertions_test.rb +118 -0
- data/test/controller/cgi_test.rb +142 -0
- data/test/controller/cookie_test.rb +38 -0
- data/test/controller/filters_test.rb +159 -0
- data/test/controller/flash_test.rb +69 -0
- data/test/controller/layout_test.rb +49 -0
- data/test/controller/redirect_test.rb +44 -0
- data/test/controller/render_test.rb +169 -0
- data/test/controller/url_test.rb +318 -0
- data/test/fixtures/layouts/builder.rxml +3 -0
- data/test/fixtures/layouts/standard.rhtml +1 -0
- data/test/fixtures/test/_customer.rhtml +1 -0
- data/test/fixtures/test/greeting.rhtml +1 -0
- data/test/fixtures/test/hello.rxml +4 -0
- data/test/fixtures/test/hello_world.rhtml +1 -0
- data/test/fixtures/test/hello_xml_world.rxml +11 -0
- data/test/fixtures/test/list.rhtml +1 -0
- data/test/template/active_record_helper_test.rb +76 -0
- data/test/template/date_helper_test.rb +103 -0
- data/test/template/form_helper_test.rb +115 -0
- data/test/template/form_options_helper_test.rb +174 -0
- data/test/template/tag_helper_test.rb +18 -0
- data/test/template/text_helper_test.rb +62 -0
- data/test/template/url_helper_test.rb +35 -0
- metadata +154 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
|
3
|
+
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create action
|
4
|
+
# that sets <tt>flash["notice"] = "Succesfully created"</tt> before redirecting to a display action that can then expose
|
5
|
+
# the flash to its template. Actually, that exposure is automatically done. Example:
|
6
|
+
#
|
7
|
+
# class WeblogController < ActionController::Base
|
8
|
+
# def create
|
9
|
+
# # save post
|
10
|
+
# flash["notice"] = "Succesfully created post"
|
11
|
+
# redirect_to :action => "display", :params => { "id" => post.id }
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# def display
|
15
|
+
# # doesn't need to assign the flash notice to the template, that's done automatically
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# display.rhtml
|
20
|
+
# <% if @flash["notice"] %><div class="notice"><%= @flash["notice"] %></div><% end %>
|
21
|
+
#
|
22
|
+
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as many
|
23
|
+
# as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
|
24
|
+
module Flash
|
25
|
+
def self.append_features(base) #:nodoc:
|
26
|
+
super
|
27
|
+
base.before_filter(:fire_flash)
|
28
|
+
base.after_filter(:clear_flash)
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to read a notice you put there or
|
33
|
+
# <tt>flash["notice"] = "hello"</tt> to put a new one.
|
34
|
+
def flash #:doc:
|
35
|
+
if @session["flash"].nil?
|
36
|
+
@session["flash"] = {}
|
37
|
+
@session["flashes"] ||= 0
|
38
|
+
end
|
39
|
+
@session["flash"]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Can be called by any action that would like to keep the current content of the flash around for one more action.
|
43
|
+
def keep_flash #:doc:
|
44
|
+
@session["flashes"] = 0
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
# Records that the contents of @session["flash"] was flashed to the action
|
49
|
+
def fire_flash
|
50
|
+
if @session["flash"]
|
51
|
+
@session["flashes"] += 1 unless @session["flash"].empty?
|
52
|
+
@assigns["flash"] = @session["flash"]
|
53
|
+
else
|
54
|
+
@assigns["flash"] = {}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def clear_flash
|
59
|
+
if @session["flash"] && (@session["flashes"].nil? || @session["flashes"] >= 1)
|
60
|
+
@session["flash"] = {}
|
61
|
+
@session["flashes"] = 0
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
module Layout #:nodoc:
|
3
|
+
def self.append_features(base)
|
4
|
+
super
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.class_eval do
|
7
|
+
alias_method :render_without_layout, :render
|
8
|
+
alias_method :render, :render_with_layout
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
13
|
+
# repeated setups. The inclusion pattern has pages that look like this:
|
14
|
+
#
|
15
|
+
# <%= render "shared/header" %>
|
16
|
+
# Hello World
|
17
|
+
# <%= render "shared/footer" %>
|
18
|
+
#
|
19
|
+
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
|
20
|
+
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
|
21
|
+
#
|
22
|
+
# With layouts, you can flip it around and have the common structure know where to insert changing content. This means
|
23
|
+
# that the header and footer is only mentioned in one place, like this:
|
24
|
+
#
|
25
|
+
# <!-- The header part of this layout -->
|
26
|
+
# <%= @content_for_layout %>
|
27
|
+
# <!-- The footer part of this layout -->
|
28
|
+
#
|
29
|
+
# And then you have content pages that look like this:
|
30
|
+
#
|
31
|
+
# hello world
|
32
|
+
#
|
33
|
+
# Not a word about common structures. At rendering time, the content page is computed and then inserted in the layout,
|
34
|
+
# like this:
|
35
|
+
#
|
36
|
+
# <!-- The header part of this layout -->
|
37
|
+
# hello world
|
38
|
+
# <!-- The footer part of this layout -->
|
39
|
+
#
|
40
|
+
# == Accessing shared variables
|
41
|
+
#
|
42
|
+
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
|
43
|
+
# references that won't materialize before rendering time:
|
44
|
+
#
|
45
|
+
# <h1><%= @page_title %></h1>
|
46
|
+
# <%= @content_for_layout %>
|
47
|
+
#
|
48
|
+
# ...and content pages that fulfill these references _at_ rendering time:
|
49
|
+
#
|
50
|
+
# <% @page_title = "Welcome" %>
|
51
|
+
# Off-world colonies offers you a chance to start a new life
|
52
|
+
#
|
53
|
+
# The result after rendering is:
|
54
|
+
#
|
55
|
+
# <h1>Welcome</h1>
|
56
|
+
# Off-world colonies offers you a chance to start a new life
|
57
|
+
#
|
58
|
+
# == Inheritance for layouts
|
59
|
+
#
|
60
|
+
# Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples:
|
61
|
+
#
|
62
|
+
# class BankController < ActionController::Base
|
63
|
+
# layout "layouts/bank_standard"
|
64
|
+
#
|
65
|
+
# class InformationController < BankController
|
66
|
+
#
|
67
|
+
# class VaultController < BankController
|
68
|
+
# layout :access_level_layout
|
69
|
+
#
|
70
|
+
# class EmployeeController < BankController
|
71
|
+
# layout nil
|
72
|
+
#
|
73
|
+
# The InformationController uses "layouts/bank_standard" inherited from the BankController, the VaultController overwrites
|
74
|
+
# and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
|
75
|
+
#
|
76
|
+
# == Types of layouts
|
77
|
+
#
|
78
|
+
# Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
|
79
|
+
# you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
|
80
|
+
# be done either by specifying a method reference as a symbol or using an inline method (as a proc).
|
81
|
+
#
|
82
|
+
# The method reference is the preferred approach to variable layouts and is used like this:
|
83
|
+
#
|
84
|
+
# class WeblogController < ActionController::Base
|
85
|
+
# layout :writers_and_readers
|
86
|
+
#
|
87
|
+
# def index
|
88
|
+
# # fetching posts
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# private
|
92
|
+
# def writers_and_readers
|
93
|
+
# logged_in? ? "writer_layout" : "reader_layout"
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
|
97
|
+
# is logged in or not.
|
98
|
+
#
|
99
|
+
# If you want to use an inline method, such as a proc, do something like this:
|
100
|
+
#
|
101
|
+
# class WeblogController < ActionController::Base
|
102
|
+
# layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
|
103
|
+
#
|
104
|
+
# Of course, the most common way of specifying a layout is still just as a plain template path:
|
105
|
+
#
|
106
|
+
# class WeblogController < ActionController::Base
|
107
|
+
# layout "layouts/weblog_standard"
|
108
|
+
module ClassMethods
|
109
|
+
# If a layout is specified, all actions rendered through render and render_action will have their result assigned
|
110
|
+
# to <tt>@content_for_layout</tt>, which can then be used by the layout to insert their contents with
|
111
|
+
# <tt><%= @content_for_layout %></tt>. This layout can itself depend on instance variables assigned during action
|
112
|
+
# performance and have access to them as any normal template would.
|
113
|
+
def layout(template_name)
|
114
|
+
write_inheritable_attribute "layout", template_name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
|
119
|
+
# is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
|
120
|
+
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
|
121
|
+
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
|
122
|
+
def active_layout(passed_layout = nil)
|
123
|
+
layout = passed_layout || self.class.read_inheritable_attribute("layout")
|
124
|
+
active_layout = case layout
|
125
|
+
when Symbol then send(layout)
|
126
|
+
when Proc then layout.call(self)
|
127
|
+
when String then layout
|
128
|
+
end
|
129
|
+
active_layout.include?("/") ? active_layout : "layouts/#{active_layout}" if active_layout
|
130
|
+
end
|
131
|
+
|
132
|
+
def render_with_layout(template_name = "#{controller_name}/#{action_name}", status = nil, layout = nil) #:nodoc:
|
133
|
+
if layout || active_layout
|
134
|
+
add_variables_to_assigns
|
135
|
+
logger.info("Rendering #{template_name} within #{layout || active_layout}") unless logger.nil?
|
136
|
+
@content_for_layout = @template.render_file(template_name, true)
|
137
|
+
render_without_layout(layout || self.active_layout, status)
|
138
|
+
else
|
139
|
+
render_without_layout(template_name, status)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module ActionController
|
2
|
+
class AbstractRequest #:nodoc:
|
3
|
+
# Returns both GET and POST parameters in a single hash.
|
4
|
+
def parameters
|
5
|
+
@parameters ||= request_parameters.update(query_parameters)
|
6
|
+
end
|
7
|
+
|
8
|
+
def method
|
9
|
+
env['REQUEST_METHOD'].downcase.intern
|
10
|
+
end
|
11
|
+
|
12
|
+
def get?
|
13
|
+
method == :get
|
14
|
+
end
|
15
|
+
|
16
|
+
def post?
|
17
|
+
method == :post
|
18
|
+
end
|
19
|
+
|
20
|
+
def put?
|
21
|
+
method == :put
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete?
|
25
|
+
method == :delete
|
26
|
+
end
|
27
|
+
|
28
|
+
# Determine originating IP address. REMOTE_ADDR is the standard
|
29
|
+
# but will fail if the user is behind a proxy. HTTP_CLIENT_IP and/or
|
30
|
+
# HTTP_X_FORWARDED_FOR are set by proxies so check for these before
|
31
|
+
# falling back to REMOTE_ADDR. HTTP_X_FORWARDED_FOR may be a comma-
|
32
|
+
# delimited list in the case of multiple chained proxies; the first is
|
33
|
+
# the originating IP.
|
34
|
+
def remote_ip
|
35
|
+
if env['HTTP_CLIENT_IP']
|
36
|
+
env['HTTP_CLIENT_IP']
|
37
|
+
elsif env['HTTP_X_FORWARDED_FOR']
|
38
|
+
env['HTTP_X_FORWARDED_FOR'].split(',').reject { |ip|
|
39
|
+
ip =~ /^unknown$|^(10|172\.16|192\.168)\./i
|
40
|
+
}.first.strip
|
41
|
+
else
|
42
|
+
env['REMOTE_ADDR']
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def request_uri
|
47
|
+
env["REQUEST_URI"]
|
48
|
+
end
|
49
|
+
|
50
|
+
def protocol
|
51
|
+
port == 443 ? "https://" : "http://"
|
52
|
+
end
|
53
|
+
|
54
|
+
def path
|
55
|
+
request_uri ? request_uri.split("?").first : ""
|
56
|
+
end
|
57
|
+
|
58
|
+
def port
|
59
|
+
env["SERVER_PORT"].to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
def host_with_port
|
63
|
+
if (protocol == "http://" && port == 80) || (protocol == "https://" && port == 443)
|
64
|
+
host
|
65
|
+
else
|
66
|
+
host + ":#{port}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Must be implemented in the concrete request
|
71
|
+
def query_parameters
|
72
|
+
end
|
73
|
+
|
74
|
+
def request_parameters
|
75
|
+
end
|
76
|
+
|
77
|
+
def env
|
78
|
+
end
|
79
|
+
|
80
|
+
def host
|
81
|
+
end
|
82
|
+
|
83
|
+
def cookies
|
84
|
+
end
|
85
|
+
|
86
|
+
def session
|
87
|
+
end
|
88
|
+
|
89
|
+
def reset_session
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
# Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view
|
3
|
+
# (with a nice user-friendly explanation) or for the developers view (with tons of debugging information). The developers view
|
4
|
+
# is already implemented by the Action Controller, but the public view should be tailored to your specific application. So too
|
5
|
+
# could the decision on whether something is a public or a developer request.
|
6
|
+
#
|
7
|
+
# You can tailor the rescuing behavior and appearance by overwriting the following two stub methods.
|
8
|
+
module Rescue
|
9
|
+
def self.append_features(base) #:nodoc:
|
10
|
+
super
|
11
|
+
base.class_eval do
|
12
|
+
alias_method :perform_action_without_rescue, :perform_action
|
13
|
+
alias_method :perform_action, :perform_action_with_rescue
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
# Exception handler called when the performance of an action raises an exception.
|
19
|
+
def rescue_action(exception)
|
20
|
+
log_error(exception) unless logger.nil?
|
21
|
+
|
22
|
+
if consider_all_requests_local || local_request?
|
23
|
+
rescue_action_locally(exception)
|
24
|
+
else
|
25
|
+
rescue_action_in_public(exception)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Overwrite to implement custom logging of errors. By default logs as fatal.
|
30
|
+
def log_error(exception) #:doc:
|
31
|
+
if ActionView::TemplateError === exception
|
32
|
+
logger.fatal(exception.to_s)
|
33
|
+
else
|
34
|
+
logger.fatal(
|
35
|
+
"\n\n#{exception.class} (#{exception.message}):\n " +
|
36
|
+
clean_backtrace(exception).join("\n ") +
|
37
|
+
"\n\n"
|
38
|
+
)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>).
|
43
|
+
def rescue_action_in_public(exception) #:doc:
|
44
|
+
render_text "<html><body><h1>Application error (Rails)</h1></body></html>"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Overwrite to expand the meaning of a local request in order to show local rescues on other occurances than
|
48
|
+
# the remote IP being 127.0.0.1. For example, this could include the IP of the developer machine when debugging
|
49
|
+
# remotely.
|
50
|
+
def local_request? #:doc:
|
51
|
+
@request.remote_addr == "127.0.0.1"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Renders a detailed diagnostics screen on action exceptions.
|
55
|
+
def rescue_action_locally(exception)
|
56
|
+
@exception = exception
|
57
|
+
@rescues_path = File.dirname(__FILE__) + "/templates/rescues/"
|
58
|
+
add_variables_to_assigns
|
59
|
+
@contents = @template.render_file(template_path_for_local_rescue(exception), false)
|
60
|
+
|
61
|
+
@headers["Content-Type"] = "text/html"
|
62
|
+
render_file(rescues_path("layout"), "500 Internal Error")
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def perform_action_with_rescue #:nodoc:
|
67
|
+
begin
|
68
|
+
perform_action_without_rescue
|
69
|
+
rescue Exception => exception
|
70
|
+
rescue_action(exception)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def rescues_path(template_name)
|
75
|
+
File.dirname(__FILE__) + "/templates/rescues/#{template_name}.rhtml"
|
76
|
+
end
|
77
|
+
|
78
|
+
def template_path_for_local_rescue(exception)
|
79
|
+
rescues_path(
|
80
|
+
case exception
|
81
|
+
when MissingTemplate then "missing_template"
|
82
|
+
when UnknownAction then "unknown_action"
|
83
|
+
when ActionView::TemplateError then "template_error"
|
84
|
+
else "diagnostics"
|
85
|
+
end
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
def clean_backtrace(exception)
|
90
|
+
base_dir = File.expand_path(File.dirname(__FILE__) + "/../../../../")
|
91
|
+
exception.backtrace.collect { |line| line.gsub(base_dir, "").gsub("/public/../config/environments/../../", "").gsub("/public/../", "") }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActionController
|
2
|
+
class AbstractResponse #:nodoc:
|
3
|
+
DEFAULT_HEADERS = { "Cache-Control" => "no-cache", "cookie" => [] }
|
4
|
+
attr_accessor :body, :headers, :session, :cookies, :assigns, :template, :redirected_to, :redirected_to_method_params
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@body, @headers, @session, @assigns = "", DEFAULT_HEADERS.dup, [], []
|
8
|
+
end
|
9
|
+
|
10
|
+
def redirect(to_url)
|
11
|
+
@headers["Status"] = "302 Moved"
|
12
|
+
@headers["location"] = to_url
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module ActionController
|
2
|
+
module Scaffolding # :nodoc:
|
3
|
+
def self.append_features(base)
|
4
|
+
super
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Scaffolding is a way to quickly put an Active Record class online by providing a series of standardized actions
|
9
|
+
# for listing, showing, creating, updating, and destroying objects of the class. These standardized actions come
|
10
|
+
# with both controller logic and default templates that through introspection already know which fields to display
|
11
|
+
# and which input types to use. Example:
|
12
|
+
#
|
13
|
+
# class WeblogController < ActionController::Base
|
14
|
+
# scaffold :entry
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# This tiny piece of code will add all of the following methods to the controller:
|
18
|
+
#
|
19
|
+
# class WeblogController < ActionController::Base
|
20
|
+
# def index
|
21
|
+
# list
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def list
|
25
|
+
# @entries = Entry.find_all
|
26
|
+
# render_scaffold "list"
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def show
|
30
|
+
# @entry = Entry.find(@params["id"])
|
31
|
+
# render_scaffold
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# def destroy
|
35
|
+
# Entry.find(@params["id"]).destroy
|
36
|
+
# redirect_to :action => "list"
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# def new
|
40
|
+
# @entry = Entry.new
|
41
|
+
# render_scaffold
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# def create
|
45
|
+
# @entry = Entry.new(@params["entry"])
|
46
|
+
# if @entry.save
|
47
|
+
# flash["notice"] = "Entry was succesfully created"
|
48
|
+
# redirect_to :action => "list"
|
49
|
+
# else
|
50
|
+
# render "entry/new"
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# def edit
|
55
|
+
# @entry = Entry.find(@params["id"])
|
56
|
+
# render_scaffold
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def update
|
60
|
+
# @entry = Entry.find(@params["entry"]["id"])
|
61
|
+
# @entry.attributes = @params["entry"]
|
62
|
+
#
|
63
|
+
# if @entry.save
|
64
|
+
# flash["notice"] = "Entry was succesfully updated"
|
65
|
+
# redirect_to :action => "show/" + @entry.id.to_s
|
66
|
+
# else
|
67
|
+
# render "entry/edit"
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# The <tt>render_scaffold</tt> method will first check to see if you've made your own template (like "weblog/show.rhtml" for
|
73
|
+
# the show action) and if not, then render the generic template for that action. This gives you the possibility of using the
|
74
|
+
# scaffold while you're building your specific application. Start out with a totally generic setup, then replace one template
|
75
|
+
# and one action at a time while relying on the rest of the scaffolded templates and actions.
|
76
|
+
module ClassMethods
|
77
|
+
# Adds a swath of generic CRUD actions to the controller. The +model_id+ is automatically converted into a class name unless
|
78
|
+
# one is specifically provide through <tt>options[:class_name]</tt>. So <tt>scaffold :post</tt> would use Post as the class
|
79
|
+
# and @post/@posts for the instance variables.
|
80
|
+
#
|
81
|
+
# It's possible to use more than one scaffold in a single controller by specifying <tt>options[:suffix] = true</tt>. This will
|
82
|
+
# make <tt>scaffold :post, :suffix => true</tt> use method names like list_post, show_post, and create_post
|
83
|
+
# instead of just list, show, and post. If suffix is used, then no index method is added.
|
84
|
+
def scaffold(model_id, options = {})
|
85
|
+
validate_options([ :class_name, :suffix ], options.keys)
|
86
|
+
|
87
|
+
require "#{model_id.id2name}" rescue logger.warn "Couldn't auto-require #{model_id.id2name}.rb" unless logger.nil?
|
88
|
+
|
89
|
+
singular_name = model_id.id2name
|
90
|
+
class_name = options[:class_name] || Inflector.camelize(singular_name)
|
91
|
+
plural_name = Inflector.pluralize(singular_name)
|
92
|
+
suffix = options[:suffix] ? "_#{singular_name}" : ""
|
93
|
+
|
94
|
+
unless options[:suffix]
|
95
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
96
|
+
def index
|
97
|
+
list
|
98
|
+
end
|
99
|
+
end_eval
|
100
|
+
end
|
101
|
+
|
102
|
+
module_eval <<-"end_eval", __FILE__, __LINE__
|
103
|
+
def list#{suffix}
|
104
|
+
@#{plural_name} = #{class_name}.find_all
|
105
|
+
render#{suffix}_scaffold "list#{suffix}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def show#{suffix}
|
109
|
+
@#{singular_name} = #{class_name}.find(@params["id"])
|
110
|
+
render#{suffix}_scaffold
|
111
|
+
end
|
112
|
+
|
113
|
+
def destroy#{suffix}
|
114
|
+
#{class_name}.find(@params["id"]).destroy
|
115
|
+
redirect_to :action => "list#{suffix}"
|
116
|
+
end
|
117
|
+
|
118
|
+
def new#{suffix}
|
119
|
+
@#{singular_name} = #{class_name}.new
|
120
|
+
render#{suffix}_scaffold
|
121
|
+
end
|
122
|
+
|
123
|
+
def create#{suffix}
|
124
|
+
@#{singular_name} = #{class_name}.new(@params["#{singular_name}"])
|
125
|
+
if @#{singular_name}.save
|
126
|
+
flash["notice"] = "#{class_name} was succesfully created"
|
127
|
+
redirect_to :action => "list#{suffix}"
|
128
|
+
else
|
129
|
+
render "#{singular_name}/new#{suffix}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def edit#{suffix}
|
134
|
+
@#{singular_name} = #{class_name}.find(@params["id"])
|
135
|
+
render#{suffix}_scaffold
|
136
|
+
end
|
137
|
+
|
138
|
+
def update#{suffix}
|
139
|
+
@#{singular_name} = #{class_name}.find(@params["#{singular_name}"]["id"])
|
140
|
+
@#{singular_name}.attributes = @params["#{singular_name}"]
|
141
|
+
|
142
|
+
if @#{singular_name}.save
|
143
|
+
flash["notice"] = "#{class_name} was succesfully updated"
|
144
|
+
redirect_to :action => "show#{suffix}/" + @#{singular_name}.id.to_s
|
145
|
+
else
|
146
|
+
render "#{singular_name}/edit#{suffix}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def render#{suffix}_scaffold(action = caller_method_name(caller))
|
152
|
+
if template_exists?("\#{controller_name}/\#{action}")
|
153
|
+
render_action(action)
|
154
|
+
else
|
155
|
+
@scaffold_class = #{class_name}
|
156
|
+
@scaffold_singular_name, @scaffold_plural_name = "#{singular_name}", "#{plural_name}"
|
157
|
+
@scaffold_suffix = "#{suffix}"
|
158
|
+
add_instance_variables_to_assigns
|
159
|
+
|
160
|
+
@content_for_layout = @template.render_file(scaffold_path(action.sub(/#{suffix}$/, "")), false)
|
161
|
+
self.active_layout ? render_file(self.active_layout, "200 OK", true) : render_file(scaffold_path("layout"))
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def scaffold_path(template_name)
|
166
|
+
File.dirname(__FILE__) + "/templates/scaffolds/" + template_name + ".rhtml"
|
167
|
+
end
|
168
|
+
|
169
|
+
def caller_method_name(caller)
|
170
|
+
caller.first.scan(/`(.*)'/).first.first # ' ruby-mode
|
171
|
+
end
|
172
|
+
end_eval
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
# Raises an exception if an invalid option has been specified to prevent misspellings from slipping through
|
177
|
+
def validate_options(valid_option_keys, supplied_option_keys)
|
178
|
+
unknown_option_keys = supplied_option_keys - valid_option_keys
|
179
|
+
raise(ActionController::ActionControllerError, "Unknown options: #{unknown_option_keys}") unless unknown_option_keys.empty?
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|