ruhl 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/README +241 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/lib/ruhl.rb +135 -0
- data/lib/ruhl/errors.rb +5 -0
- data/lib/ruhl/rails.rb +21 -0
- data/lib/ruhl/sinatra.rb +29 -0
- data/ruhl.gemspec +67 -0
- data/spec/html/basic.html +9 -0
- data/spec/html/fragment.html +2 -0
- data/spec/html/if.html +23 -0
- data/spec/html/layout.html +8 -0
- data/spec/html/main_with_sidebar.html +22 -0
- data/spec/html/medium.html +23 -0
- data/spec/html/render_if.html +25 -0
- data/spec/html/seo.html +11 -0
- data/spec/html/sidebar.html +7 -0
- data/spec/rcov.opts +5 -0
- data/spec/ruhl_spec.rb +163 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +26 -0
- metadata +96 -0
data/.gitignore
ADDED
data/README
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
Ruhl (Ruby Hypertext Language)
|
2
|
+
|
3
|
+
This project is here to flesh out an idea. What I want is to have developers
|
4
|
+
work with HTML and with the simple addition of a 'data-ruhl' attribute, convert it
|
5
|
+
to a dynamic page.
|
6
|
+
|
7
|
+
At no time in the dev process would the view be unviewable in a browser.
|
8
|
+
The view could actually retain the original template data the designer used
|
9
|
+
because this replaces the content. I think this is a nice plus.
|
10
|
+
|
11
|
+
|
12
|
+
Notes (use cases) for me to remember:
|
13
|
+
|
14
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
15
|
+
:: Basic Use ::
|
16
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
17
|
+
|
18
|
+
<h1 data-ruhl="page_header">
|
19
|
+
|
20
|
+
Method :page_header would know how to represent itself in the context of
|
21
|
+
the h1 element.
|
22
|
+
|
23
|
+
The Ruby executed would replace the content of the element it was being
|
24
|
+
called on.
|
25
|
+
|
26
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
27
|
+
:: Replacing attribute values ::
|
28
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
29
|
+
|
30
|
+
<meta data-ruhl="content: meta_description" content='This is a description template'
|
31
|
+
id='metaDescription' name='description' />
|
32
|
+
|
33
|
+
content: meta_description is telling the parser to replace attribute 'content'
|
34
|
+
with results from meta_description method.
|
35
|
+
|
36
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
37
|
+
:: Don't use iterators in views ::
|
38
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
39
|
+
|
40
|
+
<table id="aab">
|
41
|
+
<tr data-ruhl="_collection: user_list">
|
42
|
+
<td data-ruhl="name">John Doe</td>
|
43
|
+
<td data-ruhl="email">john@doe.com</td>
|
44
|
+
</tr>
|
45
|
+
</table>
|
46
|
+
|
47
|
+
The above will call the :user_list method and iterate over the results. For each result it will duplicate the tag and it's contents. For the above example this means:
|
48
|
+
|
49
|
+
<tr data-ruhl="_collection: user_list">
|
50
|
+
<td data-ruhl="name">John Doe</td>
|
51
|
+
<td data-ruhl="email">john@doe.com</td>
|
52
|
+
</tr>
|
53
|
+
|
54
|
+
is duplicated for each user in user_list.
|
55
|
+
|
56
|
+
If user_list return an array of User objects like:
|
57
|
+
|
58
|
+
[ User.create(:name => 'Rupert Boy', :email => 'rupert@stonean.com'),
|
59
|
+
User.create(:name => 'Kaylee Girl', :email => 'kaylee@stonean.com'),
|
60
|
+
User.create(:name => 'Monty Man', :email => 'monty@stonean.com')]
|
61
|
+
|
62
|
+
<table id="aab">
|
63
|
+
<tr>
|
64
|
+
<td>Rupert Boy</td>
|
65
|
+
<td>rupert@stonean.com</td>
|
66
|
+
</tr>
|
67
|
+
<tr>
|
68
|
+
<td>Kaylee Girl</td>
|
69
|
+
<td>kaylee@stonean.com</td>
|
70
|
+
</tr>
|
71
|
+
<tr>
|
72
|
+
<td>Monty Man</td>
|
73
|
+
<td>monty@stonean.com</td>
|
74
|
+
</tr>
|
75
|
+
</table>
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
80
|
+
:: Using a Layout ::
|
81
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
82
|
+
|
83
|
+
Layout:
|
84
|
+
<html>
|
85
|
+
<head>
|
86
|
+
<title>This is a title template</title>
|
87
|
+
</head>
|
88
|
+
<body>
|
89
|
+
<div data-ruhl="_render_"></div>
|
90
|
+
</body>
|
91
|
+
</html>
|
92
|
+
|
93
|
+
Fragment:
|
94
|
+
<h1 data-ruhl="generate_h1">I am a templated headline</h1>
|
95
|
+
<p data-ruhl="my_content">Lorem ipsum dolor sit amet</p>
|
96
|
+
|
97
|
+
To use:
|
98
|
+
|
99
|
+
Ruhl::Engine.new(File.read(fragment), :layout => path_to_layout).render(self)
|
100
|
+
|
101
|
+
Returns the expected result of parsed Layout w/ parsed Fragment.
|
102
|
+
|
103
|
+
Note the use of the _render_ method. This is a 'special' method that
|
104
|
+
Ruhl uses to inject the results of the parsed fragment into the layout.
|
105
|
+
|
106
|
+
|
107
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
108
|
+
:: Using a Partial ::
|
109
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
110
|
+
|
111
|
+
Main:
|
112
|
+
<html>
|
113
|
+
<head>
|
114
|
+
<title>This is a title template</title>
|
115
|
+
</head>
|
116
|
+
<body>
|
117
|
+
<div id="wrap">
|
118
|
+
<div id="sidebar" data-ruhl="_partial: sidebar_partial">
|
119
|
+
<h3>Sidebar links</h3>
|
120
|
+
<ul>
|
121
|
+
<li><a href="#">Link 1</a></li>
|
122
|
+
<li><a href="#">Link 2</a></li>
|
123
|
+
<li><a href="#">Link 3</a></li>
|
124
|
+
<li><a href="#">Link 4</a></li>
|
125
|
+
</ul>
|
126
|
+
</div>
|
127
|
+
<div id="main">
|
128
|
+
<h1> My main content</h1>
|
129
|
+
<p>Text designers would put here to test their layout</p>
|
130
|
+
</div>
|
131
|
+
</div>
|
132
|
+
</body>
|
133
|
+
</html>
|
134
|
+
|
135
|
+
Sidebar:
|
136
|
+
<h3>Real Sidebarlinks</h3>
|
137
|
+
<ul>
|
138
|
+
<li><a href="#">Real Link 1</a></li>
|
139
|
+
<li><a href="#">Real Link 2</a></li>
|
140
|
+
<li><a href="#">Real Link 3</a></li>
|
141
|
+
<li><a href="#">Real Link 4</a></li>
|
142
|
+
</ul>
|
143
|
+
|
144
|
+
To use:
|
145
|
+
|
146
|
+
Ruhl::Engine.new(File.read(path_to_main)).render(self)
|
147
|
+
|
148
|
+
Returns the expected result of parsed Main with sidebar div contents
|
149
|
+
replaced with parsed sidebar partial contents.
|
150
|
+
|
151
|
+
Note the use of the _partial key. This is a 'special' key that Ruhl
|
152
|
+
uses to inject the results of the parsed partial into the contents
|
153
|
+
of the calling node.
|
154
|
+
|
155
|
+
|
156
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
157
|
+
:: Conditional display of block (_render_if)::
|
158
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
159
|
+
|
160
|
+
<html>
|
161
|
+
<head>
|
162
|
+
<title>This is a title template</title>
|
163
|
+
</head>
|
164
|
+
<body>
|
165
|
+
<h1>This is the header template</h1>
|
166
|
+
<div data-ruhl="_render_if: has_users?">
|
167
|
+
<table>
|
168
|
+
<thead>
|
169
|
+
<tr>
|
170
|
+
<td>First Name</td>
|
171
|
+
<td>Last Name</td>
|
172
|
+
<td>Email</td>
|
173
|
+
</tr>
|
174
|
+
</thead>
|
175
|
+
<tr data-ruhl="_collection: user_list">
|
176
|
+
<td data-ruhl="first_name">Andrew</td>
|
177
|
+
<td data-ruhl="last_name">Stone</td>
|
178
|
+
<td data-ruhl="email">andy@stonean.com</td>
|
179
|
+
</tr>
|
180
|
+
</table>
|
181
|
+
</div>
|
182
|
+
</ul>
|
183
|
+
</body>
|
184
|
+
</html>
|
185
|
+
|
186
|
+
if has_users? returns false then the div (including it's contents) are not shown
|
187
|
+
on output.
|
188
|
+
|
189
|
+
|
190
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
191
|
+
:: Conditional display of tag (_if)::
|
192
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
193
|
+
|
194
|
+
<html>
|
195
|
+
<head>
|
196
|
+
<title>This is a title template</title>
|
197
|
+
</head>
|
198
|
+
<body>
|
199
|
+
<h1>This is the header template</h1>
|
200
|
+
<table>
|
201
|
+
<thead>
|
202
|
+
<tr>
|
203
|
+
<td>First Name</td>
|
204
|
+
<td>Last Name</td>
|
205
|
+
<td>Email</td>
|
206
|
+
</tr>
|
207
|
+
</thead>
|
208
|
+
<tr data-ruhl="_collection: user_list">
|
209
|
+
<td data-ruhl="first_name">Andrew</td>
|
210
|
+
<td data-ruhl="last_name">Stone</td>
|
211
|
+
<td data-ruhl="_if: email">andy@stonean.com</td>
|
212
|
+
</tr>
|
213
|
+
</table>
|
214
|
+
</ul>
|
215
|
+
</body>
|
216
|
+
</html>
|
217
|
+
|
218
|
+
if email == nil then the td is not shown.
|
219
|
+
|
220
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
221
|
+
:: Notes ::
|
222
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
223
|
+
|
224
|
+
* No eval (I don't think eval is evil, it's just not the way this works)
|
225
|
+
|
226
|
+
* The data-ruhl attribute is always removed from the output.
|
227
|
+
|
228
|
+
* Each method called must accept a tag parameter.
|
229
|
+
e.g def page_header(tag)
|
230
|
+
|
231
|
+
* Since it's just HTML, syntax highlighting is built-in.
|
232
|
+
For vim, just add this to your ~/.vimrc:
|
233
|
+
au BufNewFile,BufRead *.ruhl set filetype=html
|
234
|
+
|
235
|
+
|
236
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
237
|
+
:: TODO ::
|
238
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
239
|
+
|
240
|
+
1) Test more scenarios
|
241
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rcov'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
|
6
|
+
task :default => 'rcov'
|
7
|
+
|
8
|
+
desc "Run all specs and rcov in a non-sucky way"
|
9
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
10
|
+
t.spec_opts = IO.readlines("spec/spec.opts").map {|l| l.chomp.split " "}.flatten
|
11
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
12
|
+
t.rcov = true
|
13
|
+
t.rcov_opts = IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
begin
|
18
|
+
require 'jeweler'
|
19
|
+
Jeweler::Tasks.new do |gemspec|
|
20
|
+
gemspec.name = "ruhl"
|
21
|
+
gemspec.summary = "Ruby Hypertext Language"
|
22
|
+
gemspec.description = "Make your HTML dynamic with the addition of a ruby attribute."
|
23
|
+
gemspec.email = "andy@stonean.com"
|
24
|
+
gemspec.homepage = "http://github.com/stonean/ruhl"
|
25
|
+
gemspec.authors = ["Andrew Stone"]
|
26
|
+
gemspec.add_dependency('nokogiri','>=1.3.3')
|
27
|
+
gemspec.add_development_dependency('rspec')
|
28
|
+
end
|
29
|
+
rescue LoadError
|
30
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
31
|
+
end
|
32
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.6.0
|
data/lib/ruhl.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'ruhl/errors'
|
5
|
+
|
6
|
+
module Ruhl
|
7
|
+
class Engine
|
8
|
+
attr_reader :document, :scope, :layout, :local_object
|
9
|
+
|
10
|
+
def initialize(html, options = {})
|
11
|
+
@local_object = options[:local_object]
|
12
|
+
|
13
|
+
if @layout = options[:layout]
|
14
|
+
raise LayoutNotFoundError.new(@layout) unless File.exists?(@layout)
|
15
|
+
end
|
16
|
+
|
17
|
+
if @layout || @local_object
|
18
|
+
@document = Nokogiri::HTML.fragment(html)
|
19
|
+
else
|
20
|
+
@document = Nokogiri::HTML(html)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(current_scope)
|
25
|
+
set_scope(current_scope)
|
26
|
+
|
27
|
+
parse_doc(@document)
|
28
|
+
|
29
|
+
if @layout
|
30
|
+
render_with_layout
|
31
|
+
else
|
32
|
+
document.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# The _render_ method is used within a layout to inject
|
37
|
+
# the results of the template render.
|
38
|
+
#
|
39
|
+
# Ruhl::Engine.new(html, :layout => path_to_layout).render(self)
|
40
|
+
def _render_
|
41
|
+
document.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def render_with_layout
|
47
|
+
render_file( File.read(@layout) )
|
48
|
+
end
|
49
|
+
|
50
|
+
def render_partial(tag, code)
|
51
|
+
file = execute_ruby(tag, code)
|
52
|
+
raise PartialNotFoundError.new(file) unless File.exists?(file)
|
53
|
+
render_file( File.read(file) )
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_collection(tag, code)
|
57
|
+
results = execute_ruby(tag, code)
|
58
|
+
|
59
|
+
html = tag.to_html
|
60
|
+
|
61
|
+
new_content = results.collect do |item|
|
62
|
+
Ruhl::Engine.new(html, :local_object => item).render(scope)
|
63
|
+
end.to_s
|
64
|
+
|
65
|
+
tag.swap(new_content)
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_file(contents)
|
69
|
+
doc = Nokogiri::HTML( contents )
|
70
|
+
parse_doc(doc)
|
71
|
+
doc.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
def parse_doc(doc)
|
75
|
+
if (nodes = doc.xpath('//*[@data-ruhl][1]')).empty?
|
76
|
+
nodes = doc.search('*[@data-ruhl]')
|
77
|
+
end
|
78
|
+
|
79
|
+
return if nodes.empty?
|
80
|
+
|
81
|
+
tag = nodes.first
|
82
|
+
code = tag.remove_attribute('data-ruhl')
|
83
|
+
process_attribute(tag, code.value)
|
84
|
+
parse_doc(doc)
|
85
|
+
end
|
86
|
+
|
87
|
+
def process_attribute(tag, code)
|
88
|
+
code.split(',').each do |pair|
|
89
|
+
attribute, value = pair.split(':')
|
90
|
+
|
91
|
+
if value.nil?
|
92
|
+
tag.inner_html = execute_ruby(tag, attribute)
|
93
|
+
else
|
94
|
+
value.strip!
|
95
|
+
if attribute == "_partial"
|
96
|
+
tag.inner_html = render_partial(tag, value)
|
97
|
+
elsif attribute == "_collection"
|
98
|
+
render_collection(tag, value)
|
99
|
+
elsif attribute == "_if"
|
100
|
+
contents = execute_ruby(tag, value)
|
101
|
+
if contents
|
102
|
+
tag.inner_html = contents
|
103
|
+
else
|
104
|
+
tag.remove
|
105
|
+
end
|
106
|
+
elsif attribute == "_render_if"
|
107
|
+
unless execute_ruby(tag, value)
|
108
|
+
tag.remove
|
109
|
+
end
|
110
|
+
else
|
111
|
+
tag[attribute] = execute_ruby(tag, value)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def execute_ruby(tag, code)
|
118
|
+
unless code == '_render_'
|
119
|
+
if local_object && local_object.respond_to?(code)
|
120
|
+
local_object.send(code)
|
121
|
+
else
|
122
|
+
scope.send(code, tag)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
_render_
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def set_scope(current_scope)
|
130
|
+
raise Ruhl::NoScopeError unless current_scope
|
131
|
+
@scope = current_scope
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
data/lib/ruhl/errors.rb
ADDED
data/lib/ruhl/rails.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ruhl
|
2
|
+
class Plugin < ActionView::TemplateHandler
|
3
|
+
|
4
|
+
def initialize(action_view)
|
5
|
+
@action_view = action_view
|
6
|
+
end
|
7
|
+
|
8
|
+
def render(template, options)
|
9
|
+
layout = @action_view.controller.send(:active_layout)
|
10
|
+
|
11
|
+
puts "==========> @action_view: #{@action_view.controller.response.inspect}"
|
12
|
+
puts "==========> template: #{template.inspect}"
|
13
|
+
puts "==========> options: #{options.inspect}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
ActionView::Template.register_template_handler(:ruhl, Ruhl::Plugin)
|
19
|
+
|
20
|
+
ActionView::Template.exempt_from_layout(:ruhl)
|
21
|
+
|
data/lib/ruhl/sinatra.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'ruhl'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
module Templates
|
5
|
+
def ruhl(template, options = {}, locals = {})
|
6
|
+
require_warn('Ruhl') unless defined?(::Ruhl::Engine)
|
7
|
+
|
8
|
+
render :ruhl, template, options, locals
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def render(engine, template, options={}, locals={})
|
14
|
+
# merge app-level options
|
15
|
+
options = self.class.send(engine).merge(options) if self.class.respond_to?(engine)
|
16
|
+
|
17
|
+
views = options.delete(:views) || self.class.views || "./views"
|
18
|
+
|
19
|
+
# render template
|
20
|
+
data, options[:filename], options[:line] = lookup_template(engine, template, views)
|
21
|
+
|
22
|
+
__send__("render_#{engine}", template, data, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_ruhl(template, data, options)
|
26
|
+
::Ruhl::Engine.new(data, options).render(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/ruhl.gemspec
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{ruhl}
|
8
|
+
s.version = "0.6.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Andrew Stone"]
|
12
|
+
s.date = %q{2009-10-07}
|
13
|
+
s.description = %q{Make your HTML dynamic with the addition of a ruby attribute.}
|
14
|
+
s.email = %q{andy@stonean.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"README",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"lib/ruhl.rb",
|
24
|
+
"lib/ruhl/errors.rb",
|
25
|
+
"lib/ruhl/rails.rb",
|
26
|
+
"lib/ruhl/sinatra.rb",
|
27
|
+
"ruhl.gemspec",
|
28
|
+
"spec/html/basic.html",
|
29
|
+
"spec/html/fragment.html",
|
30
|
+
"spec/html/if.html",
|
31
|
+
"spec/html/layout.html",
|
32
|
+
"spec/html/main_with_sidebar.html",
|
33
|
+
"spec/html/medium.html",
|
34
|
+
"spec/html/render_if.html",
|
35
|
+
"spec/html/seo.html",
|
36
|
+
"spec/html/sidebar.html",
|
37
|
+
"spec/rcov.opts",
|
38
|
+
"spec/ruhl_spec.rb",
|
39
|
+
"spec/spec.opts",
|
40
|
+
"spec/spec_helper.rb"
|
41
|
+
]
|
42
|
+
s.homepage = %q{http://github.com/stonean/ruhl}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.5}
|
46
|
+
s.summary = %q{Ruby Hypertext Language}
|
47
|
+
s.test_files = [
|
48
|
+
"spec/ruhl_spec.rb",
|
49
|
+
"spec/spec_helper.rb"
|
50
|
+
]
|
51
|
+
|
52
|
+
if s.respond_to? :specification_version then
|
53
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
|
+
s.specification_version = 3
|
55
|
+
|
56
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
57
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.3.3"])
|
58
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<nokogiri>, [">= 1.3.3"])
|
61
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
62
|
+
end
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<nokogiri>, [">= 1.3.3"])
|
65
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Hello World</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1 data-ruhl="generate_h1">I am a templated headline</h1>
|
7
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sit amet lacinia metus. Nam sed dui est. Sed pellentesque aliquet massa, vel cursus arcu faucibus tincidunt. Nunc aliquet ultricies tellus sit amet elementum. Integer porttitor lorem dolor. Proin leo nunc, sollicitudin sed ullamcorper non, tempus non erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed commodo sodales pharetra. Vivamus pharetra, augue a volutpat scelerisque, elit nisl auctor elit, quis mollis dolor urna in ligula. Aliquam nisi augue, adipiscing quis mollis ut, aliquam nec urna. Morbi faucibus semper ante ut dignissim. Maecenas et dui eros, eget tristique odio. Sed vehicula erat nec dui porttitor laoreet.</p>
|
8
|
+
</body>
|
9
|
+
</html>
|
@@ -0,0 +1,2 @@
|
|
1
|
+
<h1 data-ruhl="generate_h1">I am a templated headline</h1>
|
2
|
+
<p data-ruhl="my_content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sit amet lacinia metus. Nam sed dui est. Sed pellentesque aliquet massa, vel cursus arcu faucibus tincidunt. Nunc aliquet ultricies tellus sit amet elementum. Integer porttitor lorem dolor. Proin leo nunc, sollicitudin sed ullamcorper non, tempus non erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed commodo sodales pharetra. Vivamus pharetra, augue a volutpat scelerisque, elit nisl auctor elit, quis mollis dolor urna in ligula. Aliquam nisi augue, adipiscing quis mollis ut, aliquam nec urna. Morbi faucibus semper ante ut dignissim. Maecenas et dui eros, eget tristique odio. Sed vehicula erat nec dui porttitor laoreet.</p>
|
data/spec/html/if.html
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is a title template</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>This is the header template</h1>
|
7
|
+
<table>
|
8
|
+
<thead>
|
9
|
+
<tr>
|
10
|
+
<td>First Name</td>
|
11
|
+
<td>Last Name</td>
|
12
|
+
<td>Email</td>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tr data-ruhl="_collection: user_list">
|
16
|
+
<td data-ruhl="first_name">Andrew</td>
|
17
|
+
<td data-ruhl="last_name">Stone</td>
|
18
|
+
<td data-ruhl="_if: email">andy@stonean.com</td>
|
19
|
+
</tr>
|
20
|
+
</table>
|
21
|
+
</ul>
|
22
|
+
</body>
|
23
|
+
</html>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is a title template</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<div id="wrap">
|
7
|
+
<div id="sidebar" data-ruhl="_partial: sidebar_partial">
|
8
|
+
<h3>Sidebar links</h3>
|
9
|
+
<ul>
|
10
|
+
<li><a href="#">Link 1</a></li>
|
11
|
+
<li><a href="#">Link 2</a></li>
|
12
|
+
<li><a href="#">Link 3</a></li>
|
13
|
+
<li><a href="#">Link 4</a></li>
|
14
|
+
</ul>
|
15
|
+
</div>
|
16
|
+
<div id="main">
|
17
|
+
<h1> My main content</h1>
|
18
|
+
<p>Text designers would put here to test their layout</p>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</body>
|
22
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is a title template</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>This is the header template</h1>
|
7
|
+
<table>
|
8
|
+
<thead>
|
9
|
+
<tr>
|
10
|
+
<td>First Name</td>
|
11
|
+
<td>Last Name</td>
|
12
|
+
<td>Email</td>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tr data-ruhl="_collection: user_list">
|
16
|
+
<td data-ruhl="first_name">Andrew</td>
|
17
|
+
<td data-ruhl="last_name">Stone</td>
|
18
|
+
<td data-ruhl="email">andy@stonean.com</td>
|
19
|
+
</tr>
|
20
|
+
</table>
|
21
|
+
</ul>
|
22
|
+
</body>
|
23
|
+
</html>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is a title template</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>This is the header template</h1>
|
7
|
+
<div data-ruhl="_render_if: has_users?">
|
8
|
+
<table>
|
9
|
+
<thead>
|
10
|
+
<tr>
|
11
|
+
<td>First Name</td>
|
12
|
+
<td>Last Name</td>
|
13
|
+
<td>Email</td>
|
14
|
+
</tr>
|
15
|
+
</thead>
|
16
|
+
<tr data-ruhl="_collection: user_list">
|
17
|
+
<td data-ruhl="first_name">Andrew</td>
|
18
|
+
<td data-ruhl="last_name">Stone</td>
|
19
|
+
<td data-ruhl="email">andy@stonean.com</td>
|
20
|
+
</tr>
|
21
|
+
</table>
|
22
|
+
</div>
|
23
|
+
</ul>
|
24
|
+
</body>
|
25
|
+
</html>
|
data/spec/html/seo.html
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>This is a title template</title>
|
4
|
+
<meta data-ruhl="content: generate_description" content='This is a description template' id='metaDescription' name='description' />
|
5
|
+
<meta data-ruhl="content: generate_keywords" content='these,are,template,keywords' id='metaKeywords' name='keywords' />
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<h1 data-ruhl="generate_h1">I am a templated headline</h1>
|
9
|
+
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sit amet lacinia metus. Nam sed dui est. Sed pellentesque aliquet massa, vel cursus arcu faucibus tincidunt. Nunc aliquet ultricies tellus sit amet elementum. Integer porttitor lorem dolor. Proin leo nunc, sollicitudin sed ullamcorper non, tempus non erat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed commodo sodales pharetra. Vivamus pharetra, augue a volutpat scelerisque, elit nisl auctor elit, quis mollis dolor urna in ligula. Aliquam nisi augue, adipiscing quis mollis ut, aliquam nec urna. Morbi faucibus semper ante ut dignissim. Maecenas et dui eros, eget tristique odio. Sed vehicula erat nec dui porttitor laoreet.</p>
|
10
|
+
</body>
|
11
|
+
</html>
|
data/spec/rcov.opts
ADDED
data/spec/ruhl_spec.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
def generate_h1(tag = nil)
|
4
|
+
"data from presenter"
|
5
|
+
end
|
6
|
+
|
7
|
+
def data_from_method(tag = nil)
|
8
|
+
"I am data from a method"
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_description(tag = nil)
|
12
|
+
"I am a custom meta description"
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_keywords(tag = nil)
|
16
|
+
"I, am, custom, keywords"
|
17
|
+
end
|
18
|
+
|
19
|
+
def my_content(tag = nil)
|
20
|
+
"hello from my content."
|
21
|
+
end
|
22
|
+
|
23
|
+
def sidebar_partial(tag = nil)
|
24
|
+
html(:sidebar)
|
25
|
+
end
|
26
|
+
|
27
|
+
def user_list(tag = nil)
|
28
|
+
[
|
29
|
+
TestUser.new('Jane', 'Doe', 'jane@stonean.com'),
|
30
|
+
TestUser.new('John', 'Joe', 'john@stonean.com'),
|
31
|
+
TestUser.new('Jake', 'Smo', 'jake@stonean.com'),
|
32
|
+
TestUser.new('Paul', 'Tin', 'paul@stonean.com'),
|
33
|
+
TestUser.new('NoMail', 'Man')
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
describe Ruhl do
|
38
|
+
|
39
|
+
describe "basic.html" do
|
40
|
+
before do
|
41
|
+
@html = File.read html(:basic)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "content of p should be content from data_from_method" do
|
45
|
+
doc = create_doc
|
46
|
+
doc.xpath('//h1').first.content.should == generate_h1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "seo.html" do
|
51
|
+
before do
|
52
|
+
@html = File.read html(:seo)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "meta keywords should be replaced" do
|
56
|
+
doc = create_doc
|
57
|
+
doc.xpath('//meta[@name="keywords"]').first['content'].
|
58
|
+
should == generate_keywords
|
59
|
+
end
|
60
|
+
|
61
|
+
it "meta title should be replaced" do
|
62
|
+
doc = create_doc
|
63
|
+
doc.xpath('//meta[@name="description"]').first['content'].
|
64
|
+
should == generate_description
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "medium.html" do
|
69
|
+
before do
|
70
|
+
@html = File.read html(:medium)
|
71
|
+
@doc = create_doc
|
72
|
+
end
|
73
|
+
|
74
|
+
it "first data row should equal first user " do
|
75
|
+
table = @doc.xpath('/html/body/table/tr//td')
|
76
|
+
table.children[0].to_s.should == "Jane"
|
77
|
+
table.children[1].to_s.should == "Doe"
|
78
|
+
table.children[2].to_s.should == "jane@stonean.com"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "last data row should equal last user " do
|
82
|
+
table = @doc.xpath('/html/body/table/tr//td')
|
83
|
+
table.children[9].to_s.should == "Paul"
|
84
|
+
table.children[10].to_s.should == "Tin"
|
85
|
+
table.children[11].to_s.should == "paul@stonean.com"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "fragment.html" do
|
90
|
+
before do
|
91
|
+
@html = File.read html(:fragment)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "will be injected into layout.html" do
|
95
|
+
doc = create_doc( html(:layout) )
|
96
|
+
doc.xpath('//h1').should_not be_empty
|
97
|
+
doc.xpath('//p').should_not be_empty
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "main_with_sidebar.html" do
|
102
|
+
before do
|
103
|
+
@html = File.read html(:main_with_sidebar)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should replace sidebar with partial contents" do
|
107
|
+
doc = create_doc
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "if.html" do
|
112
|
+
before do
|
113
|
+
@html = File.read html(:if)
|
114
|
+
@doc = create_doc
|
115
|
+
end
|
116
|
+
|
117
|
+
it "first data row should equal first user " do
|
118
|
+
table = @doc.xpath('/html/body/table/tr//td')
|
119
|
+
table.children[0].to_s.should == "Jane"
|
120
|
+
table.children[1].to_s.should == "Doe"
|
121
|
+
table.children[2].to_s.should == "jane@stonean.com"
|
122
|
+
end
|
123
|
+
|
124
|
+
it "last data row should equal last user " do
|
125
|
+
table = @doc.xpath('/html/body/table/tr//td')
|
126
|
+
table.children[12].to_s.should == "NoMail"
|
127
|
+
table.children[13].to_s.should == "Man"
|
128
|
+
table.children[14].should == nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "render if.html" do
|
133
|
+
it "table should not render" do
|
134
|
+
def has_users?(tag = nil)
|
135
|
+
false
|
136
|
+
end
|
137
|
+
|
138
|
+
@html = File.read html(:render_if)
|
139
|
+
@doc = create_doc
|
140
|
+
nodes = @doc.xpath('/html/body//*')
|
141
|
+
nodes.children.length.should == 1
|
142
|
+
nodes.children.to_s.should == "This is the header template"
|
143
|
+
end
|
144
|
+
|
145
|
+
it "table shouldrender" do
|
146
|
+
def has_users?(tag = nil)
|
147
|
+
true
|
148
|
+
end
|
149
|
+
|
150
|
+
@html = File.read html(:render_if)
|
151
|
+
@doc = create_doc
|
152
|
+
nodes = @doc.xpath('/html/body//*')
|
153
|
+
nodes.children.length.should > 1
|
154
|
+
|
155
|
+
table = @doc.xpath('/html/body/div/table/tr//td')
|
156
|
+
table.children[12].to_s.should == "NoMail"
|
157
|
+
table.children[13].to_s.should == "Man"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), %w[.. lib ruhl]))
|
2
|
+
|
3
|
+
def html(name)
|
4
|
+
File.join( File.dirname(__FILE__), 'html', "#{name}.html" )
|
5
|
+
end
|
6
|
+
|
7
|
+
def do_parse(html)
|
8
|
+
Nokogiri::HTML(html)
|
9
|
+
end
|
10
|
+
|
11
|
+
def create_doc(layout = nil)
|
12
|
+
options = {:layout => layout}
|
13
|
+
html = Ruhl::Engine.new(@html, :layout => layout).render(self)
|
14
|
+
do_parse(html)
|
15
|
+
end
|
16
|
+
|
17
|
+
class TestUser
|
18
|
+
attr_accessor :first_name, :last_name, :email
|
19
|
+
|
20
|
+
def initialize(first, last , email = nil)
|
21
|
+
@first_name = first
|
22
|
+
@last_name = last
|
23
|
+
@email = email
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruhl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Stone
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-10-07 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: nokogiri
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.3.3
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: Make your HTML dynamic with the addition of a ruby attribute.
|
36
|
+
email: andy@stonean.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- README
|
46
|
+
- Rakefile
|
47
|
+
- VERSION
|
48
|
+
- lib/ruhl.rb
|
49
|
+
- lib/ruhl/errors.rb
|
50
|
+
- lib/ruhl/rails.rb
|
51
|
+
- lib/ruhl/sinatra.rb
|
52
|
+
- ruhl.gemspec
|
53
|
+
- spec/html/basic.html
|
54
|
+
- spec/html/fragment.html
|
55
|
+
- spec/html/if.html
|
56
|
+
- spec/html/layout.html
|
57
|
+
- spec/html/main_with_sidebar.html
|
58
|
+
- spec/html/medium.html
|
59
|
+
- spec/html/render_if.html
|
60
|
+
- spec/html/seo.html
|
61
|
+
- spec/html/sidebar.html
|
62
|
+
- spec/rcov.opts
|
63
|
+
- spec/ruhl_spec.rb
|
64
|
+
- spec/spec.opts
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
has_rdoc: true
|
67
|
+
homepage: http://github.com/stonean/ruhl
|
68
|
+
licenses: []
|
69
|
+
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options:
|
72
|
+
- --charset=UTF-8
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: "0"
|
80
|
+
version:
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: "0"
|
86
|
+
version:
|
87
|
+
requirements: []
|
88
|
+
|
89
|
+
rubyforge_project:
|
90
|
+
rubygems_version: 1.3.5
|
91
|
+
signing_key:
|
92
|
+
specification_version: 3
|
93
|
+
summary: Ruby Hypertext Language
|
94
|
+
test_files:
|
95
|
+
- spec/ruhl_spec.rb
|
96
|
+
- spec/spec_helper.rb
|