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,59 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module ActionView
|
4
|
+
module Helpers
|
5
|
+
# This is poor man's Builder for the rare cases where you need to programmatically make tags but can't use Builder.
|
6
|
+
module TagHelper
|
7
|
+
include ERB::Util
|
8
|
+
|
9
|
+
# Examples:
|
10
|
+
# * tag("br") => <br />
|
11
|
+
# * tag("input", { "type" => "text"}) => <input type="text" />
|
12
|
+
def tag(name, options = {}, open = false)
|
13
|
+
"<#{name + tag_options(options)}" + (open ? ">" : " />")
|
14
|
+
end
|
15
|
+
|
16
|
+
# Examples:
|
17
|
+
# * content_tag("p", "Hello world!") => <p>Hello world!</p>
|
18
|
+
# * content_tag("div", content_tag("p", "Hello world!"), "class" => "strong") =>
|
19
|
+
# <div class="strong"><p>Hello world!</p></div>
|
20
|
+
def content_tag(name, content, options = {})
|
21
|
+
"<#{name + tag_options(options)}>#{content}</#{name}>"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
|
25
|
+
# ActionController::Base#url_for.
|
26
|
+
def form_tag(url_for_options, options = {}, *parameters_for_url)
|
27
|
+
html_options = { "method" => "POST" }.merge(options)
|
28
|
+
|
29
|
+
if html_options[:multipart]
|
30
|
+
html_options["enctype"] = "multipart/form-data"
|
31
|
+
html_options.delete(:multipart)
|
32
|
+
end
|
33
|
+
|
34
|
+
html_options["action"] = url_for(url_for_options, *parameters_for_url)
|
35
|
+
|
36
|
+
tag("form", html_options, true)
|
37
|
+
end
|
38
|
+
|
39
|
+
alias_method :start_form_tag, :form_tag
|
40
|
+
|
41
|
+
# Outputs "</form>"
|
42
|
+
def end_form_tag
|
43
|
+
"</form>"
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
private
|
48
|
+
def tag_options(options)
|
49
|
+
if options.empty?
|
50
|
+
""
|
51
|
+
else
|
52
|
+
" " + options.collect { |pair|
|
53
|
+
"#{pair.first}=\"#{html_escape(pair.last)}\""
|
54
|
+
}.sort.join(" ")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module ActionView
|
2
|
+
# The template helpers serves to relieve the templates from including the same inline code again and again. It's a
|
3
|
+
# set of standardized methods for working with forms (FormHelper), dates (DateHelper), texts (TextHelper), and
|
4
|
+
# Active Records (ActiveRecordHelper) that's available to all templates by default.
|
5
|
+
#
|
6
|
+
# It's also really easy to make your own helpers and it's much encouraged to keep the template files free
|
7
|
+
# from complicated logic. It's even encouraged to bundle common compositions of methods from other helpers
|
8
|
+
# (often the common helpers) as they're used by the specific application.
|
9
|
+
#
|
10
|
+
# Defining a helper requires you to include a specialized +append_features+ method that makes them capable
|
11
|
+
# of configuring their integration upon inclusion in a controller, like this:
|
12
|
+
#
|
13
|
+
# module MyHelper
|
14
|
+
# def self.append_features(controller)
|
15
|
+
# controller.ancestors.include?(ActionController::Base) ?
|
16
|
+
# controller.add_template_helper(self) : super
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def hello_world() "hello world" end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# MyHelper can now be included in a controller, like this:
|
23
|
+
#
|
24
|
+
# require 'my_helper'
|
25
|
+
# class MyController < ActionController::Base
|
26
|
+
# include MyHelper
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# ...and, same as above, used in any template rendered from MyController, like this:
|
30
|
+
#
|
31
|
+
# Let's hear what the helper has to say: <tt><%= hello_world %></tt>
|
32
|
+
module Helpers
|
33
|
+
# Provides a set of methods for working with text strings that can help unburden the level of inline Ruby code in the
|
34
|
+
# templates. In the example below we iterate over a collection of posts provided to the template and prints each title
|
35
|
+
# after making sure it doesn't run longer than 20 characters:
|
36
|
+
# <% for post in @posts %>
|
37
|
+
# Title: <%= truncate(post.title, 20) %>
|
38
|
+
# <% end %>
|
39
|
+
module TextHelper
|
40
|
+
# The regular puts and print are outlawed in eRuby. It's recommended to use the <%= "hello" %> form instead of print "hello".
|
41
|
+
# If you absolutely must use a method-based output, you can use concat. It's use like this <% concat "hello", binding %>. Notice that
|
42
|
+
# it doesn't have an equal sign in front. Using <%= concat "hello" %> would result in a double hello.
|
43
|
+
def concat(string, binding)
|
44
|
+
eval("_erbout", binding).concat(string)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Truncates +text+ to the length of +length+ and replaces the last three characters with the +truncate_string+
|
48
|
+
# if the +text+ is longer than +length+.
|
49
|
+
def truncate(text, length = 30, truncate_string = "...")
|
50
|
+
if text.nil? then return end
|
51
|
+
if text.length > length then text[0..(length - 3)] + truncate_string else text end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Highlights the +phrase+ where it is found in the +text+ by surrounding it like
|
55
|
+
# <strong class="highlight">I'm a highlight phrase</strong>. The highlighter can be specialized by
|
56
|
+
# passing +highlighter+ as single-quoted string with \1 where the phrase is supposed to be inserted.
|
57
|
+
# N.B.: The +phrase+ is sanitized to include only letters, digits, and spaces before use.
|
58
|
+
def highlight(text, phrase, highlighter = '<strong class="highlight">\1</strong>')
|
59
|
+
if text.nil? || phrase.nil? then return end
|
60
|
+
text.gsub(/(#{escape_regexp(phrase)})/i, highlighter) unless text.nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Extracts an excerpt from the +text+ surrounding the +phrase+ with a number of characters on each side determined
|
64
|
+
# by +radius+. If the phrase isn't found, nil is returned. Ex:
|
65
|
+
# excerpt("hello my world", "my", 3) => "...lo my wo..."
|
66
|
+
def excerpt(text, phrase, radius = 100, excerpt_string = "...")
|
67
|
+
if text.nil? || phrase.nil? then return end
|
68
|
+
phrase = escape_regexp(phrase)
|
69
|
+
|
70
|
+
if found_pos = text =~ /(#{phrase})/i
|
71
|
+
start_pos = [ found_pos - radius, 0 ].max
|
72
|
+
end_pos = [ found_pos + phrase.length + radius, text.length ].min
|
73
|
+
|
74
|
+
prefix = start_pos > 0 ? excerpt_string : ""
|
75
|
+
postfix = end_pos < text.length ? excerpt_string : ""
|
76
|
+
|
77
|
+
prefix + text[start_pos..end_pos].strip + postfix
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Attempts to pluralize the +singular+ word unless +count+ is 1. See source for pluralization rules.
|
84
|
+
def pluralize(count, singular, plural = nil)
|
85
|
+
"#{count} " + if count == 1
|
86
|
+
singular
|
87
|
+
elsif plural
|
88
|
+
plural
|
89
|
+
elsif Object.const_defined?("Inflector")
|
90
|
+
Inflector.pluralize(singular)
|
91
|
+
else
|
92
|
+
singular + "s"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
begin
|
97
|
+
require "redcloth"
|
98
|
+
|
99
|
+
# Returns the text with all the Textile codes turned into HTML-tags.
|
100
|
+
# <i>This method is only available if RedCloth can be required</i>.
|
101
|
+
def textilize(text)
|
102
|
+
RedCloth.new(text).to_html
|
103
|
+
end
|
104
|
+
|
105
|
+
# Returns the text with all the Textile codes turned into HTML-tags, but without the regular bounding <p> tag.
|
106
|
+
# <i>This method is only available if RedCloth can be required</i>.
|
107
|
+
def textilize_without_paragraph(text)
|
108
|
+
textiled = textilize(text)
|
109
|
+
if textiled[0..2] == "<p>" then textiled = textiled[3..-1] end
|
110
|
+
if textiled[-4..-1] == "</p>" then textiled = textiled[0..-5] end
|
111
|
+
return textiled
|
112
|
+
end
|
113
|
+
rescue LoadError
|
114
|
+
# We can't really help what's not there
|
115
|
+
end
|
116
|
+
|
117
|
+
# Turns all links into words, like "<a href="something">else</a>" to "else".
|
118
|
+
def strip_links(text)
|
119
|
+
text.gsub(/<a.*>(.*)<\/a>/m, '\1')
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
# Returns a version of the text that's safe to use in a regular expression without triggering engine features.
|
124
|
+
def escape_regexp(text)
|
125
|
+
text.gsub(/([\\|?+*\/\)\(])/) { |m| "\\#{$1}" }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module ActionView
|
2
|
+
module Helpers
|
3
|
+
# Provides a set of methods for making easy links and getting urls that depend on the controller and action. This means that
|
4
|
+
# you can use the same format for links in the views that you do in the controller. The different methods are even named
|
5
|
+
# synchronously, so link_to uses that same url as is generated by url_for, which again is the same url used for
|
6
|
+
# redirection in redirect_to.
|
7
|
+
module UrlHelper
|
8
|
+
# Returns the URL for the set of +options+ provided. See the valid options in link:classes/ActionController/Base.html#M000021
|
9
|
+
def url_for(options = {}, *parameters_for_method_reference)
|
10
|
+
if Hash === options then options = { :only_path => true }.merge(options) end
|
11
|
+
@controller.send(:url_for, options, *parameters_for_method_reference).gsub("&", "&")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a link tag of the given +name+ using an URL created by the set of +options+. See the valid options in
|
15
|
+
# link:classes/ActionController/Base.html#M000021. It's also possible to pass a string instead of an options hash to
|
16
|
+
# get a link tag that just points without consideration. The html_options have a special feature for creating javascript
|
17
|
+
# confirm alerts where if you pass :confirm => 'Are you sure?', the link will be guarded with a JS popup asking that question.
|
18
|
+
# If the user accepts, the link is processed, otherwise not.
|
19
|
+
def link_to(name, options = {}, html_options = {}, *parameters_for_method_reference)
|
20
|
+
if options.is_a?(String)
|
21
|
+
content_tag "a", name, "href" => options
|
22
|
+
else
|
23
|
+
convert_confirm_option_to_javascript!(html_options)
|
24
|
+
content_tag("a", name, html_options.merge({ "href" => url_for(options, *parameters_for_method_reference) }))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a link tag of the given +name+ using an URL created by the set of +options+, unless the current
|
29
|
+
# controller, action, and id are the same as the link's, in which case only the name is returned (or the
|
30
|
+
# given block is yielded, if one exists). This is useful for creating link bars where you don't want to link
|
31
|
+
# to the page currently being viewed.
|
32
|
+
def link_to_unless_current(name, options = {}, html_options = {}, *parameters_for_method_reference)
|
33
|
+
assume_current_url_options!(options)
|
34
|
+
|
35
|
+
if destination_equal_to_current(options)
|
36
|
+
block_given? ?
|
37
|
+
yield(name, options, html_options, *parameters_for_method_reference) :
|
38
|
+
html_escape(name)
|
39
|
+
else
|
40
|
+
link_to name, options, html_options, *parameters_for_method_reference
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def destination_equal_to_current(options)
|
46
|
+
params_without_location = @params.reject { |key, value| %w( controller action id ).include?(key) }
|
47
|
+
|
48
|
+
options[:action] == @params['action'] &&
|
49
|
+
options[:id] == @params['id'] &&
|
50
|
+
options[:controller] == @params['controller'] &&
|
51
|
+
(options.has_key?(:params) ? params_without_location == options[:params] : true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def assume_current_url_options!(options)
|
55
|
+
if options[:controller].nil?
|
56
|
+
options[:controller] = @params['controller']
|
57
|
+
if options[:action].nil?
|
58
|
+
options[:action] = @params['action']
|
59
|
+
if options[:id].nil? then options[:id] ||= @params['id'] end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def convert_confirm_option_to_javascript!(html_options)
|
65
|
+
if html_options.include?(:confirm)
|
66
|
+
html_options["onClick"] = "return confirm('#{html_options[:confirm]}');"
|
67
|
+
html_options.delete(:confirm)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActionView
|
2
|
+
# There's also a convenience method for rendering sub templates within the current controller that depends on a single object
|
3
|
+
# (we call this kind of sub templates for partials). It relies on the fact that partials should follow the naming convention of being
|
4
|
+
# prefixed with an underscore -- as to separate them from regular templates that could be rendered on their own. In the template for
|
5
|
+
# Advertiser#buy, we could have:
|
6
|
+
#
|
7
|
+
# <% for ad in @advertisements %>
|
8
|
+
# <%= render_partial "ad", ad %>
|
9
|
+
# <% end %>
|
10
|
+
#
|
11
|
+
# This would render "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display.
|
12
|
+
#
|
13
|
+
# == Rendering a collection of partials
|
14
|
+
#
|
15
|
+
# The example of partial use describes a familar pattern where a template needs to iterate over an array and render a sub
|
16
|
+
# template for each of the elements. This pattern has been implemented as a single method that accepts an array and renders
|
17
|
+
# a partial by the same name as the elements contained within. So the three-lined example in "Using partials" can be rewritten
|
18
|
+
# with a single line:
|
19
|
+
#
|
20
|
+
# <%= render_collection_of_partials "ad", @advertisements %>
|
21
|
+
#
|
22
|
+
# This will render "advertiser/_ad.rhtml" and pass the local variable +ad+ to the template for display. An iteration counter
|
23
|
+
# will automatically be made available to the template with a name of the form +partial_name_counter+. In the case of the
|
24
|
+
# example above, the template would be fed +ad_counter+.
|
25
|
+
#
|
26
|
+
# == Rendering shared partials
|
27
|
+
#
|
28
|
+
# Two controllers can share a set of partials and render them like this:
|
29
|
+
#
|
30
|
+
# <%= render_partial "advertisement/ad", ad %>
|
31
|
+
#
|
32
|
+
# This will render the partial "advertisement/_ad.rhtml" regardless of which controller this is being called from.
|
33
|
+
module Partials
|
34
|
+
def render_partial(partial_path, object = nil, local_assigns = {})
|
35
|
+
path, partial_name = partial_pieces(partial_path)
|
36
|
+
object ||= controller.instance_variable_get("@#{partial_name}")
|
37
|
+
render("#{path}/_#{partial_name}", { partial_name => object }.merge(local_assigns))
|
38
|
+
end
|
39
|
+
|
40
|
+
def render_collection_of_partials(partial_name, collection, partial_spacer_template = nil)
|
41
|
+
collection_of_partials = Array.new
|
42
|
+
collection.each_with_index do |element, counter|
|
43
|
+
collection_of_partials.push(render_partial(partial_name, element, "#{partial_name.split("/").last}_counter" => counter))
|
44
|
+
end
|
45
|
+
|
46
|
+
return nil if collection_of_partials.empty?
|
47
|
+
partial_spacer_template ?
|
48
|
+
collection_of_partials.join(render("#{controller.send(:controller_name)}/_#{partial_spacer_template}")) :
|
49
|
+
collection_of_partials
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def partial_pieces(partial_path)
|
54
|
+
if partial_path.include?('/')
|
55
|
+
return File.dirname(partial_path), File.basename(partial_path)
|
56
|
+
else
|
57
|
+
return controller.send(:controller_name), partial_path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module ActionView
|
2
|
+
# The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
|
3
|
+
# bunch of intimate details and uses it to report a very precise exception message.
|
4
|
+
class TemplateError < ActionViewError #:nodoc:
|
5
|
+
SOURCE_CODE_RADIUS = 3
|
6
|
+
|
7
|
+
attr_reader :original_exception
|
8
|
+
|
9
|
+
def initialize(base_path, file_name, assigns, source, original_exception)
|
10
|
+
@base_path, @file_name, @assigns, @source, @original_exception =
|
11
|
+
base_path, file_name, assigns, source, original_exception
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
if original_exception.message.include?("(eval):")
|
16
|
+
original_exception.message.scan(/\(eval\):(?:[0-9]*):in `.*'(.*)/).first.first
|
17
|
+
else
|
18
|
+
original_exception.message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def sub_template_message
|
23
|
+
if @sub_templates
|
24
|
+
"Trace of template inclusion: " +
|
25
|
+
@sub_templates.collect { |template| strip_base_path(template) }.join(", ")
|
26
|
+
else
|
27
|
+
""
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def source_extract
|
32
|
+
source_code = IO.readlines(@file_name)
|
33
|
+
|
34
|
+
start_on_line = [ line_number - SOURCE_CODE_RADIUS - 1, 0 ].max
|
35
|
+
end_on_line = [ line_number + SOURCE_CODE_RADIUS - 1, source_code.length].min
|
36
|
+
|
37
|
+
line_counter = start_on_line
|
38
|
+
extract = source_code[start_on_line..end_on_line].collect do |line|
|
39
|
+
line_counter += 1
|
40
|
+
"#{line_counter}: " + line
|
41
|
+
end
|
42
|
+
|
43
|
+
extract.join
|
44
|
+
end
|
45
|
+
|
46
|
+
def sub_template_of(file_name)
|
47
|
+
@sub_templates ||= []
|
48
|
+
@sub_templates << file_name
|
49
|
+
end
|
50
|
+
|
51
|
+
def line_number
|
52
|
+
begin
|
53
|
+
@original_exception.backtrace.join.scan(/\((?:erb)\):([0-9]*)/).first.first.to_i
|
54
|
+
rescue
|
55
|
+
begin
|
56
|
+
original_exception.message.scan(/\((?:eval)\):([0-9]*)/).first.first.to_i
|
57
|
+
rescue
|
58
|
+
1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def file_name
|
64
|
+
strip_base_path(@file_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
"\n\n#{self.class} (#{message}) on line ##{line_number} of #{file_name}:\n" +
|
69
|
+
source_extract + "\n " +
|
70
|
+
clean_backtrace(original_exception).join("\n ") +
|
71
|
+
"\n\n"
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
def strip_base_path(file_name)
|
76
|
+
file_name.gsub(@base_path, "")
|
77
|
+
end
|
78
|
+
|
79
|
+
def clean_backtrace(exception)
|
80
|
+
base_dir = File.expand_path(File.dirname(__FILE__) + "/../../../../")
|
81
|
+
exception.backtrace.collect { |line| line.gsub(base_dir, "").gsub("/public/../config/environments/../../", "").gsub("/public/../", "") }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#--
|
4
|
+
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
5
|
+
# All rights reserved.
|
6
|
+
|
7
|
+
# Permission is granted for use, copying, modification, distribution,
|
8
|
+
# and distribution of modified versions of this work as long as the
|
9
|
+
# above copyright notice is included.
|
10
|
+
#++
|
11
|
+
|
12
|
+
require 'builder/xmlmarkup'
|
13
|
+
require 'builder/xmlevents'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
|
4
|
+
# All rights reserved.
|
5
|
+
|
6
|
+
# Permission is granted for use, copying, modification, distribution,
|
7
|
+
# and distribution of modified versions of this work as long as the
|
8
|
+
# above copyright notice is included.
|
9
|
+
#++
|
10
|
+
|
11
|
+
module Builder #:nodoc:
|
12
|
+
|
13
|
+
# BlankSlate provides an abstract base class with no predefined
|
14
|
+
# methods (except for <tt>\_\_send__</tt> and <tt>\_\_id__</tt>).
|
15
|
+
# BlankSlate is useful as a base class when writing classes that
|
16
|
+
# depend upon <tt>method_missing</tt> (e.g. dynamic proxies).
|
17
|
+
class BlankSlate #:nodoc:
|
18
|
+
instance_methods.each { |m| undef_method m unless m =~ /^(__|instance_eval)/ }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'builder/blankslate'
|
4
|
+
|
5
|
+
module Builder #:nodoc:
|
6
|
+
|
7
|
+
# Generic error for builder
|
8
|
+
class IllegalBlockError < RuntimeError #:nodoc:
|
9
|
+
end
|
10
|
+
|
11
|
+
# XmlBase is a base class for building XML builders. See
|
12
|
+
# Builder::XmlMarkup and Builder::XmlEvents for examples.
|
13
|
+
class XmlBase < BlankSlate #:nodoc:
|
14
|
+
|
15
|
+
# Create an XML markup builder.
|
16
|
+
#
|
17
|
+
# out:: Object receiving the markup.1 +out+ must respond to
|
18
|
+
# <tt><<</tt>.
|
19
|
+
# indent:: Number of spaces used for indentation (0 implies no
|
20
|
+
# indentation and no line breaks).
|
21
|
+
# initial:: Level of initial indentation.
|
22
|
+
#
|
23
|
+
def initialize(indent=0, initial=0)
|
24
|
+
@indent = indent
|
25
|
+
@level = initial
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create a tag named +sym+. Other than the first argument which
|
29
|
+
# is the tag name, the arguements are the same as the tags
|
30
|
+
# implemented via <tt>method_missing</tt>.
|
31
|
+
def tag!(sym, *args, &block)
|
32
|
+
self.__send__(sym, *args, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create XML markup based on the name of the method. This method
|
36
|
+
# is never invoked directly, but is called for each markup method
|
37
|
+
# in the markup block.
|
38
|
+
def method_missing(sym, *args, &block)
|
39
|
+
text = nil
|
40
|
+
attrs = nil
|
41
|
+
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(Symbol)
|
42
|
+
args.each do |arg|
|
43
|
+
case arg
|
44
|
+
when Hash
|
45
|
+
attrs ||= {}
|
46
|
+
attrs.merge!(arg)
|
47
|
+
else
|
48
|
+
text ||= ''
|
49
|
+
text << arg.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
if block
|
53
|
+
unless text.nil?
|
54
|
+
raise ArgumentError, "XmlMarkup cannot mix a text argument with a block"
|
55
|
+
end
|
56
|
+
_capture_outer_self(block) if @self.nil?
|
57
|
+
_indent
|
58
|
+
_start_tag(sym, attrs)
|
59
|
+
_newline
|
60
|
+
_nested_structures(block)
|
61
|
+
_indent
|
62
|
+
_end_tag(sym)
|
63
|
+
_newline
|
64
|
+
elsif text.nil?
|
65
|
+
_indent
|
66
|
+
_start_tag(sym, attrs, true)
|
67
|
+
_newline
|
68
|
+
else
|
69
|
+
_indent
|
70
|
+
_start_tag(sym, attrs)
|
71
|
+
text! text
|
72
|
+
_end_tag(sym)
|
73
|
+
_newline
|
74
|
+
end
|
75
|
+
@target
|
76
|
+
end
|
77
|
+
|
78
|
+
# Append text to the output target. Escape any markup. May be
|
79
|
+
# used within the markup brakets as:
|
80
|
+
#
|
81
|
+
# builder.p { br; text! "HI" } #=> <p><br/>HI</p>
|
82
|
+
def text!(text)
|
83
|
+
_text(_escape(text))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Append text to the output target without escaping any markup.
|
87
|
+
# May be used within the markup brakets as:
|
88
|
+
#
|
89
|
+
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
|
90
|
+
#
|
91
|
+
# This is useful when using non-builder enabled software that
|
92
|
+
# generates strings. Just insert the string directly into the
|
93
|
+
# builder without changing the inserted markup.
|
94
|
+
#
|
95
|
+
# It is also useful for stacking builder objects. Builders only
|
96
|
+
# use <tt><<</tt> to append to the target, so by supporting this
|
97
|
+
# method/operation builders can use oother builders as their
|
98
|
+
# targets.
|
99
|
+
def <<(text)
|
100
|
+
_text(text)
|
101
|
+
end
|
102
|
+
|
103
|
+
# For some reason, nil? is sent to the XmlMarkup object. If nil?
|
104
|
+
# is not defined and method_missing is invoked, some strange kind
|
105
|
+
# of recursion happens. Since nil? won't ever be an XML tag, it
|
106
|
+
# is pretty safe to define it here. (Note: this is an example of
|
107
|
+
# cargo cult programming,
|
108
|
+
# cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
|
109
|
+
def nil?
|
110
|
+
false
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def _escape(text)
|
116
|
+
text.
|
117
|
+
gsub(%r{&}, '&').
|
118
|
+
gsub(%r{<}, '<').
|
119
|
+
gsub(%r{>}, '>')
|
120
|
+
end
|
121
|
+
|
122
|
+
def _capture_outer_self(block)
|
123
|
+
@self = eval("self", block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def _newline
|
127
|
+
return if @indent == 0
|
128
|
+
text! "\n"
|
129
|
+
end
|
130
|
+
|
131
|
+
def _indent
|
132
|
+
return if @indent == 0 || @level == 0
|
133
|
+
text!(" " * (@level * @indent))
|
134
|
+
end
|
135
|
+
|
136
|
+
def _nested_structures(block)
|
137
|
+
@level += 1
|
138
|
+
block.call(self)
|
139
|
+
ensure
|
140
|
+
@level -= 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|