shaven 0.0.1
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/.gitignore +12 -0
- data/.rspec +1 -0
- data/Isolate +12 -0
- data/README.md +183 -0
- data/Rakefile +47 -0
- data/lib/shaven.rb +27 -0
- data/lib/shaven/core_ext/hash.rb +22 -0
- data/lib/shaven/core_ext/object.rb +17 -0
- data/lib/shaven/document.rb +9 -0
- data/lib/shaven/helpers/html.rb +68 -0
- data/lib/shaven/nokogiri_ext/node.rb +86 -0
- data/lib/shaven/presenter.rb +90 -0
- data/lib/shaven/scope.rb +50 -0
- data/lib/shaven/transformer.rb +109 -0
- data/lib/shaven/transformers/auto.rb +17 -0
- data/lib/shaven/transformers/condition.rb +25 -0
- data/lib/shaven/transformers/context.rb +35 -0
- data/lib/shaven/transformers/dummy.rb +22 -0
- data/lib/shaven/transformers/list.rb +51 -0
- data/lib/shaven/transformers/reverse_condition.rb +25 -0
- data/lib/shaven/transformers/text_or_node.rb +42 -0
- data/lib/shaven/version.rb +13 -0
- data/shaven.gemspec +20 -0
- data/spec/benchmarks.rb +189 -0
- data/spec/fixtures/condition.html +5 -0
- data/spec/fixtures/context.html +7 -0
- data/spec/fixtures/dummy.html +4 -0
- data/spec/fixtures/list.html +6 -0
- data/spec/fixtures/list_of_contexts.html +6 -0
- data/spec/fixtures/reverse_condition.html +5 -0
- data/spec/fixtures/text_or_node.html +4 -0
- data/spec/helpers_html_spec.rb +54 -0
- data/spec/nokogiri_node_spec.rb +55 -0
- data/spec/presenter_spec.rb +36 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/transformers/auto_spec.rb +34 -0
- data/spec/transformers/condition_spec.rb +28 -0
- data/spec/transformers/context_spec.rb +25 -0
- data/spec/transformers/dummy_spec.rb +18 -0
- data/spec/transformers/list_spec.rb +41 -0
- data/spec/transformers/reverse_condition_spec.rb +28 -0
- data/spec/transformers/text_or_node_spec.rb +65 -0
- metadata +123 -0
data/shaven.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
$LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
|
3
|
+
require 'shaven/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "shaven"
|
7
|
+
s.rubyforge_project = "shaven"
|
8
|
+
s.version = Shaven::VERSION
|
9
|
+
s.authors = ["Chris Kowalik"]
|
10
|
+
s.email = ["chris@nu7hat.ch"]
|
11
|
+
s.homepage = "http://github.com/nu7hatch/shaven"
|
12
|
+
s.summary = "Shaven - templating without mustaches!"
|
13
|
+
s.description = "Shaven is templating system for logic less, and extra markup free templates."
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {spec}/*`.split("\n")
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
s.add_dependency 'nokogiri', '~> 1.4'
|
20
|
+
end
|
data/spec/benchmarks.rb
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
require 'shaven'
|
6
|
+
require 'erb'
|
7
|
+
require 'haml'
|
8
|
+
require 'mustache'
|
9
|
+
|
10
|
+
$erb = <<-HTML
|
11
|
+
<!DOCTYPE html>
|
12
|
+
<html>
|
13
|
+
<head>
|
14
|
+
<title><%= @title %></div>
|
15
|
+
</head>
|
16
|
+
<body>
|
17
|
+
<% if @condition %>
|
18
|
+
Something conditional!
|
19
|
+
<% else %>
|
20
|
+
Something else
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<div id="user">
|
24
|
+
<div class="name"><%= @user[:name] %></div>
|
25
|
+
<div class="email"><%= @user[:email] %></div>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<ul id="items">
|
29
|
+
<% @items.each do |item| %>
|
30
|
+
<li class="item">
|
31
|
+
<div class="name"><%= item[:name] %></div>
|
32
|
+
<div class="price"><%= item[:price] %></div>
|
33
|
+
</li>
|
34
|
+
<% end %>
|
35
|
+
</ul>
|
36
|
+
</body>
|
37
|
+
</html>
|
38
|
+
HTML
|
39
|
+
|
40
|
+
$mustache = <<-HTML
|
41
|
+
<!DOCTYPE html>
|
42
|
+
<html>
|
43
|
+
<head>
|
44
|
+
<title>{{title}}</div>
|
45
|
+
</head>
|
46
|
+
<body>
|
47
|
+
{{#condition}}
|
48
|
+
Something conditional!
|
49
|
+
{{/condition}}
|
50
|
+
{{^condition}}
|
51
|
+
Something else
|
52
|
+
{{/condition}}
|
53
|
+
|
54
|
+
{{#user}}
|
55
|
+
<div id="user">
|
56
|
+
<div class="user">{{name}}</div>
|
57
|
+
<div class="email">{{email}}</div>
|
58
|
+
</div>
|
59
|
+
{{/user}}
|
60
|
+
|
61
|
+
<ul id="items">
|
62
|
+
{{#items}}
|
63
|
+
<li class="item">
|
64
|
+
<div class="name">{{name}}</div>
|
65
|
+
<div class="price">{{price}}</div>
|
66
|
+
</li>
|
67
|
+
{{/items}}
|
68
|
+
</ul>
|
69
|
+
</body>
|
70
|
+
</html>
|
71
|
+
HTML
|
72
|
+
|
73
|
+
$haml = <<-HAML
|
74
|
+
!!!
|
75
|
+
%html
|
76
|
+
%head
|
77
|
+
%title= title
|
78
|
+
%body
|
79
|
+
- if condition
|
80
|
+
Something conditional!
|
81
|
+
- else
|
82
|
+
Something else
|
83
|
+
#user
|
84
|
+
.name= user[:name]
|
85
|
+
.email= user[:email]
|
86
|
+
%ul#items
|
87
|
+
- items.each do |item|
|
88
|
+
%li.item
|
89
|
+
.name= item[:name]
|
90
|
+
.price= item[:price]
|
91
|
+
HAML
|
92
|
+
|
93
|
+
$doc = <<-HTML
|
94
|
+
<!DOCTYPE html>
|
95
|
+
<html>
|
96
|
+
<head>
|
97
|
+
<title rb="page_title">Sample title</title>
|
98
|
+
</head>
|
99
|
+
<body>
|
100
|
+
<div rb:if="condition">
|
101
|
+
Something conditional!
|
102
|
+
</div>
|
103
|
+
<div rb:unless="condition">
|
104
|
+
Something else
|
105
|
+
</div>
|
106
|
+
|
107
|
+
<div rb="user">
|
108
|
+
<div rb="name">Name</div>
|
109
|
+
<div rb="email">email@mail.com</div>
|
110
|
+
</div>
|
111
|
+
|
112
|
+
<ul id="items">
|
113
|
+
<li rb="items">
|
114
|
+
<div rb="name">Foobar</div>
|
115
|
+
<div rb="price">$250</div>
|
116
|
+
</li>
|
117
|
+
</ul>
|
118
|
+
</body>
|
119
|
+
</html>
|
120
|
+
HTML
|
121
|
+
|
122
|
+
$user = {
|
123
|
+
:name => "Marty Macfly",
|
124
|
+
:email => "marty@macfly.com"
|
125
|
+
}
|
126
|
+
|
127
|
+
$items = [
|
128
|
+
{ :name => "First", :price => "$250" },
|
129
|
+
{ :name => "Second", :price => "$300" },
|
130
|
+
{ :name => "Third", :price => "$500" }
|
131
|
+
]
|
132
|
+
|
133
|
+
$title = "Hello world!"
|
134
|
+
|
135
|
+
class MyPresenter < Shaven::Presenter
|
136
|
+
def user
|
137
|
+
$user
|
138
|
+
end
|
139
|
+
|
140
|
+
def items
|
141
|
+
$items
|
142
|
+
end
|
143
|
+
|
144
|
+
def condition
|
145
|
+
false
|
146
|
+
end
|
147
|
+
|
148
|
+
def page_title
|
149
|
+
$title
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
class ErbView
|
154
|
+
def initialize
|
155
|
+
@condition = false
|
156
|
+
@title = $title
|
157
|
+
@items = $items
|
158
|
+
@user = $user
|
159
|
+
end
|
160
|
+
|
161
|
+
def render
|
162
|
+
ERB.new($erb).result(binding)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def render_shaven
|
167
|
+
MyPresenter.feed($doc).to_html
|
168
|
+
end
|
169
|
+
|
170
|
+
def render_erb
|
171
|
+
ErbView.new.render
|
172
|
+
end
|
173
|
+
|
174
|
+
def render_haml
|
175
|
+
Haml::Engine.new($haml).render(self, { :user => $user, :items => $items, :title => $title, :condition => false})
|
176
|
+
end
|
177
|
+
|
178
|
+
def render_mustache
|
179
|
+
Mustache.render($mustache, { :user => $user, :items => $items, :title => $title, :condition => false})
|
180
|
+
end
|
181
|
+
|
182
|
+
n = 1500
|
183
|
+
|
184
|
+
Benchmark.bm do |x|
|
185
|
+
x.report("Shaven ") { n.times { render_shaven } }
|
186
|
+
x.report("Mustache") { n.times { render_mustache } }
|
187
|
+
x.report("ERB ") { n.times { render_erb } }
|
188
|
+
x.report("HAML ") { n.times { render_haml } }
|
189
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Shaven::Presenter do
|
4
|
+
subject do
|
5
|
+
Shaven::Presenter.feed("<html><body></body></html>")
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#tag" do
|
9
|
+
it "creates element within current doc" do
|
10
|
+
div = subject.tag(:div, :id => "foo") { "Hello!" }
|
11
|
+
div.to_html.should == '<div id="foo">Hello!</div>'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates element without attributes when not given" do
|
15
|
+
div = subject.tag(:div) { "Hello!" }
|
16
|
+
div.to_html.should == '<div>Hello!</div>'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#div" do
|
21
|
+
it "creates new div element within current doc" do
|
22
|
+
div = subject.div(:id => "foo") { "Hello!" }
|
23
|
+
div.to_html.should == '<div id="foo">Hello!</div>'
|
24
|
+
end
|
25
|
+
|
26
|
+
it "creates new div element without arguments when not given" do
|
27
|
+
div = subject.div { "Hello!" }
|
28
|
+
div.to_html.should == '<div>Hello!</div>'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#img" do
|
33
|
+
it "creates new img element within current doc" do
|
34
|
+
img = subject.img(:src => "foo.jpg")
|
35
|
+
img.to_html.should == '<img src="foo.jpg">'
|
36
|
+
end
|
37
|
+
|
38
|
+
it "creates new img element without arguments when not given" do
|
39
|
+
subject.img.to_html.should == '<img>'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "#a" do
|
44
|
+
it "creates new a element within current doc" do
|
45
|
+
a = subject.a(:href => "foo.html") { "Hello!" }
|
46
|
+
a.to_html.should == '<a href="foo.html">Hello!</a>'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "creates new a element without arguments when not given" do
|
50
|
+
a = subject.a { "Hello!"}
|
51
|
+
a.to_html.should == '<a>Hello!</a>'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Nokogiri::XML::Node do
|
4
|
+
let(:doc) do
|
5
|
+
Nokogiri::HTML("<html><body></body></html>")
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:node) do
|
9
|
+
Nokogiri::XML::Node.new("div", doc)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#update!" do
|
13
|
+
it "updates attributes and content" do
|
14
|
+
node.update!({:id => "test"}, "Foobar!")
|
15
|
+
node.content.should == "Foobar!"
|
16
|
+
node['id'].should == "test"
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when attribute is set to false" do
|
20
|
+
it "removes it" do
|
21
|
+
node['id'] = "test"
|
22
|
+
node.update! :id => false
|
23
|
+
node['id'].should_not be
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when block given" do
|
28
|
+
it "treats it as content" do
|
29
|
+
node.update! { "Hello world!" }
|
30
|
+
node.content.should == "Hello world!"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when UJS attrs given" do
|
35
|
+
it "converts them properly to html attributes" do
|
36
|
+
node.update! :method => 'post', :remote => 'true', :confirm => "Are you sure?"
|
37
|
+
node['data-method'].should == 'post'
|
38
|
+
node['data-remote'].should == 'data-remote'
|
39
|
+
node['data-confirm'].should == 'Are you sure?'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#replace!" do
|
45
|
+
let(:other_node) do
|
46
|
+
other_node = Nokogiri::XML::Node.new("p", doc)
|
47
|
+
other_node.content = "Other node!"
|
48
|
+
other_node
|
49
|
+
end
|
50
|
+
|
51
|
+
it "replaces node with given text or other node" do
|
52
|
+
# tested in other place...
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/spec_helper"
|
2
|
+
|
3
|
+
describe Shaven::Presenter do
|
4
|
+
describe ".feed" do
|
5
|
+
it "creates instance of given presenter for given html" do
|
6
|
+
p = Shaven::Presenter.feed("<!DOCTYPE html><html><body>Hello!</body></html>")
|
7
|
+
p.to_html.should == "<!DOCTYPE html>\n<html><body>Hello!</body></html>\n"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#to_html" do
|
12
|
+
it "generates html code from presenter data" do
|
13
|
+
# tested in `.feed` examples
|
14
|
+
end
|
15
|
+
|
16
|
+
context "when context given" do
|
17
|
+
it "combines it with current scope" do
|
18
|
+
p = make_presenter("text_or_node.html")
|
19
|
+
p.to_html(:value => "Hello Context!").should == "<!DOCTYPE html>\n<html><body>\n<div>Hello Context!</div>\n</body></html>\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#compiled" do
|
25
|
+
it "returns false before first render" do
|
26
|
+
p = make_presenter("text_or_node.html")
|
27
|
+
p.should_not be_compiled
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns true after first render" do
|
31
|
+
p = make_presenter("text_or_node.html")
|
32
|
+
p.to_html(:value => "Hello!")
|
33
|
+
p.should be_compiled
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Shaven::Transformer::Auto do
|
4
|
+
subject do
|
5
|
+
Shaven::Transformer::Auto
|
6
|
+
end
|
7
|
+
|
8
|
+
it "always can be transformed" do
|
9
|
+
subject.can_be_transformed?(nil).should be_true
|
10
|
+
end
|
11
|
+
|
12
|
+
describe ".new" do
|
13
|
+
context "when value is an array" do
|
14
|
+
it "returns list transformer object" do
|
15
|
+
t = Shaven::Transformer::Auto.new("value", [1,2,3], Shaven::Scope.new({}))
|
16
|
+
t.should be_kind_of(Shaven::Transformer::List)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when value is an hash" do
|
21
|
+
it "returns context transformer object" do
|
22
|
+
t = Shaven::Transformer::Auto.new("value", {:foo => "bar"}, Shaven::Scope.new({}))
|
23
|
+
t.should be_kind_of(Shaven::Transformer::Context)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "otherwise" do
|
28
|
+
it "returns `text or node` transformer object" do
|
29
|
+
t = Shaven::Transformer::Auto.new("value", "Foo!", Shaven::Scope.new({}))
|
30
|
+
t.should be_kind_of(Shaven::Transformer::TextOrNode)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|