erector 0.7.2 → 0.8.0
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 +17 -3
- data/VERSION.yml +2 -2
- data/bin/erector +1 -1
- data/lib/erector.rb +22 -2
- data/lib/erector/after_initialize.rb +34 -0
- data/lib/erector/caching.rb +93 -0
- data/lib/erector/convenience.rb +58 -0
- data/lib/erector/dependencies.rb +24 -0
- data/lib/erector/dependency.rb +21 -0
- data/lib/erector/{erect.rb → erect/erect.rb} +14 -4
- data/lib/erector/{erected.rb → erect/erected.rb} +6 -4
- data/lib/erector/{indenting.rb → erect/indenting.rb} +0 -0
- data/lib/erector/{rhtml.treetop → erect/rhtml.treetop} +51 -11
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/externals.rb +88 -24
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +5 -5
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +3 -5
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +2 -2
- data/lib/erector/rails/extensions/action_controller.rb +5 -3
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +98 -56
- data/lib/erector/rails/rails_form_builder.rb +8 -4
- data/lib/erector/rails/rails_version.rb +2 -2
- data/lib/erector/rails/template_handlers/ert_handler.rb +1 -1
- data/lib/erector/rails/template_handlers/rb_handler.rb +42 -1
- data/lib/erector/raw_string.rb +2 -2
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/widget.rb +100 -653
- data/lib/erector/widgets.rb +1 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/page.rb +45 -63
- data/lib/erector/widgets/table.rb +9 -1
- data/spec/erect/erect_rails_spec.rb +19 -17
- data/spec/erect/erect_spec.rb +11 -1
- data/spec/erect/erected_spec.rb +76 -5
- data/spec/erect/rhtml_parser_spec.rb +11 -1
- data/spec/erector/caching_spec.rb +267 -0
- data/spec/erector/convenience_spec.rb +258 -0
- data/spec/erector/dependency_spec.rb +46 -0
- data/spec/erector/externals_spec.rb +233 -0
- data/spec/erector/html_spec.rb +508 -0
- data/spec/erector/indentation_spec.rb +84 -24
- data/spec/erector/inline_spec.rb +19 -8
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +1 -1
- data/spec/erector/needs_spec.rb +120 -0
- data/spec/erector/output_spec.rb +199 -0
- data/spec/erector/sample-file.txt +1 -0
- data/spec/erector/sass_spec.rb +33 -0
- data/spec/erector/widget_spec.rb +113 -932
- data/spec/erector/widgets/field_table_spec.rb +6 -6
- data/spec/erector/widgets/form_spec.rb +3 -3
- data/spec/erector/widgets/page_spec.rb +52 -6
- data/spec/erector/widgets/table_spec.rb +4 -4
- data/spec/spec_helper.rb +70 -29
- metadata +56 -19
- data/lib/erector/rails/extensions/rails_widget/rails_helpers.rb +0 -137
- data/spec/core_spec_suite.rb +0 -3
- data/spec/erector/external_spec.rb +0 -110
- data/spec/rails_spec_suite.rb +0 -3
- data/spec/spec.opts +0 -1
- data/spec/spec_suite.rb +0 -40
data/lib/erector/inline.rb
CHANGED
@@ -15,21 +15,21 @@ module Erector
|
|
15
15
|
# to smuggle in instance variables.
|
16
16
|
def call_block
|
17
17
|
# note that instance_eval seems to pass in self as a parameter to the block
|
18
|
-
instance_eval(
|
18
|
+
instance_eval(&block) if block
|
19
19
|
end
|
20
20
|
|
21
21
|
private
|
22
22
|
# This is part of the sub-widget/parent feature (see #widget method).
|
23
23
|
def method_missing(name, *args, &block)
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
if parent && parent.respond_to?(name)
|
25
|
+
block ||= lambda {} # captures self HERE
|
26
|
+
parent.send(name, *args, &block)
|
27
27
|
else
|
28
28
|
super
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
class InlineWidget < Erector::Widget
|
34
34
|
include Inline
|
35
35
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Erector
|
2
|
+
module JQuery
|
3
|
+
# Emits a jQuery script, inside its own script tag, that is to be run on document ready or load.
|
4
|
+
#
|
5
|
+
# Usage (from inside a widget method):
|
6
|
+
# jquery "alert('hi')" :: a jquery ready handler
|
7
|
+
# jquery "alert('hi')", :id => 'foo' :: a jquery ready handler, with attributes in the script tag
|
8
|
+
# jquery :load, "alert('hi')" :: a jquery load handler
|
9
|
+
#
|
10
|
+
def jquery(*args)
|
11
|
+
event = if args.first.is_a? Symbol
|
12
|
+
args.shift
|
13
|
+
else
|
14
|
+
:ready
|
15
|
+
end
|
16
|
+
txt = args.shift
|
17
|
+
attributes = args.shift || {}
|
18
|
+
|
19
|
+
javascript attributes do
|
20
|
+
rawtext "\n"
|
21
|
+
rawtext "jQuery(document).#{event}(function($){\n"
|
22
|
+
rawtext txt
|
23
|
+
rawtext "\n});"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def jquery_load(text) #:nodoc:
|
28
|
+
$stderr.puts "jquery_load is deprecated; use jquery(:load, text) instead"
|
29
|
+
end
|
30
|
+
|
31
|
+
def jquery_ready(text) #:nodoc:
|
32
|
+
$stderr.puts "jquery_ready is deprecated; use jquery(:ready, text) instead"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/erector/mixin.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
module Erector
|
2
2
|
module Mixin
|
3
3
|
# Executes the block as if it were the content body of a fresh Erector::Inline,
|
4
|
-
# and returns the #
|
4
|
+
# and returns the #to_html 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
6
|
# have access to bound variables. Funnily enough, the options are passed in to both
|
7
|
-
#
|
7
|
+
# to_html *and* to the widget itself, so they show up as instance variables.
|
8
8
|
def erector(options = {}, &block)
|
9
|
-
|
10
|
-
vars.delete_if {|key, value| Erector::Widget::RESERVED_INSTANCE_VARS.include?(key)}
|
11
|
-
Erector.inline(vars, &block).to_s(options)
|
9
|
+
Erector.inline(options, &block).to_html(options)
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Erector
|
2
|
+
module Needs
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# Class method by which widget classes can declare that they need certain
|
9
|
+
# parameters. If needed parameters are not passed in to #new, then an
|
10
|
+
# exception will be thrown (with a hopefully useful message about which
|
11
|
+
# parameters are missing). This is intended to catch silly bugs like
|
12
|
+
# passing in a parameter called 'name' to a widget that expects a
|
13
|
+
# parameter called 'title'.
|
14
|
+
#
|
15
|
+
# You can also declare default values for parameters using hash syntax.
|
16
|
+
# You can put #needs declarations on multiple lines or on the same line;
|
17
|
+
# the only caveat is that if there are default values, they all have to be
|
18
|
+
# at the end of the line (so they go into the magic hash parameter).
|
19
|
+
#
|
20
|
+
# If a widget has no #needs declaration then it will accept any
|
21
|
+
# combination of parameters just like normal. If a widget wants to declare
|
22
|
+
# that it takes no parameters, use the special incantation "needs nil"
|
23
|
+
# (and don't declare any other needs, or kittens will cry).
|
24
|
+
#
|
25
|
+
# Usage:
|
26
|
+
# class FancyForm < Erector::Widget
|
27
|
+
# needs :title, :show_okay => true, :show_cancel => false
|
28
|
+
# ...
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# That means that
|
32
|
+
# FancyForm.new(:title => 'Login')
|
33
|
+
# will succeed, as will
|
34
|
+
# FancyForm.new(:title => 'Login', :show_cancel => true)
|
35
|
+
# but
|
36
|
+
# FancyForm.new(:name => 'Login')
|
37
|
+
# will fail.
|
38
|
+
#
|
39
|
+
def needs(*args)
|
40
|
+
args.each do |arg|
|
41
|
+
(@needs ||= []) << (arg.nil? ? nil : (arg.is_a? Hash) ? arg : arg.to_sym)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_needs
|
46
|
+
@needs ||= []
|
47
|
+
|
48
|
+
ancestors[1..-1].inject(@needs.dup) do |needs, ancestor|
|
49
|
+
needs.push(*ancestor.get_needs) if ancestor.respond_to?(:get_needs)
|
50
|
+
needs
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def needed_variables
|
55
|
+
@needed_variables ||= get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
|
56
|
+
end
|
57
|
+
|
58
|
+
def needed_defaults
|
59
|
+
@needed_defaults ||= get_needs.inject({}) do |defaults, need|
|
60
|
+
defaults = need.merge(defaults) if need.is_a? Hash
|
61
|
+
defaults
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def needs?(name)
|
66
|
+
needed_variables.empty? || needed_variables.include?(name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(assigns = {})
|
71
|
+
super
|
72
|
+
|
73
|
+
assigned = assigns.keys
|
74
|
+
|
75
|
+
# set variables with default values
|
76
|
+
self.class.needed_defaults.each do |name, value|
|
77
|
+
unless assigned.include?(name)
|
78
|
+
instance_variable_set("@#{name}", value)
|
79
|
+
assigned << name
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
missing = self.class.needed_variables - assigned
|
84
|
+
unless missing.empty? || missing == [nil]
|
85
|
+
raise "Missing parameter#{missing.size == 1 ? '' : 's'}: #{missing.join(', ')}"
|
86
|
+
end
|
87
|
+
|
88
|
+
excess = assigned - self.class.needed_variables
|
89
|
+
unless self.class.needed_variables.empty? || excess.empty?
|
90
|
+
raise("Excess parameter#{excess.size == 1 ? '' : 's'}: #{excess.join(', ')}")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Erector
|
2
|
+
class Output
|
3
|
+
SPACES_PER_INDENT = 2
|
4
|
+
|
5
|
+
attr_reader :prettyprint, :widgets, :indentation, :max_length
|
6
|
+
|
7
|
+
def initialize(options = {}, & get_buffer)
|
8
|
+
@prettyprint = options.fetch(:prettyprint, Widget.prettyprint_default)
|
9
|
+
@indentation = options.fetch(:indentation, 0)
|
10
|
+
@current_line_length = 0
|
11
|
+
@max_length = options[:max_length]
|
12
|
+
@widgets = []
|
13
|
+
if get_buffer
|
14
|
+
@get_buffer = get_buffer
|
15
|
+
elsif buffer = options[:output]
|
16
|
+
@get_buffer = lambda { buffer }
|
17
|
+
else
|
18
|
+
buffer = []
|
19
|
+
@get_buffer = lambda { buffer }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def buffer
|
24
|
+
@get_buffer.call
|
25
|
+
end
|
26
|
+
|
27
|
+
def <<(s)
|
28
|
+
s = s.to_s unless s.is_a? String
|
29
|
+
append_indentation
|
30
|
+
if @max_length && s.length + @current_line_length > @max_length
|
31
|
+
leading_spaces = s =~ /^( +)/ ? $1.size : 0
|
32
|
+
trailing_spaces = s =~ /( +)$/ ? $1.size : 0
|
33
|
+
|
34
|
+
append(" " * leading_spaces)
|
35
|
+
need_space = false
|
36
|
+
words = s.split(/ /)
|
37
|
+
words.each do |word|
|
38
|
+
if (need_space ? 1 : 0) + word.length > space_left
|
39
|
+
append_newline
|
40
|
+
append_indentation
|
41
|
+
need_space = false
|
42
|
+
end
|
43
|
+
append(" ") if need_space
|
44
|
+
append(word)
|
45
|
+
need_space = true
|
46
|
+
end
|
47
|
+
append(" " * trailing_spaces)
|
48
|
+
else
|
49
|
+
append(s)
|
50
|
+
end
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
# Inserts a blank string into the output stream and returns a pointer to it.
|
55
|
+
# If the caller holds on to this pointer, she can later go back and insert text
|
56
|
+
# earlier in the stream. This is used for, e.g., inserting stuff inside the
|
57
|
+
# HEAD element that is not known until after the entire page renders.
|
58
|
+
def placeholder
|
59
|
+
s = ""
|
60
|
+
buffer << s
|
61
|
+
s
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
RawString.new(buffer.to_s)
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_a
|
69
|
+
buffer.to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
def newline
|
73
|
+
if prettyprint
|
74
|
+
append_newline
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def at_line_start?
|
79
|
+
@current_line_length == 0
|
80
|
+
end
|
81
|
+
|
82
|
+
def indent
|
83
|
+
@indentation += 1 if prettyprint
|
84
|
+
end
|
85
|
+
|
86
|
+
def undent
|
87
|
+
@indentation -= 1 if prettyprint
|
88
|
+
end
|
89
|
+
|
90
|
+
# always append a newline, regardless of prettyprint setting
|
91
|
+
#todo: test
|
92
|
+
def append_newline
|
93
|
+
buffer << "\n"
|
94
|
+
@current_line_length = 0
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
def append(s)
|
100
|
+
buffer << s
|
101
|
+
@current_line_length += s.length
|
102
|
+
end
|
103
|
+
|
104
|
+
def space_left
|
105
|
+
@max_length - @current_line_length
|
106
|
+
end
|
107
|
+
|
108
|
+
def append_indentation
|
109
|
+
if prettyprint and at_line_start?
|
110
|
+
spaces = " " * ([@indentation, 0].max * SPACES_PER_INDENT)
|
111
|
+
buffer << spaces
|
112
|
+
@current_line_length += spaces.length
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
data/lib/erector/rails.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "action_controller"
|
2
2
|
require "erector/rails/rails_version"
|
3
3
|
require "erector/rails/rails_form_builder"
|
4
|
-
require "erector/rails/extensions/rails_widget"
|
5
4
|
require "erector/rails/extensions/action_controller"
|
6
|
-
require "erector/rails/extensions/
|
5
|
+
require "erector/rails/extensions/rails_helpers"
|
6
|
+
require "erector/rails/extensions/rails_widget"
|
7
7
|
require "erector/rails/template_handlers/rb_handler"
|
8
8
|
require "erector/rails/template_handlers/ert_handler"
|
9
9
|
|
@@ -1,11 +1,13 @@
|
|
1
1
|
ActionController::Base.class_eval do
|
2
|
-
|
3
|
-
|
2
|
+
class_inheritable_accessor :ert_template_base_class
|
3
|
+
|
4
|
+
def render_widget(widget_class, assigns=nil, options={})
|
5
|
+
render options.merge(:text => Erector::Rails.render(widget_class, response.template, assigns, options))
|
4
6
|
end
|
5
7
|
|
6
8
|
def render_with_erector_widget(*options, &block)
|
7
9
|
if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
|
8
|
-
render_widget widget, @assigns
|
10
|
+
render_widget widget, @assigns, options.first
|
9
11
|
else
|
10
12
|
render_without_erector_widget *options, &block
|
11
13
|
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Erector
|
2
|
+
module Rails
|
3
|
+
module Helpers
|
4
|
+
# Set up URL helpers so that both helpers.users_url and users_url can be called.
|
5
|
+
include ActionController::UrlWriter
|
6
|
+
|
7
|
+
def url_for(*args)
|
8
|
+
parent.url_for(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Wrappers for rails helpers that produce markup. Erector needs to
|
12
|
+
# manually emit their result.
|
13
|
+
def self.def_simple_rails_helper(method_name)
|
14
|
+
module_eval(<<-METHOD_DEF, __FILE__, __LINE__+1)
|
15
|
+
def #{method_name}(*args, &block)
|
16
|
+
text parent.#{method_name}(*args, &block)
|
17
|
+
end
|
18
|
+
METHOD_DEF
|
19
|
+
end
|
20
|
+
|
21
|
+
[
|
22
|
+
# UrlHelper
|
23
|
+
:link_to,
|
24
|
+
:button_to,
|
25
|
+
:link_to_unless_current,
|
26
|
+
:link_to_unless,
|
27
|
+
:link_to_if,
|
28
|
+
:mail_to,
|
29
|
+
|
30
|
+
# JavaScriptHelper
|
31
|
+
:link_to_function,
|
32
|
+
:button_to_function,
|
33
|
+
|
34
|
+
# FormTagHelper
|
35
|
+
:select_tag,
|
36
|
+
:text_field_tag,
|
37
|
+
:label_tag,
|
38
|
+
:hidden_field_tag,
|
39
|
+
:file_field_tag,
|
40
|
+
:password_field_tag,
|
41
|
+
:text_area_tag,
|
42
|
+
:check_box_tag,
|
43
|
+
:radio_button_tag,
|
44
|
+
:submit_tag,
|
45
|
+
:image_submit_tag,
|
46
|
+
:field_set_tag,
|
47
|
+
|
48
|
+
# FormHelper
|
49
|
+
:form_for,
|
50
|
+
:text_field,
|
51
|
+
:password_field,
|
52
|
+
:hidden_field,
|
53
|
+
:file_field,
|
54
|
+
:text_area,
|
55
|
+
:check_box,
|
56
|
+
:radio_button,
|
57
|
+
|
58
|
+
# ActiveRecordHelper
|
59
|
+
:error_message_on,
|
60
|
+
:error_messages_for,
|
61
|
+
|
62
|
+
# AssetTagHelper
|
63
|
+
:auto_discovery_link_tag,
|
64
|
+
:javascript_include_tag,
|
65
|
+
:stylesheet_link_tag,
|
66
|
+
:image_tag,
|
67
|
+
|
68
|
+
# ScriptaculousHelper
|
69
|
+
:sortable_element,
|
70
|
+
:sortable_element_js,
|
71
|
+
:text_field_with_auto_complete,
|
72
|
+
:draggable_element,
|
73
|
+
:drop_receiving_element,
|
74
|
+
|
75
|
+
# PrototypeHelper
|
76
|
+
:link_to_remote,
|
77
|
+
:button_to_remote,
|
78
|
+
:periodically_call_remote,
|
79
|
+
:form_remote_tag,
|
80
|
+
:submit_to_remote,
|
81
|
+
:update_page_tag
|
82
|
+
].each do |method_name|
|
83
|
+
def_simple_rails_helper(method_name)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Wrappers for rails helpers that produce markup, concatenating
|
87
|
+
# directly to the output buffer if given a block, returning a
|
88
|
+
# string otherwise. In the latter case, Erector needs to manually
|
89
|
+
# output their result.
|
90
|
+
def self.def_block_rails_helper(method_name)
|
91
|
+
module_eval(<<-METHOD_DEF, __FILE__, __LINE__+1)
|
92
|
+
def #{method_name}(*args, &block)
|
93
|
+
if block_given?
|
94
|
+
parent.#{method_name}(*args, &block)
|
95
|
+
else
|
96
|
+
text parent.#{method_name}(*args, &block)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
METHOD_DEF
|
100
|
+
end
|
101
|
+
|
102
|
+
[:link_to,
|
103
|
+
:form_tag,
|
104
|
+
:field_set_tag,
|
105
|
+
:form_remote_tag,
|
106
|
+
:javascript_tag].each do |method_name|
|
107
|
+
def_block_rails_helper(method_name)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Delegate to non-markup producing helpers via method_missing,
|
111
|
+
# returning their result directly.
|
112
|
+
def method_missing(name, *args, &block)
|
113
|
+
if parent.respond_to?(name)
|
114
|
+
parent.send(name, *args, &block)
|
115
|
+
else
|
116
|
+
super
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Since we delegate method_missing to parent, we need to delegate
|
121
|
+
# respond_to? as well.
|
122
|
+
def respond_to?(name)
|
123
|
+
super || parent.respond_to?(name)
|
124
|
+
end
|
125
|
+
|
126
|
+
def render(*args, &block)
|
127
|
+
captured = parent.capture do
|
128
|
+
parent.concat(parent.render(*args, &block))
|
129
|
+
parent.output_buffer.to_s
|
130
|
+
end
|
131
|
+
rawtext(captured)
|
132
|
+
end
|
133
|
+
|
134
|
+
def form_for(record_or_name_or_array, *args, &proc)
|
135
|
+
options = args.extract_options!
|
136
|
+
options[:builder] ||= ::Erector::RailsFormBuilder
|
137
|
+
args.push(options)
|
138
|
+
parent.form_for(record_or_name_or_array, *args, &proc)
|
139
|
+
end
|
140
|
+
|
141
|
+
def fields_for(record_or_name_or_array, *args, &proc)
|
142
|
+
options = args.extract_options!
|
143
|
+
options[:builder] ||= ::Erector::RailsFormBuilder
|
144
|
+
args.push(options)
|
145
|
+
parent.fields_for(record_or_name_or_array, *args, &proc)
|
146
|
+
end
|
147
|
+
|
148
|
+
def flash
|
149
|
+
parent.controller.send(:flash)
|
150
|
+
end
|
151
|
+
|
152
|
+
def session
|
153
|
+
parent.controller.session
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
Erector::Widget.send :include, Helpers
|
158
|
+
end
|
159
|
+
end
|