shaven 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +12 -0
  2. data/.rspec +1 -0
  3. data/Isolate +12 -0
  4. data/README.md +183 -0
  5. data/Rakefile +47 -0
  6. data/lib/shaven.rb +27 -0
  7. data/lib/shaven/core_ext/hash.rb +22 -0
  8. data/lib/shaven/core_ext/object.rb +17 -0
  9. data/lib/shaven/document.rb +9 -0
  10. data/lib/shaven/helpers/html.rb +68 -0
  11. data/lib/shaven/nokogiri_ext/node.rb +86 -0
  12. data/lib/shaven/presenter.rb +90 -0
  13. data/lib/shaven/scope.rb +50 -0
  14. data/lib/shaven/transformer.rb +109 -0
  15. data/lib/shaven/transformers/auto.rb +17 -0
  16. data/lib/shaven/transformers/condition.rb +25 -0
  17. data/lib/shaven/transformers/context.rb +35 -0
  18. data/lib/shaven/transformers/dummy.rb +22 -0
  19. data/lib/shaven/transformers/list.rb +51 -0
  20. data/lib/shaven/transformers/reverse_condition.rb +25 -0
  21. data/lib/shaven/transformers/text_or_node.rb +42 -0
  22. data/lib/shaven/version.rb +13 -0
  23. data/shaven.gemspec +20 -0
  24. data/spec/benchmarks.rb +189 -0
  25. data/spec/fixtures/condition.html +5 -0
  26. data/spec/fixtures/context.html +7 -0
  27. data/spec/fixtures/dummy.html +4 -0
  28. data/spec/fixtures/list.html +6 -0
  29. data/spec/fixtures/list_of_contexts.html +6 -0
  30. data/spec/fixtures/reverse_condition.html +5 -0
  31. data/spec/fixtures/text_or_node.html +4 -0
  32. data/spec/helpers_html_spec.rb +54 -0
  33. data/spec/nokogiri_node_spec.rb +55 -0
  34. data/spec/presenter_spec.rb +36 -0
  35. data/spec/spec_helper.rb +11 -0
  36. data/spec/transformers/auto_spec.rb +34 -0
  37. data/spec/transformers/condition_spec.rb +28 -0
  38. data/spec/transformers/context_spec.rb +25 -0
  39. data/spec/transformers/dummy_spec.rb +18 -0
  40. data/spec/transformers/list_spec.rb +41 -0
  41. data/spec/transformers/reverse_condition_spec.rb +28 -0
  42. data/spec/transformers/text_or_node_spec.rb +65 -0
  43. metadata +123 -0
@@ -0,0 +1,13 @@
1
+ module Shaven
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+
7
+ def self.to_s
8
+ [MAJOR, MINOR, PATCH].join('.')
9
+ end
10
+ end # Version
11
+
12
+ VERSION = Version.to_s
13
+ end # Shaven
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
@@ -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,5 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <div rb:if="false?">This shouldn't appear!</div>
4
+ <div rb:if="true?">This should appear!</div>
5
+ </body></html>
@@ -0,0 +1,7 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <div rb="user">
4
+ <h2 rb="name">John Doe</h2>
5
+ <p rb="email">email@example.com</p>
6
+ </div>
7
+ </body></html>
@@ -0,0 +1,4 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <div rb:dummy="yes">This is dummy...</div>
4
+ </body></html>
@@ -0,0 +1,6 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <ul>
4
+ <li rb="users">John Doe</li>
5
+ </ul>
6
+ </body></html>
@@ -0,0 +1,6 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <ul>
4
+ <li rb="users"><strong rb="name">John Doe</strong></li>
5
+ </ul>
6
+ </body></html>
@@ -0,0 +1,5 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <div rb:unless="true?">This shouldn't appear!</div>
4
+ <div rb:unless="false?">This should appear!</div>
5
+ </body></html>
@@ -0,0 +1,4 @@
1
+ <!DOCTYPE html>
2
+ <html><body>
3
+ <div rb="value">Example...</div>
4
+ </body></html>
@@ -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
@@ -0,0 +1,11 @@
1
+ require 'rspec'
2
+ require 'shaven'
3
+
4
+ RSpec.configure do |conf|
5
+ conf.mock_with :mocha
6
+ end
7
+
8
+ def make_presenter(file, presenter=Shaven::Presenter)
9
+ html = File.read(File.expand_path("../fixtures/#{file}", __FILE__))
10
+ presenter.feed(html)
11
+ end
@@ -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