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
data/lib/action_view.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2004 David Heinemeier Hansson
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'rubygems'
|
26
|
+
require 'builder'
|
27
|
+
rescue LoadError
|
28
|
+
# RubyGems is not available, use included Builder
|
29
|
+
$:.unshift(File.dirname(__FILE__) + "/action_view/vendor")
|
30
|
+
require 'action_view/vendor/builder'
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'action_view/base'
|
34
|
+
require 'action_view/partials'
|
35
|
+
|
36
|
+
ActionView::Base.class_eval do
|
37
|
+
include ActionView::Partials
|
38
|
+
end
|
39
|
+
|
40
|
+
ActionView::Base.load_helpers(File.dirname(__FILE__) + "/action_view/helpers/")
|
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module ActionView #:nodoc:
|
4
|
+
class ActionViewError < StandardError #:nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
# Action View templates can be written in two ways. If the template file has a +.rhtml+ extension then it uses a mixture of ERb
|
8
|
+
# (included in Ruby) and HTML. If the template file has a +.rxml+ extension then Jim Weirich's Builder::XmlMarkup library is used.
|
9
|
+
#
|
10
|
+
# = ERb
|
11
|
+
#
|
12
|
+
# You trigger ERb by using embeddings such as <% %> and <%= %>. The difference is whether you want output or not. Consider the
|
13
|
+
# following loop for names:
|
14
|
+
#
|
15
|
+
# <b>Names of all the people</b>
|
16
|
+
# <% for person in @people %>
|
17
|
+
# Name: <%= person.name %><br/>
|
18
|
+
# <% end %>
|
19
|
+
#
|
20
|
+
# The loop is setup in regular embedding tags (<% %>) and the name is written using the output embedding tag (<%= %>). Note that this
|
21
|
+
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERb templates. So this would be wrong:
|
22
|
+
#
|
23
|
+
# Hi, Mr. <% puts "Frodo" %>
|
24
|
+
#
|
25
|
+
# (If you absolutely must write from within a function, you can use the TextHelper#concat)
|
26
|
+
#
|
27
|
+
# == Using sub templates
|
28
|
+
#
|
29
|
+
# Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
|
30
|
+
# classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
|
31
|
+
#
|
32
|
+
# <%= render "shared/header" %>
|
33
|
+
# Something really specific and terrific
|
34
|
+
# <%= render "shared/footer" %>
|
35
|
+
#
|
36
|
+
# As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
|
37
|
+
# result of the rendering. The output embedding writes it to the current template.
|
38
|
+
#
|
39
|
+
# But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
|
40
|
+
# variables defined in using the regular embedding tags. Like this:
|
41
|
+
#
|
42
|
+
# <% @page_title = "A Wonderful Hello" %>
|
43
|
+
# <%= render "shared/header" %>
|
44
|
+
#
|
45
|
+
# Now the header can pick up on the @page_title variable and use it for outputting a title tag:
|
46
|
+
#
|
47
|
+
# <title><%= @page_title %></title>
|
48
|
+
#
|
49
|
+
# == Passing local variables to sub templates
|
50
|
+
#
|
51
|
+
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
|
52
|
+
#
|
53
|
+
# <%= render "shared/header", { "headline" => "Welcome", "person" => person } %>
|
54
|
+
#
|
55
|
+
# These can now be accessed in shared/header with:
|
56
|
+
#
|
57
|
+
# Headline: <%= headline %>
|
58
|
+
# First name: <%= person.first_name %>
|
59
|
+
#
|
60
|
+
# == Template caching
|
61
|
+
#
|
62
|
+
# The parsing of ERb templates are cached by default, but the reading of them are not. This means that the application by default
|
63
|
+
# will reflect changes to the templates immediatly. If you'd like to sacrifice that immediacy for the speed gain given by also
|
64
|
+
# caching the loading of templates (reading from the file systen), you can turn that on with
|
65
|
+
# <tt>ActionView::Base.cache_template_loading = true</tt>.
|
66
|
+
#
|
67
|
+
# == Builder
|
68
|
+
#
|
69
|
+
# Builder templates are a more programatic alternative to ERb. They are especially useful for generating XML content. An +XmlMarkup+ object
|
70
|
+
# named +xml+ is automatically made available to templates with a +.rxml+ extension.
|
71
|
+
#
|
72
|
+
# Here are some basic examples:
|
73
|
+
#
|
74
|
+
# xml.em("emphasized") # => <em>emphasized</em>
|
75
|
+
# xml.em { xml.b("emp & bold") } # => <em><b>emph & bold</b></em>
|
76
|
+
# xml.a("A Link", "href"=>"http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
|
77
|
+
# xm.target("name"=>"compile", "option"=>"fast") # => <target option="fast" name="compile"\>
|
78
|
+
# # NOTE: order of attributes is not specified.
|
79
|
+
#
|
80
|
+
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
|
81
|
+
#
|
82
|
+
# xml.div {
|
83
|
+
# xml.h1(@person.name)
|
84
|
+
# xml.p(@person.bio)
|
85
|
+
# }
|
86
|
+
#
|
87
|
+
# would produce something like:
|
88
|
+
#
|
89
|
+
# <div>
|
90
|
+
# <h1>David Heinemeier Hansson</h1>
|
91
|
+
# <p>A product of Danish Design during the Winter of '79...</p>
|
92
|
+
# </div>
|
93
|
+
#
|
94
|
+
# A full-length RSS example actually used on Basecamp:
|
95
|
+
#
|
96
|
+
# xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
|
97
|
+
# xml.channel do
|
98
|
+
# xml.title(@feed_title)
|
99
|
+
# xml.link(@url)
|
100
|
+
# xml.description "Basecamp: Recent items"
|
101
|
+
# xml.language "en-us"
|
102
|
+
# xml.ttl "40"
|
103
|
+
#
|
104
|
+
# for item in @recent_items
|
105
|
+
# xml.item do
|
106
|
+
# xml.title(item_title(item))
|
107
|
+
# xml.description(item_description(item)) if item_description(item)
|
108
|
+
# xml.pubDate(item_pubDate(item))
|
109
|
+
# xml.guid(@person.firm.account.url + @recent_items.url(item))
|
110
|
+
# xml.link(@person.firm.account.url + @recent_items.url(item))
|
111
|
+
#
|
112
|
+
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
|
113
|
+
# end
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# More builder documentation can be found at http://builder.rubyforge.org.
|
119
|
+
class Base
|
120
|
+
include ERB::Util
|
121
|
+
|
122
|
+
attr_reader :first_render
|
123
|
+
attr_accessor :base_path, :assigns, :template_extension
|
124
|
+
attr_accessor :controller
|
125
|
+
|
126
|
+
# Turn on to cache the reading of templates from the file system. Doing so means that you have to restart the server
|
127
|
+
# when changing templates, but that rendering will be faster.
|
128
|
+
@@cache_template_loading = false
|
129
|
+
cattr_accessor :cache_template_loading
|
130
|
+
|
131
|
+
@@compiled_erb_templates = {}
|
132
|
+
@@loaded_templates = {}
|
133
|
+
|
134
|
+
def self.load_helpers(helper_dir)#:nodoc:
|
135
|
+
Dir.foreach(helper_dir) do |helper_file|
|
136
|
+
next unless helper_file =~ /_helper.rb$/
|
137
|
+
require helper_dir + helper_file
|
138
|
+
helper_module_name = helper_file.capitalize.gsub(/_([a-z])/) { |m| $1.capitalize }[0..-4]
|
139
|
+
|
140
|
+
class_eval("include ActionView::Helpers::#{helper_module_name}") if Helpers.const_defined?(helper_module_name)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def initialize(base_path = nil, assigns_for_first_render = {}, controller = nil)#:nodoc:
|
145
|
+
@base_path, @assigns = base_path, assigns_for_first_render
|
146
|
+
@controller = controller
|
147
|
+
end
|
148
|
+
|
149
|
+
# Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,
|
150
|
+
# it's relative to the template_root, otherwise it's absolute. The hash in <tt>local_assigns</tt>
|
151
|
+
# is made available as local variables.
|
152
|
+
def render_file(template_path, use_full_path = true, local_assigns = {})
|
153
|
+
@first_render = template_path if @first_render.nil?
|
154
|
+
|
155
|
+
if use_full_path
|
156
|
+
template_extension = pick_template_extension(template_path)
|
157
|
+
template_file_name = full_template_path(template_path, template_extension)
|
158
|
+
else
|
159
|
+
template_file_name = template_path
|
160
|
+
template_extension = template_path.split(".").last
|
161
|
+
end
|
162
|
+
|
163
|
+
template_source = read_template_file(template_file_name)
|
164
|
+
|
165
|
+
begin
|
166
|
+
render_template(template_extension, template_source, local_assigns)
|
167
|
+
rescue Exception => e
|
168
|
+
if TemplateError === e
|
169
|
+
e.sub_template_of(template_file_name)
|
170
|
+
raise e
|
171
|
+
else
|
172
|
+
raise TemplateError.new(@base_path, template_file_name, @assigns, template_source, e)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Renders the template present at <tt>template_path</tt> (relative to the template_root).
|
178
|
+
# The hash in <tt>local_assigns</tt> is made available as local variables.
|
179
|
+
def render(template_path, local_assigns = {})
|
180
|
+
render_file(template_path, true, local_assigns)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Renders the +template+ which is given as a string as either rhtml or rxml depending on <tt>template_extension</tt>.
|
184
|
+
# The hash in <tt>local_assigns</tt> is made available as local variables.
|
185
|
+
def render_template(template_extension, template, local_assigns = {})
|
186
|
+
b = binding
|
187
|
+
local_assigns.each { |key, value| eval "#{key} = local_assigns[\"#{key}\"]", b }
|
188
|
+
@assigns.each { |key, value| instance_variable_set "@#{key}", value }
|
189
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
190
|
+
|
191
|
+
send(pick_rendering_method(template_extension), template, binding)
|
192
|
+
end
|
193
|
+
|
194
|
+
def pick_template_extension(template_path)#:nodoc:
|
195
|
+
if erb_template_exists?(template_path)
|
196
|
+
"rhtml"
|
197
|
+
elsif builder_template_exists?(template_path)
|
198
|
+
"rxml"
|
199
|
+
else
|
200
|
+
raise ActionViewError, "No rhtml or rxml template found for #{template_path}"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def pick_rendering_method(template_extension)#:nodoc:
|
205
|
+
(template_extension == "rxml" ? "rxml" : "rhtml") + "_render"
|
206
|
+
end
|
207
|
+
|
208
|
+
def erb_template_exists?(template_path)#:nodoc:
|
209
|
+
template_exists?(template_path, "rhtml")
|
210
|
+
end
|
211
|
+
|
212
|
+
def builder_template_exists?(template_path)#:nodoc:
|
213
|
+
template_exists?(template_path, "rxml")
|
214
|
+
end
|
215
|
+
|
216
|
+
def file_exists?(template_path)#:nodoc:
|
217
|
+
erb_template_exists?(template_path) || builder_template_exists?(template_path)
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns true is the file may be rendered implicitly.
|
221
|
+
def file_public?(template_path)#:nodoc:
|
222
|
+
template_path.split("/").last[0,1] != "_"
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
def full_template_path(template_path, extension)
|
227
|
+
"#{@base_path}/#{template_path}.#{extension}"
|
228
|
+
end
|
229
|
+
|
230
|
+
def template_exists?(template_path, extension)
|
231
|
+
FileTest.exists?(full_template_path(template_path, extension))
|
232
|
+
end
|
233
|
+
|
234
|
+
def read_template_file(template_path)
|
235
|
+
unless cache_template_loading && @@loaded_templates[template_path]
|
236
|
+
@@loaded_templates[template_path] = File.read(template_path)
|
237
|
+
end
|
238
|
+
|
239
|
+
@@loaded_templates[template_path]
|
240
|
+
end
|
241
|
+
|
242
|
+
def rhtml_render(template, binding)
|
243
|
+
@@compiled_erb_templates[template] ||= ERB.new(template)
|
244
|
+
@@compiled_erb_templates[template].result(binding)
|
245
|
+
end
|
246
|
+
|
247
|
+
def rxml_render(template, binding)
|
248
|
+
eval(template, binding)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
require 'action_view/template_error'
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require File.dirname(__FILE__) + '/form_helper'
|
3
|
+
|
4
|
+
module ActionView
|
5
|
+
class Base
|
6
|
+
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }
|
7
|
+
cattr_accessor :field_error_proc
|
8
|
+
end
|
9
|
+
|
10
|
+
module Helpers
|
11
|
+
# The Active Record Helper makes it easier to create forms for records kept in instance variables. The most far-reaching is the form
|
12
|
+
# method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This
|
13
|
+
# is a great of making the record quickly available for editing, but likely to prove lacklusters for a complicated real-world form.
|
14
|
+
# In that case, it's better to use the input method and the specialized form methods in link:classes/ActionView/Helpers/FormHelper.html
|
15
|
+
module ActiveRecordHelper
|
16
|
+
# Returns a default input tag for the type of object returned by the method. Example
|
17
|
+
# (title is a VARCHAR column and holds "Hello World"):
|
18
|
+
# input("post", "title") =>
|
19
|
+
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
|
20
|
+
def input(record_name, method)
|
21
|
+
InstanceTag.new(record_name, method, self).to_tag
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns an entire form with input tags and everything for a specified Active Record object. Example
|
25
|
+
# (post is a new record that has a title using VARCHAR and a body using TEXT):
|
26
|
+
# form("post") =>
|
27
|
+
# <form action='create' method='POST'>
|
28
|
+
# <p>
|
29
|
+
# <b>Title</b><br />
|
30
|
+
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
|
31
|
+
# </p>
|
32
|
+
# <p>
|
33
|
+
# <b>Body</b><br />
|
34
|
+
# <textarea cols="40" id="post_body" name="post[body]" rows="20" wrap="virtual">
|
35
|
+
# Back to the hill and over it again!
|
36
|
+
# </textarea>
|
37
|
+
# </p>
|
38
|
+
# <input type='submit' value='Create' />
|
39
|
+
# </form>
|
40
|
+
#
|
41
|
+
# It's possible to specialize the form builder by using a different action name and by supplying another
|
42
|
+
# block renderer. Example (entry is a new record that has a message attribute using VARCHAR):
|
43
|
+
#
|
44
|
+
# form("entry", :action => "sign", :input_block =>
|
45
|
+
# Proc.new { |record, column| "#{column.human_name}: #{input(record, column.name)}<br />" }) =>
|
46
|
+
#
|
47
|
+
# <form action='sign' method='POST'>
|
48
|
+
# Message:
|
49
|
+
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /><br />
|
50
|
+
# <input type='submit' value='Sign' />
|
51
|
+
# </form>
|
52
|
+
def form(record_name, options = {})
|
53
|
+
record = instance_eval("@#{record_name}")
|
54
|
+
action = options[:action] || (record.new_record? ? "create" : "update")
|
55
|
+
id_field = record.new_record? ? "" : InstanceTag.new(record_name, "id", self).to_input_field_tag("hidden")
|
56
|
+
|
57
|
+
"<form action='#{action}' method='POST'>" +
|
58
|
+
id_field + all_input_tags(record, record_name, options) +
|
59
|
+
"<input type='submit' value='#{action.gsub(/[^A-Za-z]/, "").capitalize}' />" +
|
60
|
+
"</form>"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
|
64
|
+
# This error message is wrapped in a DIV tag, which can be specialized to include both a +prepend_text+ and +append_text+
|
65
|
+
# to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
|
66
|
+
# "can't be empty" on the title attribute):
|
67
|
+
#
|
68
|
+
# <%= error_message_on "post", "title" %> =>
|
69
|
+
# <div class="formError">can't be empty</div>
|
70
|
+
#
|
71
|
+
# <%= error_message_on "post", "title", "Title simply ", " (or it won't work)", "inputError" %> =>
|
72
|
+
# <div class="inputError">Title simply can't be empty (or it won't work)</div>
|
73
|
+
def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError")
|
74
|
+
if instance_eval("@#{object}").errors.on(method)
|
75
|
+
"<div class=\"#{css_class}\">#{prepend_text + instance_eval("@#{object}").errors.on(method) + append_text}</div>"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def error_messages_for(object_name)
|
80
|
+
object = instance_eval("@#{object_name}")
|
81
|
+
unless object.errors.empty?
|
82
|
+
"<div id=\"errorExplanation\">" +
|
83
|
+
"<h2>#{object.errors.count} error#{"s" unless object.errors.count == 1} prohibited this #{object_name.gsub("_", " ")} from being saved</h2>" +
|
84
|
+
"<p>There were problems with the following fields (marked in red below):</p>" +
|
85
|
+
"<ul>#{object.errors.full_messages.collect { |msg| "<li>#{msg}</li>"}}</ul>" +
|
86
|
+
"</div>"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def all_input_tags(record, record_name, options)
|
92
|
+
input_block = options[:input_block] || default_input_block
|
93
|
+
record.class.content_columns.collect{ |column| input_block.call(record_name, column) }.join("\n")
|
94
|
+
end
|
95
|
+
|
96
|
+
def default_input_block
|
97
|
+
Proc.new { |record, column| "<p><b>#{column.human_name}</b><br />#{input(record, column.name)}</p>" }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class InstanceTag #:nodoc:
|
102
|
+
def to_tag(options = {})
|
103
|
+
case column_type
|
104
|
+
when :string
|
105
|
+
field_type = @method_name.include?("password") ? "password" : "text"
|
106
|
+
to_input_field_tag(field_type, options)
|
107
|
+
when :text
|
108
|
+
to_text_area_tag(options)
|
109
|
+
when :integer, :float
|
110
|
+
to_input_field_tag("text", options)
|
111
|
+
when :date
|
112
|
+
to_date_select_tag(options)
|
113
|
+
when :datetime
|
114
|
+
to_datetime_select_tag(options)
|
115
|
+
when :boolean
|
116
|
+
to_boolean_select_tag(options)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
alias_method :tag_without_error_wrapping, :tag
|
121
|
+
|
122
|
+
def tag(name, options)
|
123
|
+
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
124
|
+
error_wrapping(tag_without_error_wrapping(name, options), object.errors.on(@method_name))
|
125
|
+
else
|
126
|
+
tag_without_error_wrapping(name, options)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
alias_method :content_tag_without_error_wrapping, :content_tag
|
131
|
+
|
132
|
+
def content_tag(name, value, options)
|
133
|
+
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
134
|
+
error_wrapping(content_tag_without_error_wrapping(name, value, options), object.errors.on(@method_name))
|
135
|
+
else
|
136
|
+
content_tag_without_error_wrapping(name, value, options)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag
|
141
|
+
def to_date_select_tag(options = {})
|
142
|
+
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
143
|
+
error_wrapping(to_date_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
|
144
|
+
else
|
145
|
+
to_date_select_tag_without_error_wrapping(options)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag
|
150
|
+
def to_datetime_select_tag(options = {})
|
151
|
+
if object.respond_to?("errors") && object.errors.respond_to?("on")
|
152
|
+
error_wrapping(to_datetime_select_tag_without_error_wrapping(options), object.errors.on(@method_name))
|
153
|
+
else
|
154
|
+
to_datetime_select_tag_without_error_wrapping(options)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def error_wrapping(html_tag, has_error)
|
159
|
+
has_error ? Base.field_error_proc.call(html_tag, self) : html_tag
|
160
|
+
end
|
161
|
+
|
162
|
+
def error_message
|
163
|
+
object.errors.on(@method_name)
|
164
|
+
end
|
165
|
+
|
166
|
+
def column_type
|
167
|
+
object.send("column_for_attribute", @method_name).type
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|