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.
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