honkster-erector 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +116 -0
- data/VERSION.yml +4 -0
- data/bin/erector +14 -0
- data/lib/erector.rb +34 -0
- data/lib/erector/abstract_widget.rb +172 -0
- 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 +30 -0
- data/lib/erector/erect/erect.rb +160 -0
- data/lib/erector/erect/erected.rb +75 -0
- data/lib/erector/erect/indenting.rb +36 -0
- data/lib/erector/erect/rhtml.treetop +233 -0
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/extensions/object.rb +18 -0
- data/lib/erector/externals.rb +97 -0
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +37 -0
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +12 -0
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +27 -0
- data/lib/erector/rails/extensions/action_controller.rb +16 -0
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +126 -0
- data/lib/erector/rails/rails_form_builder.rb +24 -0
- data/lib/erector/rails/rails_version.rb +6 -0
- data/lib/erector/rails/template_handlers/ert_handler.rb +32 -0
- data/lib/erector/rails/template_handlers/rb_handler.rb +52 -0
- data/lib/erector/raw_string.rb +8 -0
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/unicode.rb +18185 -0
- data/lib/erector/unicode_builder.rb +67 -0
- data/lib/erector/version.rb +12 -0
- data/lib/erector/widget.rb +54 -0
- data/lib/erector/widgets.rb +6 -0
- data/lib/erector/widgets/environment_badge.rb +29 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/field_table.rb +110 -0
- data/lib/erector/widgets/form.rb +30 -0
- data/lib/erector/widgets/page.rb +165 -0
- data/lib/erector/widgets/table.rb +104 -0
- data/rails/init.rb +4 -0
- data/spec/erect/erect_rails_spec.rb +114 -0
- data/spec/erect/erect_spec.rb +175 -0
- data/spec/erect/erected_spec.rb +164 -0
- data/spec/erect/rhtml_parser_spec.rb +361 -0
- data/spec/erector/caching_spec.rb +269 -0
- data/spec/erector/convenience_spec.rb +259 -0
- data/spec/erector/dependency_spec.rb +67 -0
- data/spec/erector/externals_spec.rb +236 -0
- data/spec/erector/html_spec.rb +509 -0
- data/spec/erector/indentation_spec.rb +211 -0
- data/spec/erector/inline_spec.rb +94 -0
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +65 -0
- 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/unicode_builder_spec.rb +75 -0
- data/spec/erector/widget_spec.rb +250 -0
- data/spec/erector/widgets/field_table_spec.rb +133 -0
- data/spec/erector/widgets/form_spec.rb +31 -0
- data/spec/erector/widgets/page_spec.rb +85 -0
- data/spec/erector/widgets/table_spec.rb +99 -0
- data/spec/spec_helper.rb +95 -0
- metadata +191 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
|
+
|
3
|
+
module Erector
|
4
|
+
module Widgets
|
5
|
+
|
6
|
+
describe FieldTable do
|
7
|
+
|
8
|
+
describe "a basic, brittle characterization test, just to get up and running" do
|
9
|
+
|
10
|
+
class PasswordForm < Erector::Widget
|
11
|
+
def content
|
12
|
+
form :action => "/user", :method => "post" do
|
13
|
+
widget(FieldTable.new(:title => "Sign Up") do |t|
|
14
|
+
t.field("Name") do
|
15
|
+
input(:name => "name", :type => "text", :size => "30", :value => @username)
|
16
|
+
end
|
17
|
+
t.field("Password") do
|
18
|
+
input(:name => "password", :type => "password", :size => "30")
|
19
|
+
end
|
20
|
+
t.field("Password Again",
|
21
|
+
"Yes, we really want you to type your new password twice, for some reason.") do
|
22
|
+
input(:name => "password_verify", :type => "password", :size => "30")
|
23
|
+
end
|
24
|
+
t.button do
|
25
|
+
input(:name => "signup", :type => "submit", :value => "Sign Up")
|
26
|
+
end
|
27
|
+
end)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "renders the CreateUser form" do
|
33
|
+
PasswordForm.new(:username => "bobdole").to_html.should ==
|
34
|
+
"<form action=\"/user\" method=\"post\">" +
|
35
|
+
"<fieldset class=\"field_table\">" +
|
36
|
+
"<legend>Sign Up</legend>" +
|
37
|
+
"<table width=\"100%\">" +
|
38
|
+
"<tr class=\"field_table_field\">" +
|
39
|
+
"<th>" +
|
40
|
+
"Name:</th>" +
|
41
|
+
"<td>" +
|
42
|
+
"<input name=\"name\" size=\"30\" type=\"text\" value=\"bobdole\" />" +
|
43
|
+
"</td>" +
|
44
|
+
"</tr>" +
|
45
|
+
"<tr class=\"field_table_field\">" +
|
46
|
+
"<th>" +
|
47
|
+
"Password:</th>" +
|
48
|
+
"<td>" +
|
49
|
+
"<input name=\"password\" size=\"30\" type=\"password\" />" +
|
50
|
+
"</td>" +
|
51
|
+
"</tr>" +
|
52
|
+
"<tr class=\"field_table_field\">" +
|
53
|
+
"<th>" +
|
54
|
+
"Password Again:</th>" +
|
55
|
+
"<td>" +
|
56
|
+
"<input name=\"password_verify\" size=\"30\" type=\"password\" />" +
|
57
|
+
"</td>" +
|
58
|
+
"<td>" +
|
59
|
+
"Yes, we really want you to type your new password twice, for some reason.</td>" +
|
60
|
+
"</tr>" +
|
61
|
+
"<tr class=\"field_table_buttons\">" +
|
62
|
+
"<td align=\"right\" colspan=\"2\">" +
|
63
|
+
"<table class=\"layout\">" +
|
64
|
+
"<tr>" +
|
65
|
+
"<td class=\"field_table_button\">" +
|
66
|
+
"<input name=\"signup\" type=\"submit\" value=\"Sign Up\" />" +
|
67
|
+
"</td>" +
|
68
|
+
"</tr>" +
|
69
|
+
"</table>" +
|
70
|
+
"</td>" +
|
71
|
+
"</tr>" +
|
72
|
+
"</table>" +
|
73
|
+
"</fieldset>" +
|
74
|
+
"</form>"
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "using the configuration API to construct it on the fly" do
|
80
|
+
|
81
|
+
it "renders a table with no fields and no buttons" do
|
82
|
+
table = FieldTable.new(:title => "Meals")
|
83
|
+
doc = Nokogiri::HTML(table.to_html)
|
84
|
+
doc.css("fieldset legend").text.should == "Meals"
|
85
|
+
doc.at("fieldset")["class"].should == "field_table"
|
86
|
+
doc.css("fieldset > table > tr").size.should == 0
|
87
|
+
end
|
88
|
+
|
89
|
+
it "renders a table with no fields and one button" do
|
90
|
+
table = FieldTable.new(:title => "Meals") do |t|
|
91
|
+
t.button { t.input :type => "button", :value => "cancel" }
|
92
|
+
end
|
93
|
+
doc = Nokogiri::HTML(table.to_html)
|
94
|
+
doc.css("fieldset > table > tr").size.should == 1
|
95
|
+
doc.at("fieldset table tr")["class"].should == "field_table_buttons"
|
96
|
+
doc.at("td.field_table_button input")["value"].should == "cancel"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "renders a table with a field and no buttons" do
|
100
|
+
table = FieldTable.new(:title => "Meals") do |t|
|
101
|
+
t.field("Breakfast") { t.text "scrambled eggs" }
|
102
|
+
end
|
103
|
+
doc = Nokogiri::HTML(table.to_html)
|
104
|
+
doc.css("fieldset > table > tr").size.should == 1
|
105
|
+
doc.at("fieldset table tr")["class"].should == "field_table_field"
|
106
|
+
doc.at("fieldset table tr th").text.should == "Breakfast:"
|
107
|
+
doc.at("fieldset table tr td").text.should == "scrambled eggs"
|
108
|
+
end
|
109
|
+
|
110
|
+
it "renders a table with a field with no label" do
|
111
|
+
table = FieldTable.new(:title => "Meals") do |t|
|
112
|
+
t.field { t.text "yum yum" }
|
113
|
+
end
|
114
|
+
doc = Nokogiri::HTML(table.to_html)
|
115
|
+
doc.css("fieldset > table > tr").size.should == 1
|
116
|
+
doc.at("fieldset table tr")["class"].should == "field_table_field"
|
117
|
+
doc.at("fieldset table tr th").text.should == ""
|
118
|
+
doc.at("fieldset table tr td").text.should == "yum yum"
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'puts in an extra cell if you pass in a note' do
|
122
|
+
table = FieldTable.new(:title => "Meals") do |t|
|
123
|
+
t.field("Breakfast", "the most important meal of the day") { t.text "eggs" }
|
124
|
+
t.field("Lunch") { t.text "hot dogs" }
|
125
|
+
end
|
126
|
+
doc = Nokogiri::HTML(table.to_html)
|
127
|
+
doc.at("fieldset table tr").css("td[3]").text.should == "the most important meal of the day"
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
|
+
|
3
|
+
describe Form do
|
4
|
+
|
5
|
+
include Erector::Mixin
|
6
|
+
|
7
|
+
it "defaults to POST, with no magic hidden method param" do
|
8
|
+
Form.new(:action => "/foo").to_html.should == "<form action=\"/foo\" method=\"post\"></form>"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "works plainly with GET too" do
|
12
|
+
Form.new(:action => "/foo", :method => "get").to_html.should == "<form action=\"/foo\" method=\"get\"></form>"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "uses POST and adds a magic hidden field with a _method param for DELETE" do
|
16
|
+
Form.new(:action => "/foo", :method => "delete").to_html.should ==
|
17
|
+
"<form action=\"/foo\" method=\"post\">"+
|
18
|
+
"<input name=\"_method\" type=\"hidden\" value=\"delete\" />"+
|
19
|
+
"</form>"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "executes its block in the caller's 'self' context" do
|
23
|
+
erector {
|
24
|
+
@pet = "dog"
|
25
|
+
widget(Form.new(:action => "/foo") do
|
26
|
+
p @pet
|
27
|
+
end)
|
28
|
+
}.should == "<form action=\"/foo\" method=\"post\"><p>dog</p></form>"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
|
+
|
3
|
+
describe Erector::Widgets::Page do
|
4
|
+
it "works" do
|
5
|
+
Erector::Widgets::Page.new.to_html
|
6
|
+
end
|
7
|
+
|
8
|
+
it "renders body_content" do
|
9
|
+
Class.new(Erector::Widgets::Page) do
|
10
|
+
def body_content
|
11
|
+
text "body_content"
|
12
|
+
end
|
13
|
+
end.new.to_html.should =~ /body_content/
|
14
|
+
end
|
15
|
+
|
16
|
+
it "renders a block passed to new" do
|
17
|
+
Erector::Widgets::Page.new do
|
18
|
+
text "nice bod"
|
19
|
+
end.to_html.should =~ /nice bod/
|
20
|
+
end
|
21
|
+
|
22
|
+
it "renders a block passed to content" do
|
23
|
+
Class.new(Erector::Widgets::Page) do
|
24
|
+
def content
|
25
|
+
super do
|
26
|
+
text "body_content"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end.new.to_html.should =~ /body_content/
|
30
|
+
end
|
31
|
+
|
32
|
+
it "allows subclasses to provide a css class for the body" do
|
33
|
+
Class.new(Erector::Widgets::Page) do
|
34
|
+
def body_attributes
|
35
|
+
{:class => "funky"}
|
36
|
+
end
|
37
|
+
end.new.to_html.should =~ /<body class=\"funky\">/
|
38
|
+
end
|
39
|
+
|
40
|
+
it "allows subclasses to be called with a block" do
|
41
|
+
fun_page_class = Class.new(Erector::Widgets::Page) do
|
42
|
+
def body_content
|
43
|
+
h3 "what's fun?"
|
44
|
+
call_block
|
45
|
+
end
|
46
|
+
end
|
47
|
+
fun_page_class.new do
|
48
|
+
text "soccer!"
|
49
|
+
end.to_html.should include("<h3>what's fun?</h3>soccer!")
|
50
|
+
end
|
51
|
+
|
52
|
+
class NiceWidget < Erector::Widget
|
53
|
+
external :style, ".nice {}"
|
54
|
+
def content
|
55
|
+
text "nice widget"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
class MeanWidget < Erector::Widget
|
59
|
+
external :style, ".mean {}"
|
60
|
+
end
|
61
|
+
|
62
|
+
class NicePage < Erector::Widgets::Page
|
63
|
+
def body_content
|
64
|
+
text "nice page"
|
65
|
+
widget NiceWidget
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class MeanPage < Erector::Widgets::Page
|
70
|
+
def body_content
|
71
|
+
widget MeanWidget
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
it "only puts into dependencies those from widgets rendered on it" do
|
76
|
+
s = NicePage.new.to_html
|
77
|
+
s.should include("nice page")
|
78
|
+
s.should include("nice widget")
|
79
|
+
s.should include(".nice {}")
|
80
|
+
s.should_not include(".mean {}")
|
81
|
+
|
82
|
+
MeanPage.new.to_html.should include(".mean {}")
|
83
|
+
MeanPage.new.to_html.should_not include(".nice {}")
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
|
2
|
+
|
3
|
+
module TableSpec
|
4
|
+
class DefaultsTestTable < Erector::Widgets::Table
|
5
|
+
column :first_name
|
6
|
+
column :last_name
|
7
|
+
column :email
|
8
|
+
row_classes :even, :odd
|
9
|
+
end
|
10
|
+
|
11
|
+
class CustomHeadingTable < Erector::Widgets::Table
|
12
|
+
column :first_name, "Column - First Name"
|
13
|
+
column :email, lambda {|id| span id}
|
14
|
+
end
|
15
|
+
|
16
|
+
class CustomCellTable < Erector::Widgets::Table
|
17
|
+
column :first_name do |obj|
|
18
|
+
span obj.first_name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe ::Erector::Widgets::Table do
|
23
|
+
describe "with custom heading" do
|
24
|
+
attr_reader :html, :doc
|
25
|
+
before do
|
26
|
+
widget = CustomHeadingTable.new(:row_objects => [])
|
27
|
+
@html = widget.to_html
|
28
|
+
@doc = Nokogiri::HTML(html)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "renders a custom heading text and procs" do
|
32
|
+
table = doc.at("table")
|
33
|
+
table.search("th").map {|c| c.inner_html}.should == [
|
34
|
+
"Column - First Name",
|
35
|
+
"<span>email</span>"
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "renders a tbody to be compatible with IE6" do
|
40
|
+
doc.at("tbody").should_not be_nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "with custom cell content" do
|
45
|
+
attr_reader :html, :doc
|
46
|
+
before do
|
47
|
+
@object1 = Struct.new(:first_name).new("Hello")
|
48
|
+
widget = CustomCellTable.new(:row_objects => [@object1])
|
49
|
+
@html = widget.to_html
|
50
|
+
@doc = Nokogiri::HTML(html)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "renders custom cell html" do
|
54
|
+
table = doc.at("table")
|
55
|
+
row = table.search("tr")[1]
|
56
|
+
row.at("td").inner_html.should == "<span>Hello</span>"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "with default heading and cell definitions" do
|
61
|
+
attr_reader :html, :doc
|
62
|
+
before do
|
63
|
+
@object1 = Struct.new(:first_name, :last_name, :email).new(1, 2, 3)
|
64
|
+
@object2 = Struct.new(:first_name, :last_name, :email).new(4, 5, 6)
|
65
|
+
@object3 = Struct.new(:first_name, :last_name, :email).new(7, 8, 9)
|
66
|
+
widget = DefaultsTestTable.new(:row_objects => [@object1, @object2, @object3])
|
67
|
+
@html = widget.to_html
|
68
|
+
@doc = Nokogiri::HTML(html)
|
69
|
+
@table = doc.at("table")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "renders column titles" do
|
73
|
+
title_row = @table.at("tr")
|
74
|
+
titles = title_row.search("th").collect {|heading| heading.inner_html}
|
75
|
+
titles.should == [ "First Name", "Last Name", "Email" ]
|
76
|
+
end
|
77
|
+
|
78
|
+
it "renders data" do
|
79
|
+
data_rows = @table.search("tr")[1..-1]
|
80
|
+
cell_values = data_rows.collect do |row|
|
81
|
+
row.search("td").collect {|col| col.inner_html}
|
82
|
+
end
|
83
|
+
|
84
|
+
cell_values.should == [
|
85
|
+
['1', '2', '3'],
|
86
|
+
['4', '5', '6'],
|
87
|
+
['7', '8', '9'],
|
88
|
+
]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "renders the row classes" do
|
92
|
+
data_rows = @table.search("tr")[1..-1]
|
93
|
+
data_rows[0]['class'].should == 'even'
|
94
|
+
data_rows[1]['class'].should == 'odd'
|
95
|
+
data_rows[2]['class'].should == 'even'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
|
2
|
+
|
3
|
+
VENDOR_RAILS = "#{File.dirname(__FILE__)}/rails_root/vendor/rails"
|
4
|
+
RAILS_LOAD_PATHS = Dir["#{VENDOR_RAILS}/*/lib"]
|
5
|
+
RAILS_LOAD_PATHS.each do |path|
|
6
|
+
$LOAD_PATH.unshift(File.expand_path(path))
|
7
|
+
end
|
8
|
+
|
9
|
+
require "rubygems"
|
10
|
+
require "bundler"
|
11
|
+
Bundler.setup
|
12
|
+
|
13
|
+
require "erector"
|
14
|
+
require "nokogiri"
|
15
|
+
require "rr"
|
16
|
+
require 'tempfile'
|
17
|
+
require 'ostruct'
|
18
|
+
require "spec"
|
19
|
+
require "spec/autorun"
|
20
|
+
|
21
|
+
Spec::Runner.configure do |config|
|
22
|
+
config.mock_with :rr
|
23
|
+
end
|
24
|
+
|
25
|
+
unless '1.9'.respond_to?(:force_encoding)
|
26
|
+
String.class_eval do
|
27
|
+
begin
|
28
|
+
remove_method :chars
|
29
|
+
rescue NameError
|
30
|
+
# OK
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Matchers
|
36
|
+
# borrowed from http://github.com/aiwilliams/spec_goodies
|
37
|
+
|
38
|
+
class IncludeOnly # :nodoc:all
|
39
|
+
def initialize(*expected)
|
40
|
+
@expected = expected.flatten
|
41
|
+
end
|
42
|
+
|
43
|
+
def matches?(actual)
|
44
|
+
@missing = @expected.reject {|e| actual.include?(e)}
|
45
|
+
@extra = actual.reject {|e| @expected.include?(e)}
|
46
|
+
@extra.empty? && @missing.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
def failure_message
|
50
|
+
message = "expected to include only #{@expected.inspect}"
|
51
|
+
message << "\nextra: #{@extra.inspect}" unless @extra.empty?
|
52
|
+
message << "\nmissing: #{@missing.inspect}" unless @missing.empty?
|
53
|
+
message
|
54
|
+
end
|
55
|
+
|
56
|
+
def negative_failure_message
|
57
|
+
"expected to include more than #{@expected.inspect}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
"include only #{@expected.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Unlike checking that two Enumerables are equal, where the
|
66
|
+
# objects in corresponding positions must be equal, this will
|
67
|
+
# allow you to ensure that an Enumerable has all the objects
|
68
|
+
# you expect, in any order; no more, no less.
|
69
|
+
def include_only(*expected)
|
70
|
+
IncludeOnly.new(*expected)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Spec::Runner.configure do |config|
|
75
|
+
include Matchers
|
76
|
+
end
|
77
|
+
|
78
|
+
def capturing_output
|
79
|
+
output = StringIO.new
|
80
|
+
$stdout = output
|
81
|
+
yield
|
82
|
+
output.string
|
83
|
+
ensure
|
84
|
+
$stdout = STDOUT
|
85
|
+
end
|
86
|
+
|
87
|
+
def capturing_stderr
|
88
|
+
output = StringIO.new
|
89
|
+
$stderr = output
|
90
|
+
yield
|
91
|
+
output.string
|
92
|
+
ensure
|
93
|
+
$stderr = STDERR
|
94
|
+
end
|
95
|
+
|