erector 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|