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/lib/erector/widgets.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require "
|
2
|
-
require "
|
3
|
-
require "
|
4
|
-
require "
|
1
|
+
require "erector/widgets/environment_badge"
|
2
|
+
require "erector/widgets/field_table"
|
3
|
+
require "erector/widgets/form"
|
4
|
+
require "erector/widgets/page"
|
5
|
+
require "erector/widgets/table"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# todo: make more like http://github.com/justinfrench/formtastic
|
2
|
+
|
3
|
+
class Form < Erector::Widget
|
4
|
+
needs :action, :method => "post"
|
5
|
+
|
6
|
+
def content
|
7
|
+
form :method => form_method, :action => @action do
|
8
|
+
unless rest_method == form_method
|
9
|
+
input :type => "hidden", :name => "_method", :value => rest_method
|
10
|
+
end
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method
|
16
|
+
@method.to_s.downcase
|
17
|
+
end
|
18
|
+
|
19
|
+
def form_method
|
20
|
+
if method == "get"
|
21
|
+
"get"
|
22
|
+
else
|
23
|
+
"post"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rest_method
|
28
|
+
method
|
29
|
+
end
|
30
|
+
end
|
data/lib/erector/widgets/page.rb
CHANGED
@@ -5,18 +5,55 @@
|
|
5
5
|
# a js script in all widgets that use it, so you don't accidentally lose the script if you remove
|
6
6
|
# the one widget that happened to declare it.
|
7
7
|
#
|
8
|
-
# At minimum, child classes must override #body_content. You can also get a "quick and dirty"
|
9
|
-
# page by passing a block to Page.new but that doesn't really buy you much.
|
10
|
-
#
|
11
8
|
# The script and style declarations are accumulated at class load time, as 'externals'.
|
12
9
|
# This technique allows all widgets to add their own requirements to the page header
|
13
10
|
# without extra logic for declaring which pages include which nested widgets.
|
14
11
|
# Unfortunately, this means that every page in the application will share the same headers,
|
15
|
-
# which may lead to conflicts.
|
12
|
+
# which may lead to conflicts.
|
16
13
|
#
|
17
|
-
# If you want something to show up in the headers for just one page type (subclass),
|
14
|
+
# If you want something to show up in the headers for just one page type (subclass),
|
18
15
|
# then override #head_content, call super, and then emit it yourself.
|
19
16
|
#
|
17
|
+
# Body content can be supplied in several ways:
|
18
|
+
#
|
19
|
+
# * In a Page subclass, by overriding the #body_content method:
|
20
|
+
#
|
21
|
+
# class MyPage < Erector::Widgets::Page
|
22
|
+
# def body_content
|
23
|
+
# text "body content"
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# * Or by overriding #content and passing a block to super:
|
28
|
+
#
|
29
|
+
# class MyPage < Erector::Widgets::Page
|
30
|
+
# def content
|
31
|
+
# super do
|
32
|
+
# text "body content"
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# * Or by passing a block to Page.new:
|
38
|
+
#
|
39
|
+
# Erector::Widgets::Page.new do
|
40
|
+
# text "body content"
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# This last trick (passing a block to Page.new) works because Page is an InlineWidget
|
44
|
+
# so its block is evaluated in the context of the newly instantiated widget object,
|
45
|
+
# and not in the context of its caller. But this means you can't access instance variables
|
46
|
+
# of the caller, e.g.
|
47
|
+
#
|
48
|
+
# @name = "fred"
|
49
|
+
# Erector::Widgets::Page.new do
|
50
|
+
# text "my name is #{@name}"
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# will emit "my name is " because @name is nil inside the new Page. However, you *can*
|
54
|
+
# call methods in the parent class, thanks to some method_missing magic. Confused? You
|
55
|
+
# should be. See Erector::Inline#content for more documentation.
|
56
|
+
#
|
20
57
|
# Author:: Alex Chaffee, alex@stinky.com
|
21
58
|
#
|
22
59
|
# = Example Usage:
|
@@ -52,9 +89,7 @@
|
|
52
89
|
# * It may be desirable to unify #js and #script, and #css and #style, and have the routine be
|
53
90
|
# smart enough to analyze its parameter to decide whether to make it a file or a script.
|
54
91
|
#
|
55
|
-
class Erector::Widgets::Page < Erector::
|
56
|
-
|
57
|
-
needs :basic_styles => true
|
92
|
+
class Erector::Widgets::Page < Erector::InlineWidget
|
58
93
|
|
59
94
|
# Emit the Transitional doctype.
|
60
95
|
# TODO: allow selection from among different standard doctypes
|
@@ -65,13 +100,18 @@ class Erector::Widgets::Page < Erector::Widget
|
|
65
100
|
|
66
101
|
def content
|
67
102
|
rawtext doctype
|
68
|
-
|
69
|
-
html :xmlns => 'http://www.w3.org/1999/xhtml', 'xml:lang' => 'en', :lang => 'en' do
|
103
|
+
html(html_attributes) do
|
70
104
|
head do
|
71
105
|
head_content
|
72
106
|
end
|
73
|
-
body
|
74
|
-
|
107
|
+
body(body_attributes) do
|
108
|
+
if block_given?
|
109
|
+
yield
|
110
|
+
elsif @block
|
111
|
+
super
|
112
|
+
else
|
113
|
+
body_content
|
114
|
+
end
|
75
115
|
end
|
76
116
|
end
|
77
117
|
end
|
@@ -81,13 +121,18 @@ class Erector::Widgets::Page < Erector::Widget
|
|
81
121
|
self.class.name
|
82
122
|
end
|
83
123
|
|
84
|
-
# override me to
|
85
|
-
def
|
124
|
+
# override me to change the attributes of the HTML element
|
125
|
+
def html_attributes
|
126
|
+
{:xmlns => 'http://www.w3.org/1999/xhtml', 'xml:lang' => 'en', :lang => 'en'}
|
127
|
+
end
|
128
|
+
|
129
|
+
# override me to add attributes (e.g. a css class) to the body
|
130
|
+
def body_attributes
|
131
|
+
{}
|
86
132
|
end
|
87
133
|
|
88
134
|
# override me (or instantiate Page with a block)
|
89
135
|
def body_content
|
90
|
-
instance_eval(&@block) if @block
|
91
136
|
end
|
92
137
|
|
93
138
|
# emit the contents of the head element. Override and call super if you want to put more stuff in there.
|
@@ -95,7 +140,6 @@ class Erector::Widgets::Page < Erector::Widget
|
|
95
140
|
meta 'http-equiv' => 'content-type', :content => 'text/html;charset=UTF-8'
|
96
141
|
title page_title
|
97
142
|
|
98
|
-
basic_styles if @basic_styles
|
99
143
|
included_stylesheets
|
100
144
|
inline_styles
|
101
145
|
|
@@ -104,53 +148,34 @@ class Erector::Widgets::Page < Erector::Widget
|
|
104
148
|
end
|
105
149
|
|
106
150
|
def included_scripts
|
107
|
-
self.class.externals(:js).each do |
|
108
|
-
script
|
151
|
+
self.class.externals(:js).each do |external|
|
152
|
+
script({:type => "text/javascript", :src => external.text}.merge(external.options))
|
109
153
|
end
|
110
154
|
end
|
111
155
|
|
112
156
|
def included_stylesheets
|
113
|
-
self.class.externals(:css).each do |
|
114
|
-
|
115
|
-
link :rel => "stylesheet", :href => file, :type => "text/css", :media => "all"
|
157
|
+
self.class.externals(:css).each do |external|
|
158
|
+
link({:rel => "stylesheet", :href => external.text, :type => "text/css", :media => "all"}.merge(external.options))
|
116
159
|
end
|
117
160
|
end
|
118
|
-
|
119
|
-
# Emit some *very* basic styles, hopefully not too controversial. Suppress
|
120
|
-
# them by setting :basic_styles => false when you construct your Page, or just
|
121
|
-
# override the basic_styles method in your subclass and make it do nothing.
|
122
|
-
# You can also redefine them since they're defined above any other styles in the HEAD.
|
123
|
-
#
|
124
|
-
# Class "right" floats right, class "left" floats left, and class "clear" clears
|
125
|
-
# any floats on both sides while being as small as possible to minimize impact
|
126
|
-
# on your layout. And images have no border.
|
127
|
-
def basic_styles
|
128
|
-
style <<-STYLE
|
129
|
-
img {border: none}
|
130
|
-
.right {float: right;}
|
131
|
-
.left {float: left;}
|
132
|
-
.clear {background: none;border: 0;clear: both;display: block;float: none;font-size: 0;margin: 0;padding: 0;position: static;overflow: hidden;visibility: hidden;width: 0;height: 0;}
|
133
|
-
STYLE
|
134
|
-
end
|
135
161
|
|
136
162
|
def inline_styles
|
137
|
-
style
|
138
|
-
|
139
|
-
|
140
|
-
rawtext "\n"
|
141
|
-
rawtext txt
|
163
|
+
self.class.externals(:style).each do |external|
|
164
|
+
style({:type => "text/css", 'xml:space' => 'preserve'}.merge(external.options)) do
|
165
|
+
rawtext external.text
|
142
166
|
end
|
143
167
|
end
|
144
168
|
end
|
145
169
|
|
146
170
|
def inline_scripts
|
147
|
-
|
148
|
-
|
149
|
-
rawtext
|
150
|
-
rawtext txt
|
171
|
+
self.class.externals(:script).each do |external|
|
172
|
+
javascript external.options do
|
173
|
+
rawtext external.text
|
151
174
|
end
|
152
|
-
|
153
|
-
|
175
|
+
end
|
176
|
+
self.class.externals(:jquery).each do |external|
|
177
|
+
javascript external.options do
|
178
|
+
jquery_ready external.text
|
154
179
|
end
|
155
180
|
end
|
156
181
|
end
|
data/rails/init.rb
ADDED
@@ -1,6 +1,47 @@
|
|
1
1
|
require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
|
2
2
|
require "erector/rails"
|
3
3
|
|
4
|
+
# backport mktmpdir so this test will work on Ruby 1.8.6
|
5
|
+
unless Dir.respond_to?(:mktmpdir)
|
6
|
+
def Dir.mktmpdir(prefix_suffix=nil, tmpdir=nil)
|
7
|
+
case prefix_suffix
|
8
|
+
when nil
|
9
|
+
prefix = "d"
|
10
|
+
suffix = ""
|
11
|
+
when String
|
12
|
+
prefix = prefix_suffix
|
13
|
+
suffix = ""
|
14
|
+
when Array
|
15
|
+
prefix = prefix_suffix[0]
|
16
|
+
suffix = prefix_suffix[1]
|
17
|
+
else
|
18
|
+
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
|
19
|
+
end
|
20
|
+
tmpdir ||= Dir.tmpdir
|
21
|
+
t = Time.now.strftime("%Y%m%d")
|
22
|
+
n = nil
|
23
|
+
begin
|
24
|
+
path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
|
25
|
+
path << "-#{n}" if n
|
26
|
+
path << suffix
|
27
|
+
Dir.mkdir(path, 0700)
|
28
|
+
rescue Errno::EEXIST
|
29
|
+
n ||= 0
|
30
|
+
n += 1
|
31
|
+
retry
|
32
|
+
end
|
33
|
+
|
34
|
+
if block_given?
|
35
|
+
begin
|
36
|
+
yield path
|
37
|
+
ensure
|
38
|
+
FileUtils.remove_entry_secure path
|
39
|
+
end
|
40
|
+
else
|
41
|
+
path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
4
45
|
|
5
46
|
# Note: this is *not* inside the rails_root since we're not testing
|
6
47
|
# Erector inside a rails app. We're testing that we can use the command-line
|
@@ -9,19 +50,16 @@ require "erector/rails"
|
|
9
50
|
#
|
10
51
|
module Erector
|
11
52
|
|
12
|
-
describe "the
|
13
|
-
it "should
|
14
|
-
|
15
|
-
dep = Gem::Dependency.new "rails", target_version
|
16
|
-
specs = Gem.source_index.search dep
|
17
|
-
specs.size.should == 1
|
53
|
+
describe "the Rails version" do
|
54
|
+
it "should be #{Erector::Rails::RAILS_VERSION}" do
|
55
|
+
::Rails::VERSION::STRING.should == Erector::Rails::RAILS_VERSION
|
18
56
|
end
|
19
57
|
end
|
20
58
|
|
21
59
|
describe "Erect in a Rails app" do
|
22
60
|
|
23
61
|
def run(cmd)
|
24
|
-
puts "Running #{cmd}"
|
62
|
+
# puts "Running #{cmd}"
|
25
63
|
stderr_file = Dir.tmpdir + "/stderr.txt"
|
26
64
|
stdout = IO.popen(cmd + " 2>#{stderr_file}") do |pipe|
|
27
65
|
pipe.read
|
@@ -44,29 +82,28 @@ module Erector
|
|
44
82
|
# in a "ruby -e" command line invocation of the rails executable to generate an
|
45
83
|
# app called explode.
|
46
84
|
#
|
47
|
-
puts "Generating fresh rails #{Erector::Rails::RAILS_VERSION} app in #{app_dir}"
|
85
|
+
# puts "Generating fresh rails #{Erector::Rails::RAILS_VERSION} app in #{app_dir}"
|
48
86
|
run "ruby -e \"require 'rubygems'; gem 'rails', '#{Erector::Rails::RAILS_VERSION}'; load 'rails'\" #{app_dir}"
|
49
87
|
end
|
50
88
|
|
51
89
|
it "works like we say it does in the user guide" do
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# open http://localhost:3000/posts
|
90
|
+
erector_dir = File.expand_path("#{File.dirname(__FILE__)}/../..")
|
91
|
+
|
92
|
+
Dir.mktmpdir do |app_dir|
|
93
|
+
run_rails app_dir
|
94
|
+
|
95
|
+
FileUtils.mkdir_p(app_dir + "/vendor/gems")
|
96
|
+
FileUtils.cp_r(erector_dir, "#{app_dir}/vendor/gems/erector")
|
97
|
+
|
98
|
+
FileUtils.cd(app_dir) do
|
99
|
+
run "script/generate scaffold post title:string body:text published:boolean"
|
100
|
+
run "ruby -I#{erector_dir}/lib #{erector_dir}/bin/erector app/views/posts"
|
101
|
+
FileUtils.rm_f("app/views/posts/*.erb")
|
102
|
+
run "rake --trace db:migrate"
|
103
|
+
# run "script/server" # todo: launch in background; use mechanize or something to crawl it; then kill it
|
104
|
+
# perhaps use open4?
|
105
|
+
# open http://localhost:3000/posts
|
106
|
+
end
|
70
107
|
end
|
71
108
|
end
|
72
109
|
|
data/spec/erect/erected_spec.rb
CHANGED
@@ -32,9 +32,9 @@ module Erector
|
|
32
32
|
Erected.new("stuff/foo_bar.html.erb").parent_class.should == "Erector::Widget"
|
33
33
|
end
|
34
34
|
|
35
|
-
it "uses
|
36
|
-
Erected.new("app/views/stuff/foo_bar.html.erb").parent_class.should == "Erector::
|
37
|
-
Erected.new("views/stuff/foo_bar.html.erb").parent_class.should == "Erector::
|
35
|
+
it "uses Widget as the parent class if it's in a views dir" do
|
36
|
+
Erected.new("app/views/stuff/foo_bar.html.erb").parent_class.should == "Erector::Widget"
|
37
|
+
Erected.new("views/stuff/foo_bar.html.erb").parent_class.should == "Erector::Widget"
|
38
38
|
end
|
39
39
|
|
40
40
|
def convert(dir, input, output)
|
@@ -70,7 +70,7 @@ module Erector
|
|
70
70
|
it "converts a views file" do
|
71
71
|
convert("app/views/foos",
|
72
72
|
"<div>hello</div>",
|
73
|
-
"class Views::Foos::Dummy < Erector::
|
73
|
+
"class Views::Foos::Dummy < Erector::Widget\n" +
|
74
74
|
" def content\n" +
|
75
75
|
" div do\n" +
|
76
76
|
" text 'hello'\n" +
|
@@ -1,9 +1,50 @@
|
|
1
1
|
require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
|
2
2
|
require 'benchmark'
|
3
|
+
require 'active_support' # for Symbol#to_proc
|
4
|
+
|
5
|
+
describe Erector::External do
|
6
|
+
it "can be constructed with type, klass, text" do
|
7
|
+
x = Erector::External.new(:foo, Object, "abc")
|
8
|
+
x.type.should == :foo
|
9
|
+
x.klass.should == Object
|
10
|
+
x.text.should == "abc"
|
11
|
+
x.options.should == {}
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can be constructed with type, klass, text, and options" do
|
15
|
+
x = Erector::External.new(:foo, Object, "abc", {:bar => 7})
|
16
|
+
x.options.should == {:bar => 7}
|
17
|
+
end
|
18
|
+
|
19
|
+
it "is equal to an identical external" do
|
20
|
+
x = Erector::External.new(:foo, Object, "abc", {:bar => 7})
|
21
|
+
y = Erector::External.new(:foo, Object, "abc", {:bar => 7})
|
22
|
+
x.should == y
|
23
|
+
[x].should include(y)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "is equal to an identical external with a different class" do
|
27
|
+
class_x = Class.new
|
28
|
+
class_y = Class.new
|
29
|
+
x = Erector::External.new(:foo, class_x, "abc", {:bar => 7})
|
30
|
+
y = Erector::External.new(:foo, class_y, "abc", {:bar => 7})
|
31
|
+
x.should == y
|
32
|
+
[x].should include(y)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "is not equal to an identical external with a different options" do
|
36
|
+
class_x = Class.new
|
37
|
+
x = Erector::External.new(:foo, class_x, "abc")
|
38
|
+
y = Erector::External.new(:foo, class_x, "abc", {:bar => 7})
|
39
|
+
x.should_not == y
|
40
|
+
[x].should_not include(y)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
3
44
|
|
4
45
|
describe "external declarations" do
|
5
46
|
class HotSauce < Erector::Widget
|
6
|
-
external :css, "/css/tapatio.css"
|
47
|
+
external :css, "/css/tapatio.css", :media => "print"
|
7
48
|
external :css, "/css/salsa_picante.css"
|
8
49
|
external :js, "/lib/jquery.js"
|
9
50
|
external :js, "/lib/picante.js"
|
@@ -16,7 +57,7 @@ describe "external declarations" do
|
|
16
57
|
end
|
17
58
|
|
18
59
|
it "can be fetched via the type" do
|
19
|
-
Erector::Widget.externals(:css).should == [
|
60
|
+
Erector::Widget.externals(:css).map(&:text).should == [
|
20
61
|
"/css/tapatio.css",
|
21
62
|
"/css/salsa_picante.css",
|
22
63
|
"/css/sourcream.css",
|
@@ -24,25 +65,42 @@ describe "external declarations" do
|
|
24
65
|
end
|
25
66
|
|
26
67
|
it "can be filtered via the class" do
|
27
|
-
Erector::Widget.externals(:css, HotSauce).should == [
|
68
|
+
Erector::Widget.externals(:css, HotSauce).map(&:text).should == [
|
28
69
|
"/css/tapatio.css",
|
29
70
|
"/css/salsa_picante.css",
|
30
71
|
]
|
31
|
-
Erector::Widget.externals(:css, SourCream).should == [
|
72
|
+
Erector::Widget.externals(:css, SourCream).map(&:text).should == [
|
32
73
|
"/css/sourcream.css",
|
33
74
|
]
|
34
75
|
end
|
35
76
|
|
77
|
+
it "retains the options" do
|
78
|
+
Erector::Widget.externals(:css, HotSauce).map(&:options).should == [
|
79
|
+
{:media => "print"},
|
80
|
+
{}
|
81
|
+
]
|
82
|
+
end
|
83
|
+
|
36
84
|
it "removes duplicates" do
|
37
|
-
Erector::Widget.externals(:js).should == [
|
85
|
+
Erector::Widget.externals(:js).map(&:text).should == [
|
38
86
|
"/lib/jquery.js",
|
39
87
|
"/lib/picante.js",
|
40
88
|
"/lib/dairy.js",
|
41
89
|
]
|
42
90
|
end
|
91
|
+
|
92
|
+
|
93
|
+
class Taco < Erector::Widget
|
94
|
+
external :filling, "beef"
|
95
|
+
external :filling, "beef", :media => "print"
|
96
|
+
end
|
97
|
+
|
98
|
+
it "considers options when removing duplicates" do
|
99
|
+
Erector::Widget.externals(:filling).map(&:text).should == ["beef", "beef"]
|
100
|
+
end
|
43
101
|
|
44
102
|
it "works with strings or symbols" do
|
45
|
-
Erector::Widget.externals("js").should == [
|
103
|
+
Erector::Widget.externals("js").map(&:text).should == [
|
46
104
|
"/lib/jquery.js",
|
47
105
|
"/lib/picante.js",
|
48
106
|
"/lib/dairy.js",
|