erector 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +14 -0
- data/VERSION.yml +1 -1
- data/bin/erector +9 -4
- data/lib/erector.rb +11 -17
- data/lib/erector/erected.rb +1 -1
- data/lib/erector/externals.rb +19 -16
- data/lib/erector/inline.rb +12 -11
- data/lib/erector/mixin.rb +5 -2
- data/lib/erector/rails.rb +26 -9
- data/lib/erector/rails/extensions/action_controller.rb +2 -14
- data/lib/erector/rails/extensions/rails_widget.rb +65 -25
- data/lib/erector/rails/extensions/rails_widget/rails_helpers.rb +4 -3
- data/lib/erector/rails/template_handlers/ert_handler.rb +1 -1
- data/lib/erector/rails/template_handlers/rb_handler.rb +8 -43
- data/lib/erector/widget.rb +147 -70
- data/lib/erector/widgets.rb +5 -4
- data/lib/erector/widgets/form.rb +30 -0
- data/lib/erector/widgets/page.rb +74 -49
- data/rails/init.rb +4 -0
- data/spec/erect/erect_rails_spec.rb +63 -26
- data/spec/erect/erected_spec.rb +4 -4
- data/spec/erector/external_spec.rb +64 -6
- data/spec/erector/indentation_spec.rb +2 -2
- data/spec/erector/inline_spec.rb +74 -42
- data/spec/erector/mixin_spec.rb +16 -5
- data/spec/erector/widget_spec.rb +184 -7
- data/spec/erector/widgets/form_spec.rb +31 -0
- data/spec/erector/widgets/page_spec.rb +30 -20
- data/spec/spec_suite.rb +4 -3
- metadata +5 -3
- data/lib/erector/rails/extensions/action_view.rb +0 -21
data/README.txt
CHANGED
@@ -62,6 +62,20 @@ To install as a Rails plugin:
|
|
62
62
|
When installing this way, erector is automatically available to your Rails code
|
63
63
|
(no require directive is needed).
|
64
64
|
|
65
|
+
== CREDITS:
|
66
|
+
|
67
|
+
Core Team:
|
68
|
+
* Alex Chaffee
|
69
|
+
* Brian Takita
|
70
|
+
|
71
|
+
Special Thanks To:
|
72
|
+
* Abby (Chaffee's muse & Best friend)
|
73
|
+
* Jim Kingdon
|
74
|
+
* Jeff Dean
|
75
|
+
* John Firebaugh
|
76
|
+
* Nathan Sobo
|
77
|
+
* Nick Kallen
|
78
|
+
|
65
79
|
== LICENSE:
|
66
80
|
|
67
81
|
(The MIT License)
|
data/VERSION.yml
CHANGED
data/bin/erector
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
|
4
|
-
require
|
5
|
-
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'erector'
|
5
|
+
rescue LoadError
|
6
|
+
require 'rubygems'
|
7
|
+
require 'erector'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'erector/erect'
|
6
11
|
|
7
12
|
unless Erector::Erect.new(ARGV).run
|
8
13
|
exit 1
|
data/lib/erector.rb
CHANGED
@@ -1,19 +1,13 @@
|
|
1
|
-
require "
|
2
|
-
|
3
|
-
require 'cgi'
|
4
|
-
require 'yaml'
|
1
|
+
require "cgi"
|
2
|
+
require "yaml"
|
5
3
|
require "active_support/inflector"
|
6
4
|
require "active_support/inflections"
|
7
|
-
require "
|
8
|
-
require "
|
9
|
-
require "
|
10
|
-
require "
|
11
|
-
require "
|
12
|
-
require "
|
13
|
-
require "
|
14
|
-
require "
|
15
|
-
require "
|
16
|
-
if Object.const_defined?(:RAILS_ROOT)
|
17
|
-
require "#{dir}/erector/rails"
|
18
|
-
end
|
19
|
-
|
5
|
+
require "erector/extensions/object"
|
6
|
+
require "erector/raw_string"
|
7
|
+
require "erector/externals"
|
8
|
+
require "erector/widget"
|
9
|
+
require "erector/inline"
|
10
|
+
require "erector/unicode"
|
11
|
+
require "erector/widgets"
|
12
|
+
require "erector/version"
|
13
|
+
require "erector/mixin"
|
data/lib/erector/erected.rb
CHANGED
@@ -20,7 +20,7 @@ module Erector
|
|
20
20
|
parent = File.dirname(@in_file)
|
21
21
|
grandparent = File.dirname(parent)
|
22
22
|
if File.basename(grandparent) == "views"
|
23
|
-
["Views::" + classize(File.basename(parent)) + "::" + base, "Erector::
|
23
|
+
["Views::" + classize(File.basename(parent)) + "::" + base, "Erector::Widget"]
|
24
24
|
else
|
25
25
|
[base, "Erector::Widget"]
|
26
26
|
end
|
data/lib/erector/externals.rb
CHANGED
@@ -1,28 +1,31 @@
|
|
1
1
|
module Erector
|
2
|
+
class External < Struct.new(:type, :klass, :text, :options)
|
3
|
+
def initialize(type, klass, text, options = {})
|
4
|
+
super(type.to_sym, klass, text, options)
|
5
|
+
end
|
6
|
+
|
7
|
+
def ==(other)
|
8
|
+
(self.type == other.type and
|
9
|
+
self.text == other.text and
|
10
|
+
self.options == other.options) ? true : false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
2
14
|
module Externals
|
3
15
|
def externals(type, klass = nil)
|
4
16
|
type = type.to_sym
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
x.select{|value| @@externals[klass].include?(value)}
|
9
|
-
else
|
10
|
-
x
|
17
|
+
(@@externals ||= []).select do |x|
|
18
|
+
x.type == type &&
|
19
|
+
(klass.nil? || x.klass == klass)
|
11
20
|
end
|
12
21
|
end
|
13
22
|
|
14
|
-
def
|
15
|
-
@@externals ||= {}
|
16
|
-
@@externals[type] ||= []
|
17
|
-
@@externals[klass] ||= [] if klass
|
18
|
-
end
|
19
|
-
|
20
|
-
def external(type, value)
|
23
|
+
def external(type, value, options = {})
|
21
24
|
type = type.to_sym
|
22
25
|
klass = self # since it's a class method, self should be the class itself
|
23
|
-
|
24
|
-
@@externals
|
25
|
-
@@externals
|
26
|
+
x = External.new(type, klass, value, options)
|
27
|
+
@@externals ||= []
|
28
|
+
@@externals << x unless @@externals.include?(x)
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
data/lib/erector/inline.rb
CHANGED
@@ -4,17 +4,18 @@ module Erector
|
|
4
4
|
end
|
5
5
|
|
6
6
|
module Inline
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
# Executes the widget's block (the one that was passed in the
|
8
|
+
# constructor). Since "self" is pointing to the new widget, the block does
|
9
|
+
# not naturally have access to parent method methods, so an
|
10
|
+
# Erector::Inline widget uses some method_missing black magic to propagate
|
11
|
+
# messages to the parent object. Since it executes inside the *called*
|
12
|
+
# widget's context, when the block refers to instance variables, it's
|
13
|
+
# talking about those of this widget, not the caller. It does, of course,
|
14
|
+
# have access to bound local variables of the caller, so you can use those
|
15
|
+
# to smuggle in instance variables.
|
16
|
+
def call_block
|
17
|
+
# note that instance_eval seems to pass in self as a parameter to the block
|
18
|
+
instance_eval(&@block) if @block
|
18
19
|
end
|
19
20
|
|
20
21
|
private
|
data/lib/erector/mixin.rb
CHANGED
@@ -3,9 +3,12 @@ module Erector
|
|
3
3
|
# Executes the block as if it were the content body of a fresh Erector::Inline,
|
4
4
|
# and returns the #to_s value. Since it executes inside the new widget it does not
|
5
5
|
# have access to instance variables of the caller, although it does
|
6
|
-
# have access to bound variables.
|
6
|
+
# have access to bound variables. Funnily enough, the options are passed in to both
|
7
|
+
# to_s *and* to the widget itself, so they show up as instance variables.
|
7
8
|
def erector(options = {}, &block)
|
8
|
-
|
9
|
+
vars = options.dup
|
10
|
+
vars.delete_if {|key, value| Erector::Widget::RESERVED_INSTANCE_VARS.include?(key)}
|
11
|
+
Erector.inline(vars, &block).to_s(options)
|
9
12
|
end
|
10
13
|
end
|
11
14
|
end
|
data/lib/erector/rails.rb
CHANGED
@@ -1,10 +1,27 @@
|
|
1
|
-
dir = File.dirname(__FILE__)
|
2
1
|
require "action_controller"
|
3
|
-
require "
|
4
|
-
require "
|
5
|
-
require "
|
6
|
-
require "
|
7
|
-
require "
|
8
|
-
require "
|
9
|
-
require "
|
10
|
-
|
2
|
+
require "erector/rails/rails_version"
|
3
|
+
require "erector/rails/rails_form_builder"
|
4
|
+
require "erector/rails/extensions/rails_widget"
|
5
|
+
require "erector/rails/extensions/action_controller"
|
6
|
+
require "erector/rails/extensions/rails_widget/rails_helpers"
|
7
|
+
require "erector/rails/template_handlers/rb_handler"
|
8
|
+
require "erector/rails/template_handlers/ert_handler"
|
9
|
+
|
10
|
+
module Erector
|
11
|
+
def self.init_rails(binding)
|
12
|
+
# Rails defaults do not include app/views in the eager load path.
|
13
|
+
# It needs to be there, because erector views are .rb files.
|
14
|
+
if config = eval("config if defined? config", binding)
|
15
|
+
view_path = config.view_path
|
16
|
+
config.load_paths << view_path unless config.load_paths.include?(view_path)
|
17
|
+
config.eager_load_paths << view_path unless config.eager_load_paths.include?(view_path)
|
18
|
+
|
19
|
+
# Rails probably already ran Initializer#set_load_path and
|
20
|
+
# #set_autoload_paths by the time we got here.
|
21
|
+
$LOAD_PATH.unshift(view_path) unless $LOAD_PATH.include?(view_path)
|
22
|
+
unless ActiveSupport::Dependencies.load_paths.include?(view_path)
|
23
|
+
ActiveSupport::Dependencies.load_paths << view_path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -1,23 +1,11 @@
|
|
1
1
|
ActionController::Base.class_eval do
|
2
2
|
def render_widget(widget_class, assigns=nil)
|
3
|
-
|
4
|
-
if assigns
|
5
|
-
@__widget_assigns = assigns
|
6
|
-
else
|
7
|
-
@__widget_assigns = {}
|
8
|
-
variables = instance_variable_names
|
9
|
-
variables -= protected_instance_variables
|
10
|
-
variables.each do |name|
|
11
|
-
@__widget_assigns[name.sub('@', "")] = instance_variable_get(name)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
response.template.send(:_evaluate_assigns_and_ivars)
|
15
|
-
render :inline => "<% @__widget_class.new(@__widget_assigns.merge(:parent => self)).to_s %>"
|
3
|
+
render :text => Erector::Rails.render(widget_class, self, assigns)
|
16
4
|
end
|
17
5
|
|
18
6
|
def render_with_erector_widget(*options, &block)
|
19
7
|
if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
|
20
|
-
render_widget widget, @assigns
|
8
|
+
render_widget widget, @assigns
|
21
9
|
else
|
22
10
|
render_without_erector_widget *options, &block
|
23
11
|
end
|
@@ -1,44 +1,84 @@
|
|
1
1
|
module Erector
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
module Rails
|
3
|
+
# These controller instance variables should not be passed to the
|
4
|
+
# widget if it does not +need+ them.
|
5
|
+
NON_NEEDED_CONTROLLER_INSTANCE_VARIABLES = [:@template, :@_request]
|
6
|
+
|
7
|
+
def self.render(widget, controller, assigns = nil)
|
8
|
+
if widget.is_a?(Class)
|
9
|
+
unless assigns
|
10
|
+
needs = widget.get_needed_variables
|
11
|
+
assigns = {}
|
12
|
+
variables = controller.instance_variable_names
|
13
|
+
variables -= controller.protected_instance_variables
|
14
|
+
variables.each do |name|
|
15
|
+
assign = name.sub('@', '').to_sym
|
16
|
+
next if !needs.empty? && !needs.include?(assign) && NON_NEEDED_CONTROLLER_INSTANCE_VARIABLES.include?(name.to_sym)
|
17
|
+
assigns[assign] = controller.instance_variable_get(name)
|
18
|
+
end
|
19
|
+
end
|
6
20
|
|
7
|
-
|
8
|
-
|
9
|
-
|
21
|
+
widget = widget.new(assigns)
|
22
|
+
end
|
23
|
+
|
24
|
+
view = controller.response.template
|
25
|
+
view.send(:_evaluate_assigns_and_ivars)
|
10
26
|
|
11
|
-
|
12
|
-
|
27
|
+
view.with_output_buffer do
|
28
|
+
widget.to_s(
|
29
|
+
:output => view.output_buffer,
|
30
|
+
:parent => view,
|
31
|
+
:helpers => view
|
32
|
+
)
|
33
|
+
end
|
13
34
|
end
|
14
35
|
|
15
|
-
|
36
|
+
module WidgetExtensions
|
37
|
+
def self.included(base)
|
38
|
+
base.alias_method_chain :output, :parent
|
39
|
+
base.alias_method_chain :capture, :parent
|
40
|
+
end
|
16
41
|
|
17
|
-
|
18
|
-
|
42
|
+
def output_with_parent
|
43
|
+
if parent.respond_to?(:output_buffer)
|
44
|
+
parent.output_buffer.is_a?(String) ? parent.output_buffer : handle_rjs_buffer
|
45
|
+
else
|
46
|
+
output_without_parent
|
47
|
+
end
|
48
|
+
end
|
19
49
|
|
20
|
-
|
50
|
+
def capture_with_parent(&block)
|
51
|
+
parent ? raw(parent.capture(&block).to_s) : capture_without_parent(&block)
|
52
|
+
end
|
21
53
|
|
22
|
-
|
23
|
-
|
24
|
-
parent.output_buffer.is_a?(String) ? parent.output_buffer : handle_rjs_buffer
|
25
|
-
else
|
26
|
-
nil
|
54
|
+
# This is here to force #parent.capture to return the output
|
55
|
+
def __in_erb_template;
|
27
56
|
end
|
28
|
-
end
|
29
57
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
parent.
|
34
|
-
|
58
|
+
private
|
59
|
+
|
60
|
+
def handle_rjs_buffer
|
61
|
+
returning buffer = parent.output_buffer.dup.to_s do
|
62
|
+
parent.output_buffer.clear
|
63
|
+
parent.with_output_buffer(buffer) do
|
64
|
+
buffer << parent.output_buffer.to_s
|
65
|
+
end
|
35
66
|
end
|
36
67
|
end
|
37
68
|
end
|
69
|
+
|
70
|
+
Erector::Widget.send :include, WidgetExtensions
|
71
|
+
end
|
72
|
+
|
73
|
+
# RailsWidget and InlineRailsWidget are for backward compatibility.
|
74
|
+
# New code should use Widget, InlineWidget, or Erector.inline.
|
75
|
+
class RailsWidget < Widget
|
76
|
+
def self.inline(*args, &block)
|
77
|
+
InlineRailsWidget.new(*args, &block)
|
78
|
+
end
|
38
79
|
end
|
39
80
|
|
40
81
|
class InlineRailsWidget < RailsWidget
|
41
82
|
include Inline
|
42
83
|
end
|
43
|
-
|
44
84
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Erector
|
2
|
-
|
3
|
-
module
|
2
|
+
module Rails
|
3
|
+
module Helpers
|
4
4
|
include ActionController::UrlWriter
|
5
5
|
|
6
6
|
# parent returning raw text whose first parameter is HTML escaped
|
@@ -131,6 +131,7 @@ module Erector
|
|
131
131
|
parent.pluralize(*args)
|
132
132
|
end
|
133
133
|
end
|
134
|
-
|
134
|
+
|
135
|
+
Erector::Widget.send :include, Helpers
|
135
136
|
end
|
136
137
|
end
|
@@ -18,7 +18,7 @@ module ActionView #:nodoc:
|
|
18
18
|
" all[instance_variable] = instance_variable_get(instance_variable)",
|
19
19
|
" all",
|
20
20
|
"end",
|
21
|
-
"r =::Erector
|
21
|
+
"r =::Erector.inline do",
|
22
22
|
" memoized_instance_variables.each do |instance_variable, value|",
|
23
23
|
" instance_variable_set(instance_variable, value)",
|
24
24
|
" end",
|
@@ -1,46 +1,11 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
ActionView::Template.instance_eval do
|
10
|
-
register_template_handler :rb, ActionView::TemplateHandlers::RbHandler
|
11
|
-
end
|
12
|
-
|
13
|
-
def compile(template)
|
14
|
-
relative_path_parts = template.path.split('/')
|
15
|
-
|
16
|
-
is_partial = relative_path_parts.last =~ /^_/
|
17
|
-
require_dependency File.expand_path(template.filename)
|
18
|
-
|
19
|
-
widget_class_parts = relative_path_parts.inject(['Views']) do |class_parts, node|
|
20
|
-
class_parts << node.gsub(/^_/, "").gsub(/(\.html)?\.rb$/, '').camelize
|
21
|
-
class_parts
|
22
|
-
end
|
23
|
-
widget_class_name = widget_class_parts.join("::")
|
24
|
-
render_method = is_partial ? 'render_partial' : 'content'
|
25
|
-
|
26
|
-
erb_template = <<-ERB
|
27
|
-
<%
|
28
|
-
assigns = instance_variables.inject({}) do |hash, name|
|
29
|
-
hash[name.sub('@', "")] = instance_variable_get(name)
|
30
|
-
hash
|
31
|
-
end
|
32
|
-
|
33
|
-
widget = #{widget_class_name}.new(assigns)
|
34
|
-
widget.to_s(:output => output_buffer, :helpers => self, :content_method_name => :#{render_method})
|
35
|
-
%>
|
36
|
-
ERB
|
37
|
-
::ERB.new(
|
38
|
-
erb_template,
|
39
|
-
nil,
|
40
|
-
::ActionView::TemplateHandlers::ERB.erb_trim_mode,
|
41
|
-
"@output_buffer"
|
42
|
-
).src
|
43
|
-
end
|
1
|
+
module Erector
|
2
|
+
class RbHandler < ActionView::TemplateHandler
|
3
|
+
def render(template, local_assigns)
|
4
|
+
require_dependency File.expand_path(template.filename)
|
5
|
+
widget_class = "views/#{template.path_without_format_and_extension}".camelize.constantize
|
6
|
+
Erector::Rails.render(widget_class, @view.controller)
|
44
7
|
end
|
45
8
|
end
|
46
9
|
end
|
10
|
+
|
11
|
+
ActionView::Template.register_template_handler :rb, Erector::RbHandler
|
data/lib/erector/widget.rb
CHANGED
@@ -49,24 +49,47 @@ module Erector
|
|
49
49
|
# Tags which can contain other stuff. Click "[Source]" to see the full list.
|
50
50
|
def full_tags
|
51
51
|
[
|
52
|
-
'a', 'abbr', 'acronym', 'address',
|
52
|
+
'a', 'abbr', 'acronym', 'address', 'article', 'aside', 'audio',
|
53
53
|
'b', 'bdo', 'big', 'blockquote', 'body', 'button',
|
54
|
-
'caption', 'center', 'cite', 'code', 'colgroup',
|
55
|
-
'dd', 'del', 'dfn', '
|
56
|
-
'embed',
|
57
|
-
'fieldset', 'form', 'frameset',
|
58
|
-
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'i',
|
59
|
-
'iframe', 'ins', 'kbd', 'label', 'legend', 'li',
|
60
|
-
'
|
61
|
-
'
|
62
|
-
'
|
63
|
-
'
|
54
|
+
'canvas', 'caption', 'center', 'cite', 'code', 'colgroup', 'command',
|
55
|
+
'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt',
|
56
|
+
'em', 'embed',
|
57
|
+
'fieldset', 'figure', 'footer', 'form', 'frameset',
|
58
|
+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'html', 'i',
|
59
|
+
'iframe', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li',
|
60
|
+
'map', 'mark', 'meter',
|
61
|
+
'nav', 'noframes', 'noscript',
|
62
|
+
'object', 'ol', 'optgroup', 'option',
|
63
|
+
'p', 'param', 'pre', 'progress',
|
64
|
+
'q', 'ruby', 'rt', 'rp', 's',
|
65
|
+
'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike',
|
64
66
|
'strong', 'style', 'sub', 'sup',
|
65
|
-
'table', 'tbody', 'td', 'textarea', 'tfoot',
|
66
|
-
'th', 'thead', 'title', 'tr', 'tt', 'u', 'ul',
|
67
|
+
'table', 'tbody', 'td', 'textarea', 'tfoot',
|
68
|
+
'th', 'thead', 'time', 'title', 'tr', 'tt', 'u', 'ul',
|
69
|
+
'var', 'video'
|
67
70
|
]
|
68
71
|
end
|
69
72
|
|
73
|
+
def def_empty_tag_method(tag_name)
|
74
|
+
self.class_eval(<<-SRC, __FILE__, __LINE__)
|
75
|
+
def #{tag_name}(*args, &block)
|
76
|
+
__empty_element__('#{tag_name}', *args, &block)
|
77
|
+
end
|
78
|
+
SRC
|
79
|
+
end
|
80
|
+
|
81
|
+
def def_full_tag_method(tag_name)
|
82
|
+
self.class_eval(<<-SRC, __FILE__, __LINE__)
|
83
|
+
def #{tag_name}(*args, &block)
|
84
|
+
__element__(false, '#{tag_name}', *args, &block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def #{tag_name}!(*args, &block)
|
88
|
+
__element__(true, '#{tag_name}', *args, &block)
|
89
|
+
end
|
90
|
+
SRC
|
91
|
+
end
|
92
|
+
|
70
93
|
def after_initialize(instance=nil, &blk)
|
71
94
|
if blk
|
72
95
|
after_initialize_parts << blk
|
@@ -139,6 +162,14 @@ module Erector
|
|
139
162
|
end
|
140
163
|
end
|
141
164
|
|
165
|
+
def self.get_needed_variables
|
166
|
+
get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.get_needed_defaults
|
170
|
+
get_needs.select{|need| need.is_a? Hash}
|
171
|
+
end
|
172
|
+
|
142
173
|
public
|
143
174
|
@@prettyprint_default = false
|
144
175
|
def prettyprint_default
|
@@ -160,6 +191,7 @@ module Erector
|
|
160
191
|
|
161
192
|
attr_reader *RESERVED_INSTANCE_VARS
|
162
193
|
attr_reader :parent
|
194
|
+
attr_writer :block
|
163
195
|
|
164
196
|
def initialize(assigns={}, &block)
|
165
197
|
unless assigns.is_a? Hash
|
@@ -178,12 +210,14 @@ module Erector
|
|
178
210
|
#++
|
179
211
|
|
180
212
|
protected
|
181
|
-
def context(output, prettyprint = false, indentation = 0, helpers = nil)
|
213
|
+
def context(parent, output, prettyprint = false, indentation = 0, helpers = nil)
|
182
214
|
#TODO: pass in options hash, maybe, instead of parameters
|
215
|
+
original_parent = @parent
|
183
216
|
original_output = @output
|
184
217
|
original_indendation = @indentation
|
185
218
|
original_helpers = @helpers
|
186
219
|
original_prettyprint = @prettyprint
|
220
|
+
@parent = parent
|
187
221
|
@output = output
|
188
222
|
@at_start_of_line = true
|
189
223
|
raise "indentation must be a number, not #{indentation.inspect}" unless indentation.is_a? Fixnum
|
@@ -192,6 +226,7 @@ module Erector
|
|
192
226
|
@prettyprint = prettyprint
|
193
227
|
yield
|
194
228
|
ensure
|
229
|
+
@parent = original_parent
|
195
230
|
@output = original_output
|
196
231
|
@indentation = original_indendation
|
197
232
|
@helpers = original_helpers
|
@@ -200,7 +235,7 @@ module Erector
|
|
200
235
|
|
201
236
|
public
|
202
237
|
def assign_instance_variables (instance_variables)
|
203
|
-
needed = self.class.
|
238
|
+
needed = self.class.get_needed_variables
|
204
239
|
assigned = []
|
205
240
|
instance_variables.each do |name, value|
|
206
241
|
unless needed.empty? || needed.include?(name)
|
@@ -211,7 +246,7 @@ module Erector
|
|
211
246
|
end
|
212
247
|
|
213
248
|
# set variables with default values
|
214
|
-
self.class.
|
249
|
+
self.class.get_needed_defaults.each do |hash|
|
215
250
|
hash.each_pair do |name, value|
|
216
251
|
unless assigned.include?(name)
|
217
252
|
assign_instance_variable(name, value)
|
@@ -239,6 +274,11 @@ module Erector
|
|
239
274
|
def to_pretty
|
240
275
|
to_s(:prettyprint => true)
|
241
276
|
end
|
277
|
+
|
278
|
+
# Render (like to_s) but stripping all tags.
|
279
|
+
def to_text
|
280
|
+
CGI.unescapeHTML(to_s(:prettyprint => false).gsub(/<[^>]*>/, ''))
|
281
|
+
end
|
242
282
|
|
243
283
|
# Entry point for rendering a widget (and all its children). This method
|
244
284
|
# creates a new output string (if necessary), calls this widget's #content
|
@@ -275,9 +315,10 @@ module Erector
|
|
275
315
|
:prettyprint => prettyprint_default,
|
276
316
|
:indentation => 0,
|
277
317
|
:helpers => nil,
|
318
|
+
:parent => @parent,
|
278
319
|
:content_method_name => :content,
|
279
320
|
}.merge(options)
|
280
|
-
context(options[:output], options[:prettyprint], options[:indentation], options[:helpers]) do
|
321
|
+
context(options[:parent], options[:output], options[:prettyprint], options[:indentation], options[:helpers]) do
|
281
322
|
send(options[:content_method_name], &blk)
|
282
323
|
output
|
283
324
|
end
|
@@ -286,13 +327,27 @@ module Erector
|
|
286
327
|
# Template method which must be overridden by all widget subclasses.
|
287
328
|
# Inside this method you call the magic #element methods which emit HTML
|
288
329
|
# and text to the output string. If you call "super" (or don't override
|
289
|
-
# +content
|
290
|
-
#
|
291
|
-
#
|
330
|
+
# +content+, or explicitly call "call_block") then your widget will
|
331
|
+
# execute the block that was passed into its constructor. The semantics of
|
332
|
+
# this block are confusing; make sure to read the rdoc for Erector#call_block
|
292
333
|
def content
|
293
|
-
|
294
|
-
|
295
|
-
|
334
|
+
call_block
|
335
|
+
end
|
336
|
+
|
337
|
+
# When this method is executed, the default block that was passed in to
|
338
|
+
# the widget's constructor will be executed. The semantics of this
|
339
|
+
# block -- that is, what "self" is, and whether it has access to
|
340
|
+
# Erector methods like "div" and "text", and the widget's instance
|
341
|
+
# variables -- can be quite confusing. The rule is, most of the time the
|
342
|
+
# block is evaluated using "call" or "yield", which means that its scope
|
343
|
+
# is that of the caller. So if that caller is not an Erector widget, it
|
344
|
+
# will *not* have access to the Erector methods, but it *will* have access
|
345
|
+
# to instance variables and methods of the calling object.
|
346
|
+
#
|
347
|
+
# If you want this block to have access to Erector methods then use
|
348
|
+
# Erector::Inline#content or Erector#inline.
|
349
|
+
def call_block
|
350
|
+
@block.call(self) if @block
|
296
351
|
end
|
297
352
|
|
298
353
|
# To call one widget from another, inside the parent widget's +content+
|
@@ -301,8 +356,7 @@ module Erector
|
|
301
356
|
# which gives better performance than using +capture+ or +to_s+. You can
|
302
357
|
# also use the +widget+ method.
|
303
358
|
def write_via(parent)
|
304
|
-
|
305
|
-
context(parent.output, parent.prettyprint, parent.indentation, parent.helpers) do
|
359
|
+
context(parent, parent.output, parent.prettyprint, parent.indentation, parent.helpers) do
|
306
360
|
content
|
307
361
|
end
|
308
362
|
end
|
@@ -311,10 +365,8 @@ module Erector
|
|
311
365
|
# either a class or an instance. If the first argument is a class, then
|
312
366
|
# the second argument is a hash used to populate its instance variables.
|
313
367
|
# If the first argument is an instance then the hash must be unspecified
|
314
|
-
# (or empty).
|
315
|
-
#
|
316
|
-
# The sub-widget will have access to the methods of the parent class, via
|
317
|
-
# some method_missing magic and a "parent" pointer.
|
368
|
+
# (or empty). If a block is passed to this method, then it gets set as the
|
369
|
+
# rendered widget's block.
|
318
370
|
def widget(target, assigns={}, &block)
|
319
371
|
child = if target.is_a? Class
|
320
372
|
target.new(assigns, &block)
|
@@ -322,6 +374,7 @@ module Erector
|
|
322
374
|
unless assigns.empty?
|
323
375
|
raise "Unexpected second parameter. Did you mean to pass in variables when you instantiated the #{target.class.to_s}?"
|
324
376
|
end
|
377
|
+
target.block = block unless block.nil?
|
325
378
|
target
|
326
379
|
end
|
327
380
|
child.write_via(self)
|
@@ -354,9 +407,14 @@ module Erector
|
|
354
407
|
# how elegant it is? Not confusing at all if you don't think about it.
|
355
408
|
#
|
356
409
|
def element(*args, &block)
|
357
|
-
__element__(*args, &block)
|
410
|
+
__element__(false, *args, &block)
|
358
411
|
end
|
359
|
-
|
412
|
+
|
413
|
+
# Like +element+, but string parameters are not escaped.
|
414
|
+
def element!(*args, &block)
|
415
|
+
__element__(true, *args, &block)
|
416
|
+
end
|
417
|
+
|
360
418
|
# Internal method used to emit a self-closing HTML/XML element, including
|
361
419
|
# a tag name and optional attributes (passed in via the default hash).
|
362
420
|
#
|
@@ -409,10 +467,12 @@ module Erector
|
|
409
467
|
end
|
410
468
|
|
411
469
|
# Emits text which will *not* be HTML-escaped. Same effect as text(raw(s))
|
412
|
-
def
|
470
|
+
def text!(value)
|
413
471
|
text raw(value)
|
414
472
|
end
|
415
473
|
|
474
|
+
alias rawtext text!
|
475
|
+
|
416
476
|
# Returns a copy of value with spaces replaced by non-breaking space characters.
|
417
477
|
# With no arguments, return a single non-breaking space.
|
418
478
|
# The output uses the escaping format ' ' since that works
|
@@ -443,7 +503,7 @@ module Erector
|
|
443
503
|
|
444
504
|
output <<("</#{tag_name}>")
|
445
505
|
|
446
|
-
if newliney(tag_name)
|
506
|
+
if newliney?(tag_name)
|
447
507
|
_newline
|
448
508
|
end
|
449
509
|
end
|
@@ -467,17 +527,36 @@ module Erector
|
|
467
527
|
output << "<?xml#{format_sorted(sort_for_xml_declaration(attributes))}?>"
|
468
528
|
end
|
469
529
|
|
470
|
-
# Emits an HTML comment
|
530
|
+
# Emits an HTML comment (<!-- ... -->) surrounding +text+ and/or the output of +block+.
|
471
531
|
# see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
|
532
|
+
#
|
533
|
+
# If +text+ is an Internet Explorer conditional comment condition such as "[if IE]",
|
534
|
+
# the output includes the opening condition and closing "[endif]". See
|
535
|
+
# http://www.quirksmode.org/css/condcom.html
|
536
|
+
#
|
472
537
|
# Since "Authors should avoid putting two or more adjacent hyphens inside comments,"
|
473
538
|
# we emit a warning if you do that.
|
474
|
-
def comment(text)
|
539
|
+
def comment(text = '', &block)
|
475
540
|
puts "Warning: Authors should avoid putting two or more adjacent hyphens inside comments." if text =~ /--/
|
476
|
-
|
541
|
+
|
542
|
+
conditional = text =~ /\[if .*\]/
|
543
|
+
|
544
|
+
rawtext "<!--"
|
545
|
+
rawtext text
|
546
|
+
rawtext ">" if conditional
|
547
|
+
|
548
|
+
if block
|
549
|
+
rawtext "\n"
|
550
|
+
block.call
|
551
|
+
rawtext "\n"
|
552
|
+
end
|
553
|
+
|
554
|
+
rawtext "<![endif]" if conditional
|
555
|
+
rawtext "-->\n"
|
477
556
|
end
|
478
557
|
|
479
558
|
# Creates a whole new output string, executes the block, then converts the
|
480
|
-
# output string to a string and
|
559
|
+
# output string to a string and returns it as raw text. If at all possible
|
481
560
|
# you should avoid this method since it hurts performance, and use
|
482
561
|
# +widget+ or +write_via+ instead.
|
483
562
|
def capture(&block)
|
@@ -492,23 +571,11 @@ module Erector
|
|
492
571
|
end
|
493
572
|
|
494
573
|
full_tags.each do |tag_name|
|
495
|
-
|
496
|
-
"def #{tag_name}(*args, &block)\n" <<
|
497
|
-
" __element__('#{tag_name}', *args, &block)\n" <<
|
498
|
-
"end",
|
499
|
-
__FILE__,
|
500
|
-
__LINE__ - 4
|
501
|
-
)
|
574
|
+
def_full_tag_method(tag_name)
|
502
575
|
end
|
503
576
|
|
504
577
|
empty_tags.each do |tag_name|
|
505
|
-
|
506
|
-
"def #{tag_name}(*args, &block)\n" <<
|
507
|
-
" __empty_element__('#{tag_name}', *args, &block)\n" <<
|
508
|
-
"end",
|
509
|
-
__FILE__,
|
510
|
-
__LINE__ - 4
|
511
|
-
)
|
578
|
+
def_empty_tag_method(tag_name)
|
512
579
|
end
|
513
580
|
|
514
581
|
# Emits a javascript block inside a +script+ tag, wrapped in CDATA
|
@@ -556,23 +623,21 @@ module Erector
|
|
556
623
|
# The parameter is the full contents of the href attribute, including any ".css" extension.
|
557
624
|
#
|
558
625
|
# If you want to emit raw CSS inline, use the #style method instead.
|
559
|
-
def css(href)
|
560
|
-
link
|
626
|
+
def css(href, options = {})
|
627
|
+
link({:rel => 'stylesheet', :type => 'text/css', :href => href}.merge(options))
|
561
628
|
end
|
562
629
|
|
563
630
|
# Convenience method to emit an anchor tag whose href and text are the same,
|
564
631
|
# e.g. <a href="http://example.com">http://example.com</a>
|
565
|
-
def url(href)
|
566
|
-
a href, :href => href
|
632
|
+
def url(href, options = {})
|
633
|
+
a href, ({:href => href}.merge(options))
|
567
634
|
end
|
568
635
|
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
end
|
575
|
-
end
|
636
|
+
# makes a unique id based on the widget's class name and object id
|
637
|
+
# that you can use as the HTML id of an emitted element
|
638
|
+
def dom_id
|
639
|
+
"#{self.class.name.gsub(/:+/,"_")}_#{self.object_id}"
|
640
|
+
end
|
576
641
|
|
577
642
|
# emits a jQuery script that is to be run on document ready
|
578
643
|
def jquery(txt)
|
@@ -584,7 +649,7 @@ module Erector
|
|
584
649
|
protected
|
585
650
|
def jquery_ready(txt)
|
586
651
|
rawtext "\n"
|
587
|
-
rawtext "
|
652
|
+
rawtext "jQuery(document).ready(function($){\n"
|
588
653
|
rawtext txt
|
589
654
|
rawtext "\n});"
|
590
655
|
end
|
@@ -592,10 +657,9 @@ module Erector
|
|
592
657
|
### internal utility methods
|
593
658
|
|
594
659
|
protected
|
595
|
-
|
596
|
-
def __element__(tag_name, *args, &block)
|
660
|
+
def __element__(raw, tag_name, *args, &block)
|
597
661
|
if args.length > 2
|
598
|
-
raise ArgumentError, "Cannot accept more than
|
662
|
+
raise ArgumentError, "Cannot accept more than four arguments"
|
599
663
|
end
|
600
664
|
attributes, value = nil, nil
|
601
665
|
arg0 = args[0]
|
@@ -614,7 +678,9 @@ protected
|
|
614
678
|
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
615
679
|
end
|
616
680
|
if block
|
617
|
-
|
681
|
+
block.call
|
682
|
+
elsif raw
|
683
|
+
text! value
|
618
684
|
else
|
619
685
|
text value
|
620
686
|
end
|
@@ -626,7 +692,7 @@ protected
|
|
626
692
|
|
627
693
|
output << "<#{tag_name}#{format_attributes(attributes)} />"
|
628
694
|
|
629
|
-
if newliney(tag_name)
|
695
|
+
if newliney?(tag_name)
|
630
696
|
_newline
|
631
697
|
end
|
632
698
|
end
|
@@ -639,7 +705,7 @@ protected
|
|
639
705
|
|
640
706
|
def indent_for_open_tag(tag_name)
|
641
707
|
return unless @prettyprint
|
642
|
-
if !@at_start_of_line && newliney(tag_name)
|
708
|
+
if !@at_start_of_line && newliney?(tag_name)
|
643
709
|
_newline
|
644
710
|
end
|
645
711
|
indent()
|
@@ -664,7 +730,9 @@ protected
|
|
664
730
|
sorted.each do |key, value|
|
665
731
|
if value
|
666
732
|
if value.is_a?(Array)
|
667
|
-
value =
|
733
|
+
value = value.flatten
|
734
|
+
next if value.empty?
|
735
|
+
value = value.join(' ')
|
668
736
|
end
|
669
737
|
results << "#{key}=\"#{value.html_escape}\""
|
670
738
|
end
|
@@ -689,5 +757,14 @@ protected
|
|
689
757
|
end
|
690
758
|
return stringized.sort{|a, b| b <=> a}
|
691
759
|
end
|
760
|
+
|
761
|
+
def newliney?(tag_name)
|
762
|
+
if @prettyprint
|
763
|
+
!NON_NEWLINEY.include?(tag_name)
|
764
|
+
else
|
765
|
+
false
|
766
|
+
end
|
767
|
+
end
|
768
|
+
|
692
769
|
end
|
693
770
|
end
|