erector 0.2.42 → 0.2.61
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +41 -5
- data/bin/erect +31 -0
- data/lib/erector/erected.rb +63 -0
- data/lib/erector/helpers.rb +13 -0
- data/lib/erector/indenting.rb +36 -0
- data/lib/erector/rhtml.treetop +143 -0
- data/lib/erector/widget.rb +17 -5
- data/spec/convert/erected_spec.rb +83 -0
- data/spec/convert/rhtml_parser_spec.rb +304 -0
- metadata +40 -17
- data/spec/erect/erect_spec.rb +0 -7
data/README.txt
CHANGED
@@ -136,10 +136,10 @@ Here are the basics:
|
|
136
136
|
|
137
137
|
TODO: document more obscure features like capture, Table, :class => ['one', 'two']
|
138
138
|
|
139
|
-
=== Using
|
139
|
+
=== Using Erector from Ruby on Rails
|
140
140
|
|
141
|
-
Your views are just ruby classes. Your controller instantiates the
|
142
|
-
|
141
|
+
Your views are just ruby classes. Your controller instantiates the relevant view and calls render.
|
142
|
+
For example:
|
143
143
|
|
144
144
|
app/controllers/welcome_controller.rb:
|
145
145
|
|
@@ -169,6 +169,40 @@ app/views/welcome/show.rb:
|
|
169
169
|
|
170
170
|
end
|
171
171
|
|
172
|
+
For Rails to find these .rb files during render, you must first either copy the erector source to
|
173
|
+
vendor/plugins/erector, or add `require 'erector'` to config/environment.rb. You also should delete (or rename)
|
174
|
+
any other view files with the same base name that might be getting in the way.
|
175
|
+
|
176
|
+
=== Erect
|
177
|
+
|
178
|
+
To make Rails integration as smooth as possible, we've written a little tool that will help you
|
179
|
+
erect your existing Rails app. The "erect" tool will convert HTML or HTML/ERB into an Erector class.
|
180
|
+
It ships as part of the Erector gem, so to try it out, install the gem, then run
|
181
|
+
|
182
|
+
erect app/views/foos/*.html.erb
|
183
|
+
|
184
|
+
or just
|
185
|
+
|
186
|
+
erect app/views
|
187
|
+
|
188
|
+
and then delete the original files when you're satisfied.
|
189
|
+
|
190
|
+
Here's a little command-line howto for erecting a scaffold Rails app:
|
191
|
+
|
192
|
+
rails foo
|
193
|
+
cd foo
|
194
|
+
script/generate scaffold post title:string body:text published:boolean
|
195
|
+
|
196
|
+
erect app/views/posts/*.erb
|
197
|
+
|
198
|
+
mate app/views/posts
|
199
|
+
sleep 30 # this should be enough time for you to stop drooling
|
200
|
+
rm app/views/posts/*.erb
|
201
|
+
(echo ""; echo "require 'erector'") >> config/environment.rb
|
202
|
+
rake db:migrate
|
203
|
+
script/server
|
204
|
+
open http://localhost:3000/posts
|
205
|
+
|
172
206
|
=== Layout Inheritance
|
173
207
|
|
174
208
|
Erector replaces the typical Rails layout mechanism with a more natural construct, the use of inheritance. Want a common
|
@@ -211,6 +245,8 @@ method, you can pass a block to Erector::Widget.new. For example:
|
|
211
245
|
end
|
212
246
|
html.to_s #=> <p>Hello, world!</p>
|
213
247
|
|
248
|
+
This lets you define mini-widgets on the fly.
|
249
|
+
|
214
250
|
== DEVELOPER NOTES
|
215
251
|
|
216
252
|
* Check out project from rubyforge:
|
@@ -219,7 +255,7 @@ method, you can pass a block to Erector::Widget.new. For example:
|
|
219
255
|
|
220
256
|
* Install gems:
|
221
257
|
|
222
|
-
sudo gem install rake rails rspec rubyforge hpricot
|
258
|
+
sudo gem install rake rails rspec rubyforge hpricot treetop
|
223
259
|
|
224
260
|
* Run specs:
|
225
261
|
|
@@ -241,6 +277,6 @@ method, you can pass a block to Erector::Widget.new. For example:
|
|
241
277
|
* We will not be shy about incrementing version numbers -- if we end up going to version 0.943.67454 then so be it.
|
242
278
|
* Developers should attempt to add lines in History.txt to reflect their checkins. These should reflect feature-level changes, not just one line per checkin. The top section of History.txt is used as the Release Notes by the "rake publish" task and will appear on the RubyForge file page.
|
243
279
|
* Someone making a release must fill in the version number in History.txt as well as in Rakefile. Note that "rake release" requires a "VERSION=1.2.3" parameter to confirm you're releasing the version you intend.
|
244
|
-
* As soon as a release is made and published, the publisher should go into History.txt and make a new section. Since we won't yet know what the next version will be called, the new section will be noted by a single "
|
280
|
+
* As soon as a release is made and published, the publisher should go into History.txt and make a new section. Since we won't yet know what the next version will be called, the new section will be noted by a single "===" at the top of the file.
|
245
281
|
|
246
282
|
|
data/bin/erect
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
dir = File.expand_path(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift("#{dir}/../lib")
|
4
|
+
require "erector"
|
5
|
+
require "erector/erected" # pull this out so we don't recreate the grammar every time
|
6
|
+
require "rake"
|
7
|
+
|
8
|
+
# todo:
|
9
|
+
# --add_to_svn
|
10
|
+
# --delete_original
|
11
|
+
|
12
|
+
files = FileList.new
|
13
|
+
ARGV.each do |file|
|
14
|
+
if File.directory?(file)
|
15
|
+
files.add(FileList["#{file}/**/*.rhtml", "#{file}/**/*.html", "#{file}/**/*.html.erb"])
|
16
|
+
else
|
17
|
+
files.add(file)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
files.each do |file|
|
22
|
+
print "Erecting #{file}... "
|
23
|
+
begin
|
24
|
+
e = Erector::Erected.new(file)
|
25
|
+
e.convert
|
26
|
+
puts " --> #{e.filename}"
|
27
|
+
rescue => e
|
28
|
+
puts e
|
29
|
+
puts
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'treetop'
|
3
|
+
dir = File.dirname(__FILE__)
|
4
|
+
require "#{dir}/indenting"
|
5
|
+
Treetop.load "#{dir}/../../lib/erector/rhtml"
|
6
|
+
|
7
|
+
module Erector
|
8
|
+
class Erected
|
9
|
+
def initialize(in_file)
|
10
|
+
@in_file = in_file
|
11
|
+
end
|
12
|
+
|
13
|
+
def filename
|
14
|
+
dir + basename + ".rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
def classname
|
18
|
+
base = classize(basename)
|
19
|
+
parent = File.dirname(@in_file)
|
20
|
+
grandparent = File.dirname(parent)
|
21
|
+
if File.basename(grandparent) == "views"
|
22
|
+
base = "Views::" + classize(File.basename(parent)) + "::" + base
|
23
|
+
end
|
24
|
+
base
|
25
|
+
end
|
26
|
+
|
27
|
+
def text
|
28
|
+
File.read(@in_file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert
|
32
|
+
parser = RhtmlParser.new
|
33
|
+
parsed = parser.parse(File.read(@in_file))
|
34
|
+
if parsed.nil?
|
35
|
+
raise "Could not parse #{@in_file}\n" +
|
36
|
+
parser.failure_reason
|
37
|
+
else
|
38
|
+
File.open(filename, "w") do |f|
|
39
|
+
f.puts("class #{classname} < Erector::Widget")
|
40
|
+
f.puts(" def render")
|
41
|
+
f.puts(parsed.set_indent(2).convert)
|
42
|
+
f.puts(" end")
|
43
|
+
f.puts("end")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def basename
|
51
|
+
@in_file.split("/").last.gsub(/\..*$/, '')
|
52
|
+
end
|
53
|
+
|
54
|
+
def dir
|
55
|
+
x = File.dirname(@in_file)
|
56
|
+
return (x == ".") ? "" : "#{x}/"
|
57
|
+
end
|
58
|
+
|
59
|
+
def classize(filename)
|
60
|
+
filename.split("_").map{|part| part.capitalize}.join
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/erector/helpers.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Erector
|
2
2
|
module Helpers
|
3
|
+
|
3
4
|
[
|
4
5
|
:image_tag,
|
5
6
|
:javascript_include_tag,
|
@@ -23,6 +24,18 @@ module Erector
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
def error_messages_for(*args)
|
28
|
+
fake_erbout do
|
29
|
+
helpers.error_messages_for(*args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def form_for(*args, &block)
|
34
|
+
fake_erbout do
|
35
|
+
helpers.form_for(*args, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
26
39
|
def javascript_include_merged(key)
|
27
40
|
helpers.javascript_include_merged(key)
|
28
41
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'treetop'
|
3
|
+
|
4
|
+
module Erector
|
5
|
+
class Indenting < Treetop::Runtime::SyntaxNode
|
6
|
+
@@indent = 0
|
7
|
+
|
8
|
+
def set_indent(x)
|
9
|
+
@@indent = x
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
def indent
|
14
|
+
[0, @@indent].max
|
15
|
+
end
|
16
|
+
|
17
|
+
def indented(s)
|
18
|
+
" " * indent + s + "\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
def line(s)
|
22
|
+
indented(s)
|
23
|
+
end
|
24
|
+
|
25
|
+
def line_in(s)
|
26
|
+
s = indented(s)
|
27
|
+
@@indent += 1
|
28
|
+
s
|
29
|
+
end
|
30
|
+
|
31
|
+
def line_out(s)
|
32
|
+
@@indent -= 1
|
33
|
+
indented(s)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
grammar Rhtml
|
2
|
+
|
3
|
+
rule doc
|
4
|
+
space node space x:doc? <Erector::Indenting> {
|
5
|
+
def convert
|
6
|
+
if x.empty?
|
7
|
+
node.convert
|
8
|
+
else
|
9
|
+
node.convert + x.convert
|
10
|
+
end
|
11
|
+
end
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
rule node
|
16
|
+
hprintlet / printlet / scriptlet / doctype / self_closing_tag / closetag / opentag / text
|
17
|
+
end
|
18
|
+
|
19
|
+
rule scriptlet
|
20
|
+
'<%' space code space '%>' <Erector::Indenting> {
|
21
|
+
def convert
|
22
|
+
text = code.text_value.strip
|
23
|
+
if text =~ /\bdo( |.*|)?$/
|
24
|
+
line_in text
|
25
|
+
elsif text == "end"
|
26
|
+
line_out text
|
27
|
+
else
|
28
|
+
line text
|
29
|
+
end
|
30
|
+
end
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
rule printlet
|
35
|
+
'<%=' space code space '%>' <Erector::Indenting> {
|
36
|
+
def convert
|
37
|
+
line "rawtext #{code.convert}"
|
38
|
+
end
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
rule hprintlet
|
43
|
+
'<%=' space 'h' ' '+ code space '%>' <Erector::Indenting> {
|
44
|
+
def convert
|
45
|
+
line "text #{code.convert}"
|
46
|
+
end
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
rule code
|
51
|
+
(('%' !'>') / [^%])* <Erector::Indenting> {
|
52
|
+
def convert
|
53
|
+
code = text_value.strip
|
54
|
+
# matches a word, followed by either a word, a string, or a symbol
|
55
|
+
code.gsub(/(\w+) ([\w:"'].*)$/, '\1(\2)')
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
rule doctype
|
61
|
+
'<!DOCTYPE' [^>]* '>' <Erector::Indenting> {
|
62
|
+
def convert
|
63
|
+
line "rawtext '#{text_value}'"
|
64
|
+
end
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
rule tagname
|
69
|
+
[A-Za-z0-9_:-]+
|
70
|
+
end
|
71
|
+
|
72
|
+
rule self_closing_tag
|
73
|
+
'<' tag_name:tagname attrs:attributes? space '/>' <Erector::Indenting> {
|
74
|
+
def convert
|
75
|
+
line "#{tag_name.text_value}#{attrs.blank? ? "" : attrs.convert}"
|
76
|
+
end
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
rule opentag
|
81
|
+
'<' tag_name:tagname attrs:attributes? space '>' <Erector::Indenting> {
|
82
|
+
def convert
|
83
|
+
line_in "#{tag_name.text_value}#{attrs.blank? ? "" : attrs.convert} do"
|
84
|
+
end
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
rule closetag
|
89
|
+
'</' tag_name:tagname '>' <Erector::Indenting> {
|
90
|
+
def convert
|
91
|
+
line_out "end"
|
92
|
+
end
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
rule text
|
97
|
+
(([<>] !(tagname / [/%!])) / [^<>])+ <Erector::Indenting> {
|
98
|
+
def convert
|
99
|
+
stripped = text_value.strip
|
100
|
+
if stripped.blank?
|
101
|
+
""
|
102
|
+
else
|
103
|
+
line "text '#{text_value.strip.html_unescape}'"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
rule attributes
|
110
|
+
first:attribute rest:attributes* {
|
111
|
+
def convert
|
112
|
+
" " + first.convert +
|
113
|
+
if rest.blank?
|
114
|
+
""
|
115
|
+
else
|
116
|
+
",#{rest.elements.first.convert}" # this is hacky -- is there a better way?
|
117
|
+
end
|
118
|
+
end
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
rule attribute
|
123
|
+
space n:(tagname) space '=' space v:quoted space {
|
124
|
+
def convert
|
125
|
+
attr_name = (n.text_value =~ /[-:]/) ? "'#{n.text_value}'" : ":#{n.text_value}"
|
126
|
+
"#{attr_name} => '#{v.value.html_unescape.escape_single_quotes}'"
|
127
|
+
end
|
128
|
+
}
|
129
|
+
end
|
130
|
+
|
131
|
+
rule quoted
|
132
|
+
(('"' val:([^"]*) '"') / ('\'' val:([^']*) '\'')) {
|
133
|
+
def value
|
134
|
+
val.text_value
|
135
|
+
end
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
rule space
|
140
|
+
' '*
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
data/lib/erector/widget.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
1
3
|
module Erector
|
2
4
|
class Widget
|
3
5
|
class << self
|
@@ -22,7 +24,8 @@ module Erector
|
|
22
24
|
end
|
23
25
|
end
|
24
26
|
|
25
|
-
include ActionController::UrlWriter
|
27
|
+
include ActionController::UrlWriter
|
28
|
+
include Helpers
|
26
29
|
attr_reader :helpers
|
27
30
|
attr_reader :assigns
|
28
31
|
attr_reader :doc
|
@@ -190,8 +193,8 @@ module Erector
|
|
190
193
|
@__to_s = @doc.to_s
|
191
194
|
end
|
192
195
|
|
193
|
-
def html_escape
|
194
|
-
return to_s
|
196
|
+
def html_escape
|
197
|
+
return to_s
|
195
198
|
end
|
196
199
|
|
197
200
|
alias_method :inspect, :to_s
|
@@ -232,7 +235,7 @@ module Erector
|
|
232
235
|
raise "Cannot nest fake_erbout" if instance_methods.include?('concat_without_erector')
|
233
236
|
alias_method :concat_without_erector, :concat
|
234
237
|
define_method :concat do |some_text, binding|
|
235
|
-
widget.
|
238
|
+
widget.rawtext(some_text)
|
236
239
|
end
|
237
240
|
end
|
238
241
|
yield
|
@@ -253,6 +256,15 @@ end
|
|
253
256
|
|
254
257
|
class Object
|
255
258
|
def html_escape
|
256
|
-
return CGI.escapeHTML(to_s
|
259
|
+
return CGI.escapeHTML(to_s)
|
260
|
+
end
|
261
|
+
|
262
|
+
def html_unescape
|
263
|
+
CGI.unescapeHTML(to_s)
|
264
|
+
end
|
265
|
+
|
266
|
+
# OMGWTF
|
267
|
+
def escape_single_quotes
|
268
|
+
self.gsub(/[']/, '\\\\\'')
|
257
269
|
end
|
258
270
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require "#{dir}/../spec_helper"
|
3
|
+
require "#{dir}/../../lib/erector/erected"
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
module Erector
|
7
|
+
describe Erected do
|
8
|
+
|
9
|
+
it "picks the right file name" do
|
10
|
+
Erected.new("foo.html.erb").filename.should == "foo.rb"
|
11
|
+
Erected.new("foo.html").filename.should == "foo.rb"
|
12
|
+
Erected.new("foo.bar.html").filename.should == "foo.rb"
|
13
|
+
Erected.new("foo_bar.html.erb").filename.should == "foo_bar.rb"
|
14
|
+
Erected.new("stuff/foo_bar.html.erb").filename.should == "stuff/foo_bar.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "picks a nice class name" do
|
18
|
+
Erected.new("foo.html.erb").classname.should == "Foo"
|
19
|
+
Erected.new("foo.html").classname.should == "Foo"
|
20
|
+
Erected.new("foo.bar.html").classname.should == "Foo"
|
21
|
+
Erected.new("foo_bar.html.erb").classname.should == "FooBar"
|
22
|
+
Erected.new("stuff/foo_bar.html.erb").classname.should == "FooBar"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "picks an even nicer class name if it's in a views dir" do
|
26
|
+
Erected.new("app/views/stuff/foo_bar.html.erb").classname.should == "Views::Stuff::FooBar"
|
27
|
+
Erected.new("views/stuff/foo_bar.html.erb").classname.should == "Views::Stuff::FooBar"
|
28
|
+
end
|
29
|
+
|
30
|
+
def convert(dir, input, output)
|
31
|
+
dir = Dir.tmpdir + "/#{Time.now.to_i}" + "/#{dir}"
|
32
|
+
|
33
|
+
FileUtils.mkdir_p(dir)
|
34
|
+
html = "#{dir}/dummy.html"
|
35
|
+
rb = "#{dir}/dummy.rb"
|
36
|
+
|
37
|
+
File.open(html, "w") do |f|
|
38
|
+
f.puts(input)
|
39
|
+
end
|
40
|
+
|
41
|
+
@e = Erected.new(html)
|
42
|
+
@e.convert
|
43
|
+
|
44
|
+
File.read(rb).should == output
|
45
|
+
end
|
46
|
+
|
47
|
+
it "converts a normal file" do
|
48
|
+
convert(".",
|
49
|
+
"<div>hello</div>",
|
50
|
+
"class Dummy < Erector::Widget\n" +
|
51
|
+
" def render\n" +
|
52
|
+
" div do\n" +
|
53
|
+
" text 'hello'\n" +
|
54
|
+
" end\n" +
|
55
|
+
" end\n" +
|
56
|
+
"end\n"
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "converts a views file" do
|
61
|
+
convert("app/views/foos",
|
62
|
+
"<div>hello</div>",
|
63
|
+
"class Views::Foos::Dummy < Erector::Widget\n" +
|
64
|
+
" def render\n" +
|
65
|
+
" div do\n" +
|
66
|
+
" text 'hello'\n" +
|
67
|
+
" end\n" +
|
68
|
+
" end\n" +
|
69
|
+
"end\n"
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
# todo: figure out if there is any such thing as unparsable HTML anymore
|
74
|
+
# it "raises an exception if given unparsable HTML" do
|
75
|
+
# begin
|
76
|
+
# convert(".", "<", "")
|
77
|
+
# rescue => e
|
78
|
+
# e.to_s.should include("Could not parse")
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
dir = File.dirname(__FILE__)
|
2
|
+
require "#{dir}/../spec_helper"
|
3
|
+
|
4
|
+
# require 'test/unit'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'treetop'
|
7
|
+
require "erector/erected" # pull this out so we don't recreate the grammar every time
|
8
|
+
|
9
|
+
module ParserTestHelper
|
10
|
+
def assert_evals_to_self(input)
|
11
|
+
assert_evals_to(input, input)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse(input)
|
15
|
+
result = @parser.parse(input)
|
16
|
+
if result
|
17
|
+
result.set_indent(0) if result.respond_to? :set_indent
|
18
|
+
else
|
19
|
+
puts @parser.failure_reason
|
20
|
+
puts @parser.terminal_failures.join("\n")
|
21
|
+
result.should_not be_nil
|
22
|
+
end
|
23
|
+
result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe RhtmlParser do
|
28
|
+
include ParserTestHelper
|
29
|
+
|
30
|
+
before :each do
|
31
|
+
@parser = RhtmlParser.new
|
32
|
+
end
|
33
|
+
|
34
|
+
it "converts text" do
|
35
|
+
parse("hello").convert.should == "text 'hello'\n"
|
36
|
+
parse("hello maude!").convert.should == "text 'hello maude!'\n"
|
37
|
+
parse(" hello ").convert.should == "text 'hello'\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "unescapes HTML entities in text" do
|
41
|
+
parse("<").convert.should == "text '<'\n"
|
42
|
+
parse("5 > 2").convert.should == "text '5 > 2'\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "converts self-closing tags" do
|
46
|
+
parse("<br/>").convert.should == "br\n"
|
47
|
+
parse("<br />").convert.should == "br\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "converts open tag" do
|
51
|
+
parse("<div>").convert.should == "div do\n"
|
52
|
+
parse("<h1>").convert.should == "h1 do\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "converts close tag" do
|
56
|
+
parse("</div>").convert.should == "end\n"
|
57
|
+
parse("</h1>").convert.should == "end\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "converts two nested divs" do
|
61
|
+
parse("<div><div></div></div>").convert.should ==
|
62
|
+
"div do\n" +
|
63
|
+
" div do\n" +
|
64
|
+
" end\n" +
|
65
|
+
"end\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "converts two nested divs with whitespace" do
|
69
|
+
parse("<div> <div> </div> </div>").convert.should ==
|
70
|
+
"div do\n" +
|
71
|
+
" div do\n" +
|
72
|
+
" end\n" +
|
73
|
+
"end\n"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "converts no open, text, and no close tag" do
|
77
|
+
parse("hello</div>").convert.should == "text 'hello'\nend\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "converts open, text, and no close tag" do
|
81
|
+
parse("<div>hello").convert.should == "div do\n text 'hello'\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
it "converts open, text, close" do
|
85
|
+
parse("<div>hello</div>").convert.should == "div do\n text 'hello'\nend\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "converts a scriptlet" do
|
89
|
+
parse("<% foo %>").convert.should == "foo\n"
|
90
|
+
end
|
91
|
+
|
92
|
+
it "converts open, text, scriptlet, text, close" do
|
93
|
+
parse("<div>hello <% 5.times do %> very <% end %> much</div>").convert.should ==
|
94
|
+
"div do\n" +
|
95
|
+
" text 'hello'\n" +
|
96
|
+
" 5.times do\n" +
|
97
|
+
" text 'very'\n" +
|
98
|
+
" end\n" +
|
99
|
+
" text 'much'\n" +
|
100
|
+
"end\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
it "converts open, scriptlet, text, close" do
|
104
|
+
parse("<div><% 5.times do %> very <% end %> much</div>").convert.should ==
|
105
|
+
"div do\n" +
|
106
|
+
" 5.times do\n" +
|
107
|
+
" text 'very'\n" +
|
108
|
+
" end\n" +
|
109
|
+
" text 'much'\n" +
|
110
|
+
"end\n"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "converts open, text, scriptlet, close" do
|
114
|
+
parse("<div>hello <% 5.times do %> very <% end %></div>").convert.should ==
|
115
|
+
"div do\n" +
|
116
|
+
" text 'hello'\n" +
|
117
|
+
" 5.times do\n" +
|
118
|
+
" text 'very'\n" +
|
119
|
+
" end\n" +
|
120
|
+
"end\n"
|
121
|
+
end
|
122
|
+
|
123
|
+
it "converts printlets into rawtext statements" do
|
124
|
+
parse("<%= 1+1 %>").convert.should == "rawtext 1+1\n"
|
125
|
+
parse("<%= link_to \"mom\" %>").convert.should == "rawtext link_to(\"mom\")\n"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "converts h-printlets into text statements" do
|
129
|
+
parse("<%=h foo %>").convert.should == "text foo\n"
|
130
|
+
parse("<%= h \"mom\" %>").convert.should == "text \"mom\"\n"
|
131
|
+
end
|
132
|
+
|
133
|
+
it "allows naked percent signs inside scriptlets" do
|
134
|
+
parse("<% x = 10 % 5 %>").convert.should == "x = 10 % 5\n"
|
135
|
+
end
|
136
|
+
|
137
|
+
it "indents" do
|
138
|
+
i = Erector::Indenting.new(nil, nil)
|
139
|
+
i.line("foo").should == "foo\n"
|
140
|
+
i.line_in("bar").should == "bar\n"
|
141
|
+
i.line_in("baz").should == " baz\n"
|
142
|
+
i.line("baf").should == " baf\n"
|
143
|
+
i.line_out("end").should == " end\n"
|
144
|
+
i.line_out("end").should == "end\n"
|
145
|
+
end
|
146
|
+
|
147
|
+
it "indents extra when told to" do
|
148
|
+
parse("<div>hello</div>").set_indent(2).convert.should ==
|
149
|
+
" div do\n" +
|
150
|
+
" text 'hello'\n" +
|
151
|
+
" end\n"
|
152
|
+
end
|
153
|
+
|
154
|
+
it "indents scriptlets ending with do and end" do
|
155
|
+
parse("<% form_for :foo do |x,y| %><% 5.times do %>hello<% end %><% end %>bye").convert.should ==
|
156
|
+
"form_for :foo do |x,y|\n" +
|
157
|
+
" 5.times do\n" +
|
158
|
+
" text 'hello'\n" +
|
159
|
+
" end\n" +
|
160
|
+
"end\n" +
|
161
|
+
"text 'bye'\n"
|
162
|
+
end
|
163
|
+
|
164
|
+
it "converts HTML attributes" do
|
165
|
+
parse("<div id='foo'/>").convert.should == "div :id => 'foo'\n"
|
166
|
+
parse("<div id='foo' class='bar'/>").convert.should == "div :id => 'foo', :class => 'bar'\n"
|
167
|
+
parse("<div id='foo'>bar</div>").convert.should == "div :id => 'foo' do\n text 'bar'\nend\n"
|
168
|
+
end
|
169
|
+
|
170
|
+
it "escapes single quotes inside attribute values" do
|
171
|
+
@parser.root = :attribute
|
172
|
+
parse("a=\"don't worry\"").convert.should == ":a => 'don\\'t worry'"
|
173
|
+
end
|
174
|
+
|
175
|
+
it "deals with HTML entities in text" do
|
176
|
+
parse("<").convert.should == "text '<'\n"
|
177
|
+
end
|
178
|
+
|
179
|
+
it "deals with a naked less-than or greater-than sign inside text" do
|
180
|
+
parse("if x > 2 or x< 5 then").convert.should == "text 'if x > 2 or x< 5 then'\n"
|
181
|
+
end
|
182
|
+
|
183
|
+
it "wraps printlets in parens if necessary, to avoid warning: parenthesize argument(s) for future version" do
|
184
|
+
parse("<%= h \"mom\" %>").convert.should == "text \"mom\"\n"
|
185
|
+
parse("<%= h hi \"mom\" %>").convert.should == "text hi(\"mom\")\n"
|
186
|
+
|
187
|
+
parse("<%= \"mom\" %>").convert.should == "rawtext \"mom\"\n"
|
188
|
+
parse("<%= hi \"mom\" %>").convert.should == "rawtext hi(\"mom\")\n"
|
189
|
+
|
190
|
+
parse("<%= link_to blah %>").convert.should == "rawtext link_to(blah)\n"
|
191
|
+
parse("<%= link_to blah blah %>").convert.should == "rawtext link_to(blah blah)\n"
|
192
|
+
parse("<%= link_to blah(blah) %>").convert.should == "rawtext link_to(blah(blah))\n"
|
193
|
+
|
194
|
+
parse("<%= link_to(blah) %>").convert.should == "rawtext link_to(blah)\n"
|
195
|
+
end
|
196
|
+
|
197
|
+
it "won't parenthesize expressions" do
|
198
|
+
parse("<%= h foo / bar %>").convert.should == "text foo / bar\n"
|
199
|
+
end
|
200
|
+
|
201
|
+
it "parses quoted strings" do
|
202
|
+
@parser.root = :quoted
|
203
|
+
parse("'foo'").value.should == "foo"
|
204
|
+
parse("\"foo\"").value.should == "foo"
|
205
|
+
end
|
206
|
+
|
207
|
+
it "converts attributes in isolation" do
|
208
|
+
@parser.root = :attribute
|
209
|
+
parse("a='foo'").convert.should == ":a => 'foo'"
|
210
|
+
parse("a=\"foo\"").convert.should == ":a => 'foo'"
|
211
|
+
end
|
212
|
+
|
213
|
+
it "parses a set of attributes" do
|
214
|
+
@parser.root = :attributes
|
215
|
+
parse("a='foo' b='bar'").convert.should == " :a => 'foo', :b => 'bar'"
|
216
|
+
end
|
217
|
+
|
218
|
+
it "works with namespaced attributes" do
|
219
|
+
@parser.root = :attribute
|
220
|
+
parse('xml:lang="en"').convert.should == "'xml:lang' => 'en'"
|
221
|
+
end
|
222
|
+
|
223
|
+
it "deals with HTML entities in attribute values" do
|
224
|
+
@parser.root = :attribute
|
225
|
+
parse("foo='b<r'").convert.should == ":foo => 'b<r'"
|
226
|
+
parse("foo='b<r'").convert.should == ":foo => 'b<r'"
|
227
|
+
end
|
228
|
+
|
229
|
+
it "converts DOCTYPEs" do
|
230
|
+
html = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
231
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
|
232
|
+
parse(html).convert.should == "rawtext '#{html}'\n"
|
233
|
+
end
|
234
|
+
|
235
|
+
## More functional-type specs below here
|
236
|
+
|
237
|
+
it "ignores spaces, tabs and newlines" do
|
238
|
+
parse(" <div>\t\n" + "\thello !" + "\n\t</div>").convert.should ==
|
239
|
+
"div do\n" +
|
240
|
+
" text 'hello !'\n" +
|
241
|
+
"end\n"
|
242
|
+
end
|
243
|
+
|
244
|
+
it "parses some scaffolding" do
|
245
|
+
parse("""<p>
|
246
|
+
<b>Name:</b>
|
247
|
+
<%=h @foo.name %>
|
248
|
+
</p>""").convert.should ==
|
249
|
+
"p do\n" +
|
250
|
+
" b do\n" +
|
251
|
+
" text 'Name:'\n" +
|
252
|
+
" end\n" +
|
253
|
+
" text @foo.name\n" +
|
254
|
+
"end\n"
|
255
|
+
end
|
256
|
+
|
257
|
+
it "parses edit.erb.html" do
|
258
|
+
parse("""<h1>Editing foo</h1>
|
259
|
+
|
260
|
+
<%= error_messages_for :foo %>
|
261
|
+
|
262
|
+
<% form_for(@foo) do |f| %>
|
263
|
+
<p>
|
264
|
+
<b>Name</b><br />
|
265
|
+
<%= f.text_field :name %>
|
266
|
+
</p>
|
267
|
+
|
268
|
+
<p>
|
269
|
+
<b>Age</b><br />
|
270
|
+
<%= f.text_field :age %>
|
271
|
+
</p>
|
272
|
+
|
273
|
+
<p>
|
274
|
+
<%= f.submit \"Update\" %>
|
275
|
+
</p>
|
276
|
+
<% end %>
|
277
|
+
|
278
|
+
<%= link_to 'Show', @foo %> |
|
279
|
+
<%= link_to 'Back', foos_path %>
|
280
|
+
""")
|
281
|
+
end
|
282
|
+
|
283
|
+
it "parses show.html.erb" do
|
284
|
+
parse("""<p>
|
285
|
+
<b>Name:</b>
|
286
|
+
<%=h @foo.name %>
|
287
|
+
</p>
|
288
|
+
|
289
|
+
<p>
|
290
|
+
<b>Age:</b>
|
291
|
+
<%=h @foo.age %>
|
292
|
+
</p>
|
293
|
+
|
294
|
+
|
295
|
+
<%= link_to 'Edit', edit_foo_path(@foo) %> |
|
296
|
+
<%= link_to 'Back', foos_path %>
|
297
|
+
""")
|
298
|
+
end
|
299
|
+
|
300
|
+
it "does meta" do
|
301
|
+
parse('<meta http-equiv="content-type" content="text/html;charset=UTF-8" />').convert.should ==
|
302
|
+
"meta 'http-equiv' => 'content-type', :content => 'text/html;charset=UTF-8'\n"
|
303
|
+
end
|
304
|
+
end
|
metadata
CHANGED
@@ -1,17 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
5
|
-
platform:
|
4
|
+
version: 0.2.61
|
5
|
+
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pivotal Labs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-04-
|
12
|
+
date: 2008-04-18 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: treetop
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.2.3
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: rake
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
15
33
|
- !ruby/object:Gem::Dependency
|
16
34
|
name: hoe
|
17
35
|
version_requirement:
|
@@ -24,37 +42,42 @@ dependencies:
|
|
24
42
|
description: Erector is a Builder-like view framework, inspired by Markaby but overcoming some of its flaws. In Erector all views are objects, not template files, which allows the full power of object-oriented programming (inheritance, modular decomposition, encapsulation) in views.
|
25
43
|
email:
|
26
44
|
- alex@pivotallabs.com
|
27
|
-
executables:
|
28
|
-
|
45
|
+
executables:
|
46
|
+
- erect
|
29
47
|
extensions: []
|
30
48
|
|
31
49
|
extra_rdoc_files:
|
32
50
|
- README.txt
|
33
51
|
files:
|
34
|
-
- spec/
|
52
|
+
- spec/convert
|
53
|
+
- spec/convert/erected_spec.rb
|
54
|
+
- spec/convert/rhtml_parser_spec.rb
|
35
55
|
- spec/erector
|
36
|
-
- spec/spec_helper.rb
|
37
|
-
- spec/spec_suite.rb
|
38
|
-
- spec/view_caching.rb
|
39
|
-
- spec/erect/erect_spec.rb
|
40
56
|
- spec/erector/extensions
|
57
|
+
- spec/erector/extensions/render_widget_spec.rb
|
41
58
|
- spec/erector/widget_spec.rb
|
42
59
|
- spec/erector/widgets
|
43
|
-
- spec/erector/extensions/render_widget_spec.rb
|
44
60
|
- spec/erector/widgets/table_spec.rb
|
61
|
+
- spec/spec_helper.rb
|
62
|
+
- spec/spec_suite.rb
|
63
|
+
- spec/view_caching.rb
|
45
64
|
- lib/erector
|
46
|
-
- lib/erector.rb
|
65
|
+
- lib/erector/erected.rb
|
47
66
|
- lib/erector/extensions
|
67
|
+
- lib/erector/extensions/action_controller.rb
|
68
|
+
- lib/erector/extensions/action_view_template_handler.rb
|
69
|
+
- lib/erector/extensions/object.rb
|
48
70
|
- lib/erector/helpers.rb
|
49
71
|
- lib/erector/html_parts.rb
|
72
|
+
- lib/erector/indenting.rb
|
73
|
+
- lib/erector/rhtml.treetop
|
50
74
|
- lib/erector/widget.rb
|
51
75
|
- lib/erector/widgets
|
52
|
-
- lib/erector/widgets.rb
|
53
|
-
- lib/erector/extensions/action_controller.rb
|
54
|
-
- lib/erector/extensions/action_view_template_handler.rb
|
55
|
-
- lib/erector/extensions/object.rb
|
56
76
|
- lib/erector/widgets/table.rb
|
77
|
+
- lib/erector/widgets.rb
|
78
|
+
- lib/erector.rb
|
57
79
|
- README.txt
|
80
|
+
- bin/erect
|
58
81
|
has_rdoc: true
|
59
82
|
homepage: http://erector.rubyforge.org
|
60
83
|
post_install_message:
|
@@ -78,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
78
101
|
requirements: []
|
79
102
|
|
80
103
|
rubyforge_project: erector
|
81
|
-
rubygems_version: 0.
|
104
|
+
rubygems_version: 1.0.1
|
82
105
|
signing_key:
|
83
106
|
specification_version: 2
|
84
107
|
summary: Erector is a Builder-like view framework, inspired by Markaby but overcoming some of its flaws
|