hoshi 0.0.186
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +34 -0
- data/bin/html2hoshi +65 -0
- data/doc/LICENSE +19 -0
- data/doc/README +180 -0
- data/doc/TODO +10 -0
- data/doc/examples/blocks.rb +18 -0
- data/doc/examples/env.cgi +42 -0
- data/doc/examples/feed.rb +40 -0
- data/doc/examples/layouts.rb +60 -0
- data/doc/examples/rss2.rb +52 -0
- data/doc/examples/trivial.rb +21 -0
- data/lib/hoshi/monkey_patches.rb +8 -0
- data/lib/hoshi/tag.rb +42 -0
- data/lib/hoshi/view/html.rb +16 -0
- data/lib/hoshi/view/html3.rb +7 -0
- data/lib/hoshi/view/html4.rb +17 -0
- data/lib/hoshi/view/html4_frameset.rb +8 -0
- data/lib/hoshi/view/html4_transitional.rb +8 -0
- data/lib/hoshi/view/rss2.rb +56 -0
- data/lib/hoshi/view/xhtml.rb +9 -0
- data/lib/hoshi/view/xhtml1.rb +17 -0
- data/lib/hoshi/view/xhtml1_frameset.rb +16 -0
- data/lib/hoshi/view/xhtml1_strict.rb +8 -0
- data/lib/hoshi/view/xhtml1_transitional.rb +9 -0
- data/lib/hoshi/view/xhtml2.rb +7 -0
- data/lib/hoshi/view.rb +205 -0
- data/lib/hoshi.rb +20 -0
- data/lib/html2hoshi.rb +85 -0
- metadata +104 -0
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rake/gempackagetask'
|
2
|
+
require 'rake/rdoctask'
|
3
|
+
|
4
|
+
|
5
|
+
spec = Gem::Specification.new { |s|
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
|
8
|
+
s.author = "Pete Elmore"
|
9
|
+
s.email = "pete.elmore@gmail.com"
|
10
|
+
s.files = Dir["{lib,doc,bin,ext}/**/*"].delete_if {|f|
|
11
|
+
/\/rdoc(\/|$)/i.match f
|
12
|
+
} + %w(Rakefile)
|
13
|
+
s.require_path = 'lib'
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.extra_rdoc_files = Dir['doc/*'].select(&File.method(:file?))
|
16
|
+
s.extensions << 'ext/extconf.rb' if File.exist? 'ext/extconf.rb'
|
17
|
+
Dir['bin/*'].map(&File.method(:basename)).map(&s.executables.method(:<<))
|
18
|
+
|
19
|
+
s.name = 'hoshi'
|
20
|
+
s.rubyforge_project = 'hoshi-view'
|
21
|
+
s.summary = "Nice, object-oriented, first-class views."
|
22
|
+
s.homepage = "http://debu.gs/#{s.name}"
|
23
|
+
%w(metaid hpricot).each &s.method(:add_dependency)
|
24
|
+
s.version = '0.0.186'
|
25
|
+
}
|
26
|
+
|
27
|
+
Rake::GemPackageTask.new(spec) { |pkg|
|
28
|
+
pkg.need_tar_bz2 = true
|
29
|
+
}
|
30
|
+
|
31
|
+
task(:install => :package) {
|
32
|
+
g = "pkg/#{spec.name}-#{spec.version}.gem"
|
33
|
+
system "gem install -l #{g}"
|
34
|
+
}
|
data/bin/html2hoshi
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# A script that turns HTML into a Hoshi method. Useful to turn some HTML
|
4
|
+
# (generated or not) into Ruby so that you can add some code to it or just for
|
5
|
+
# readability.
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
|
10
|
+
%w(
|
11
|
+
html2hoshi
|
12
|
+
).each &method(:require)
|
13
|
+
|
14
|
+
if ARGV.empty?
|
15
|
+
$stderr.puts "Usage: #{$0} html_file [output_file]\nFor either input " \
|
16
|
+
"or output, using - as the filename does the usual thing."
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
html_filename = ARGV.shift
|
21
|
+
html_file =
|
22
|
+
if html_filename == '-'
|
23
|
+
html_file = $stdin
|
24
|
+
elsif File.exist?(html_filename)
|
25
|
+
File.open html_filename
|
26
|
+
else
|
27
|
+
$stderr.puts "Apparently, #{html_filename} does not exist."
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
x = ARGV.shift
|
32
|
+
if x.nil? || x == '-'
|
33
|
+
output = File.basename(html_filename, '.html')
|
34
|
+
output = 'unnamed_view' if output == '-'
|
35
|
+
outfile = $stdout
|
36
|
+
else
|
37
|
+
output = File.basename(x, '.rb')
|
38
|
+
outfile = File.open(x, 'w')
|
39
|
+
end
|
40
|
+
|
41
|
+
class_name = output.capitalize.gsub(/_(\w)/) { $1.upcase }
|
42
|
+
indent = "\t" # TODO: Make it a command-line option.
|
43
|
+
|
44
|
+
hoshi = Hoshi.from_html html_file.read, indent
|
45
|
+
|
46
|
+
outfile.print(<<EOHOSHI)
|
47
|
+
#!/usr/bin/env ruby
|
48
|
+
|
49
|
+
require 'rubygems'
|
50
|
+
require 'hoshi'
|
51
|
+
|
52
|
+
# Generated with html2hoshi. You will likely want to change the class name,
|
53
|
+
# method name, and class that it inherits from.
|
54
|
+
class #{class_name} < Hoshi::View :html
|
55
|
+
#{indent}permissive!
|
56
|
+
#{hoshi}
|
57
|
+
end
|
58
|
+
|
59
|
+
# This file can also be run stand-alone:
|
60
|
+
if __FILE__ == $0
|
61
|
+
#{indent}require 'cgi'
|
62
|
+
#{indent}puts CGI.pretty(#{class_name}.new.page)
|
63
|
+
end
|
64
|
+
EOHOSHI
|
65
|
+
outfile.close
|
data/doc/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008 Peter Elmore (pete.elmore at gmail.com)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
copy of this software and associated documentation files (the "Software"),
|
5
|
+
to deal in the Software without restriction, including without limitation
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
data/doc/README
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
= Hoshi
|
2
|
+
|
3
|
+
== Summary
|
4
|
+
|
5
|
+
Hoshi is a library for creating real first-class HTML/XML views. So,
|
6
|
+
unlike template libraries, you can take advantage of mixins,
|
7
|
+
inheritance, and all the other wonderful features of Ruby's object
|
8
|
+
system. There is also support for easy RSS feeds and CGI.
|
9
|
+
|
10
|
+
Hoshi is designed to:
|
11
|
+
* Generate clean HTML/XHTML/XML with minimal effort
|
12
|
+
* Be easy for a coder to use and understand
|
13
|
+
* Take full advantage of Ruby's object sytem
|
14
|
+
* Be more readable and easier to write than bare HTML
|
15
|
+
|
16
|
+
It is semi-modeled after Markaby, but with a much more straightforward
|
17
|
+
implementation and different semantics (e.g., no instance_eval, so scope
|
18
|
+
inside a tag is as expected). Hoshi also allows a tag to follow another
|
19
|
+
tag without requiring any <<, +, or +=.
|
20
|
+
|
21
|
+
== Installation
|
22
|
+
|
23
|
+
You can install via rubygems,
|
24
|
+
|
25
|
+
gem install hoshi
|
26
|
+
|
27
|
+
or by downloading from github (http://github.com/pete/hoshi).
|
28
|
+
|
29
|
+
== Usage
|
30
|
+
|
31
|
+
These examples and more featured in the fabulous doc/examples directory.
|
32
|
+
Also, there is a program included called html2hoshi (and associated
|
33
|
+
lib/html2hoshi.rb; see Hoshi.from_html) that takes HTML as input and
|
34
|
+
converts it to Ruby code using Hoshi.
|
35
|
+
|
36
|
+
=== Class-based
|
37
|
+
|
38
|
+
These should be fairly straightforward:
|
39
|
+
|
40
|
+
require 'hoshi'
|
41
|
+
|
42
|
+
class Trivial < Hoshi::View :html4
|
43
|
+
def show
|
44
|
+
doctype
|
45
|
+
html {
|
46
|
+
head {
|
47
|
+
title "Hello, world!"
|
48
|
+
link :rel => 'stylesheet', :href => '/css/hoshi.css'
|
49
|
+
}
|
50
|
+
|
51
|
+
body {
|
52
|
+
h1 "Hello, world!"
|
53
|
+
p "This is a greeting to the world."
|
54
|
+
}
|
55
|
+
}
|
56
|
+
render
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
puts Trivial.new.show
|
61
|
+
|
62
|
+
You can get a little more complicated:
|
63
|
+
|
64
|
+
require 'hoshi'
|
65
|
+
require 'cgi'
|
66
|
+
|
67
|
+
module Layout
|
68
|
+
def main_page(t)
|
69
|
+
doctype
|
70
|
+
html {
|
71
|
+
head {
|
72
|
+
title t
|
73
|
+
script(:type => 'text/javascript') {
|
74
|
+
raw "alert(\"Hi, I'm some javascript, I suppose.\");"
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
body {
|
79
|
+
h1 t, :class => 'page_title'
|
80
|
+
|
81
|
+
yield
|
82
|
+
}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def list_page(t)
|
87
|
+
main_page(t) {
|
88
|
+
ul {
|
89
|
+
yield
|
90
|
+
}
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
class Fibonacci < Hoshi::View :xhtml1
|
97
|
+
include Layout
|
98
|
+
|
99
|
+
def list_page(n)
|
100
|
+
super("Fibonacci: f(0)..f(#{n})") {
|
101
|
+
fib_upto(n).map { |i| li i.to_s }
|
102
|
+
}
|
103
|
+
CGI.pretty(render)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def fib_upto n
|
109
|
+
a = Array.new(n)
|
110
|
+
|
111
|
+
0.upto(n) { |i|
|
112
|
+
a[i] =
|
113
|
+
if i < 2
|
114
|
+
1
|
115
|
+
else
|
116
|
+
a[i - 1] + a[i - 2]
|
117
|
+
end
|
118
|
+
}
|
119
|
+
|
120
|
+
a
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
puts Fibonacci.new.list_page(n)
|
125
|
+
|
126
|
+
=== Block-based
|
127
|
+
|
128
|
+
For simpler cases where you only intend to produce markup, perhaps for use as a templating engine.
|
129
|
+
|
130
|
+
require 'hoshi'
|
131
|
+
|
132
|
+
str = Hoshi::View::HTML4.build {
|
133
|
+
doctype
|
134
|
+
html {
|
135
|
+
head {
|
136
|
+
title "Hello, world!"
|
137
|
+
link :rel => 'stylesheet', :href => '/css/hoshi.css'
|
138
|
+
}
|
139
|
+
|
140
|
+
body {
|
141
|
+
h1 "Hello, world!"
|
142
|
+
p "This is a greeting to the world."
|
143
|
+
}
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
puts str
|
148
|
+
|
149
|
+
== Bugs
|
150
|
+
|
151
|
+
There needs to be some work done on correcting the tags.
|
152
|
+
|
153
|
+
== Credits
|
154
|
+
|
155
|
+
Author:
|
156
|
+
Pete Elmore -- (pete.elmore(a)gmail.com)
|
157
|
+
|
158
|
+
Initial Design:
|
159
|
+
Dan Yoder -- (danyoder(a)mac.com)
|
160
|
+
|
161
|
+
Simple block version:
|
162
|
+
Nolan Darilek -- (nolan(a)thewordnerd.info)
|
163
|
+
|
164
|
+
Homie that be lookin' out for my broken deps:
|
165
|
+
Lars Lethonen
|
166
|
+
|
167
|
+
The guys that paid me to do this:
|
168
|
+
AT&T Interactive. (By the way, email me if you want to come work here.)
|
169
|
+
|
170
|
+
Also, I guess I should credit Attractive Eighties Women
|
171
|
+
(http://attractiveeightieswomen.com/), since I was blasting them the
|
172
|
+
whole time I was developing this. Like, over and over. I couldn't stop
|
173
|
+
listening. My friends and family are becoming concerned. I don't feel
|
174
|
+
that I am yet ready to take the first step by admitting there is a
|
175
|
+
problem. Intervention may be required. This paragraph should probably
|
176
|
+
be considered a cry for help.
|
177
|
+
|
178
|
+
== Home page
|
179
|
+
|
180
|
+
http://debu.gs/hoshi
|
data/doc/TODO
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
TODO for 1.0:
|
2
|
+
* Separate the tags by close type in the various View sub-classes. (The most
|
3
|
+
tedious thing on the list, but required for compliance.)
|
4
|
+
* Still not too happy about the way RSS feeds are done, going to rework.
|
5
|
+
|
6
|
+
TODO for later:
|
7
|
+
* Have html2hoshi do a smarter job of deciding doctypes, not using permissive!
|
8
|
+
so much.
|
9
|
+
* View logic helpers.
|
10
|
+
* Ooh, ooh, SVG!
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'hoshi'
|
2
|
+
|
3
|
+
str = Hoshi::View::HTML4.build {
|
4
|
+
doctype
|
5
|
+
html {
|
6
|
+
head {
|
7
|
+
title "Hello, world!"
|
8
|
+
link :rel => 'stylesheet', :href => '/css/hoshi.css'
|
9
|
+
}
|
10
|
+
|
11
|
+
body {
|
12
|
+
h1 "Hello, world!"
|
13
|
+
p "This is a greeting to the world."
|
14
|
+
}
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
puts str
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Perl, eat your heart out.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'hoshi'
|
7
|
+
|
8
|
+
qstring = ENV.delete 'QUERY_STRING'
|
9
|
+
query = CGI.parse qstring if qstring
|
10
|
+
|
11
|
+
Hoshi::View(:html4) {
|
12
|
+
doc {
|
13
|
+
head { title "env.cgi, just like Mom used to make" }
|
14
|
+
body {
|
15
|
+
h1 "CGI Environment:"
|
16
|
+
ul {
|
17
|
+
ENV.to_a.sort.map { |k,v|
|
18
|
+
li CGI.escapeHTML("#{k} => #{v}")
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
if query
|
23
|
+
h1 "Arguments:"
|
24
|
+
ul {
|
25
|
+
query.sort.map { |k,v|
|
26
|
+
li {
|
27
|
+
safe "#{k} = "
|
28
|
+
if v.size == 1
|
29
|
+
safe v
|
30
|
+
else
|
31
|
+
ul { v.map { |sv| li { safe sv } } }
|
32
|
+
end
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
else
|
37
|
+
h1 "No Arguments"
|
38
|
+
end
|
39
|
+
}
|
40
|
+
}
|
41
|
+
render_cgi
|
42
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoshi'
|
5
|
+
|
6
|
+
class Feed < Hoshi::View :html
|
7
|
+
permissive!
|
8
|
+
def show
|
9
|
+
raw "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
10
|
+
raw "<?xml-stylesheet href=\"http://feeds.feedburner.com/~d/styles/rss2full.xsl\" type=\"text/xsl\" media=\"screen\"?>"
|
11
|
+
raw "<?xml-stylesheet href=\"http://feeds.feedburner.com/~d/styles/itemcontent.css\" type=\"text/css\" media=\"screen\"?>"
|
12
|
+
rss("xmlns:wfw"=>"http://wellformedweb.org/CommentAPI/", "xmlns:atom"=>"http://www.w3.org/2005/Atom", "version"=>"2.0", "xmlns:content"=>"http://purl.org/rss/1.0/modules/content/", "xmlns:dc"=>"http://purl.org/dc/elements/1.1/") {
|
13
|
+
channel {
|
14
|
+
title "Debugs"
|
15
|
+
link "http://debu.gs/blog/debugs.rss"
|
16
|
+
description
|
17
|
+
language "en"
|
18
|
+
tag "atom10:link", nil, "href"=>"http://feeds.feedburner.com/debugs", "rel"=>"self", "xmlns:atom10"=>"http://www.w3.org/2005/Atom", "type"=>"application/rss+xml"
|
19
|
+
item {
|
20
|
+
title "Hoshi 0.1.0 Released...Soon"
|
21
|
+
link "http://debu.gs/hoshi-010-released"
|
22
|
+
pubDate "Thu, 06 Nov 2008 00:00:00 +0000"
|
23
|
+
description "Bringing first-class views to Ruby."
|
24
|
+
}
|
25
|
+
item {
|
26
|
+
title "LiveConsole 0.2.0 Released"
|
27
|
+
link "http://debu.gs/liveconsole-020-released"
|
28
|
+
pubDate "Thu, 16 Oct 2008 00:00:00 +0000"
|
29
|
+
description "LiveConsole 0.2.0 released with support for Unix Domain Sockets and arbitrary bindings."
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
render
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if __FILE__ == $0
|
38
|
+
require 'cgi'
|
39
|
+
puts CGI.pretty(Feed.new.show)
|
40
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'hoshi'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Layout
|
5
|
+
def main_page(t)
|
6
|
+
doctype
|
7
|
+
html {
|
8
|
+
head {
|
9
|
+
title t
|
10
|
+
script(:type => 'text/javascript') {
|
11
|
+
raw "alert(\"Hi, I'm some javascript, I suppose.\");"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
body {
|
16
|
+
h1 t, :class => 'page_title'
|
17
|
+
yield
|
18
|
+
}
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def list_page(t)
|
23
|
+
main_page(t) {
|
24
|
+
ul {
|
25
|
+
yield
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
class Fibonacci < Hoshi::View :xhtml1
|
33
|
+
include Layout
|
34
|
+
|
35
|
+
def list_page(n)
|
36
|
+
super("Fibonacci: f(0)..f(#{n})") {
|
37
|
+
fib_upto(n).map { |i| li i.to_s }
|
38
|
+
}
|
39
|
+
CGI.pretty(render)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def fib_upto n
|
45
|
+
a = Array.new(n)
|
46
|
+
|
47
|
+
0.upto(n) { |i|
|
48
|
+
a[i] =
|
49
|
+
if i < 2
|
50
|
+
1
|
51
|
+
else
|
52
|
+
a[i - 1] + a[i - 2]
|
53
|
+
end
|
54
|
+
}
|
55
|
+
|
56
|
+
a
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
puts Fibonacci.new.list_page(10)
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'hoshi'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class BlogFeed < Hoshi::View :rss2
|
5
|
+
def initialize(blog)
|
6
|
+
super()
|
7
|
+
|
8
|
+
# This will generate the inside of the channel tag.
|
9
|
+
def_channel {
|
10
|
+
title blog.title
|
11
|
+
link blog.url
|
12
|
+
description blog.description
|
13
|
+
}
|
14
|
+
|
15
|
+
# This block gets called once per item.
|
16
|
+
def_item { |i|
|
17
|
+
title i.title
|
18
|
+
link i.url
|
19
|
+
description i.summary
|
20
|
+
# pub_date is a helper for pubDate:
|
21
|
+
pub_date i.time
|
22
|
+
author i.author.email if i.author
|
23
|
+
}
|
24
|
+
|
25
|
+
# You need items to have a feed.
|
26
|
+
self.items = blog.entries
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# We're going to stub out a fake blog object full of fake entries before we get
|
31
|
+
# to the good part.
|
32
|
+
blog = OpenStruct.new(:title => 'Hello, world of syndication!',
|
33
|
+
:url => 'http://www.example.com/blog',
|
34
|
+
:description => 'Nope, no, not at all.')
|
35
|
+
|
36
|
+
author = Struct.new(:email)
|
37
|
+
blog.entries = [
|
38
|
+
{ :title => "first ps0t!!!1!",
|
39
|
+
:url => 'http://www.example.com/blog/first_psot',
|
40
|
+
:description => 'This is the first post.',
|
41
|
+
:time => (Time.now - 60 * 60 * 24),
|
42
|
+
:author => author.new('biff@example.com'),
|
43
|
+
},
|
44
|
+
{ :title => "What is with Biff?",
|
45
|
+
:url => 'http://www.example.com/blog/biff_sux',
|
46
|
+
:description => 'Jerk stole the first post from me.',
|
47
|
+
:time => Time.now,
|
48
|
+
:author => author.new('emily_postnews@example.com'),
|
49
|
+
},
|
50
|
+
].map &OpenStruct.method(:new)
|
51
|
+
|
52
|
+
puts BlogFeed.new(blog).render
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'hoshi'
|
2
|
+
|
3
|
+
class Trivial < Hoshi::View :html4
|
4
|
+
def show
|
5
|
+
doctype
|
6
|
+
html {
|
7
|
+
head {
|
8
|
+
title "Hello, world!"
|
9
|
+
link :rel => 'stylesheet', :href => '/css/hoshi.css'
|
10
|
+
}
|
11
|
+
|
12
|
+
body {
|
13
|
+
h1 "Hello, world!"
|
14
|
+
p "This is a greeting to the world."
|
15
|
+
}
|
16
|
+
}
|
17
|
+
render
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
puts Trivial.new.show
|
@@ -0,0 +1,8 @@
|
|
1
|
+
class Hash
|
2
|
+
# Makes this hash fit to be put into a tag.
|
3
|
+
# { :a => 1, :b => "two" }.to_html_options # => 'a="one" b="two"'
|
4
|
+
def to_html_options double_quotes = true
|
5
|
+
qchar = double_quotes ? '"' : "'"
|
6
|
+
map { |k,v| "#{k}=#{qchar}#{v}#{qchar}" }.join(' ')
|
7
|
+
end
|
8
|
+
end
|
data/lib/hoshi/tag.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Hoshi
|
2
|
+
# Represents an HTML tag. You usually won't be using this class directly.
|
3
|
+
class Tag
|
4
|
+
attr_accessor :name, :close_type
|
5
|
+
|
6
|
+
# A tag currently has only two attributes: a name and a method for
|
7
|
+
# closing it, which both decide how it is rendered as a string.
|
8
|
+
# A self-closing tag:
|
9
|
+
# Tag.new('test', :self).render # => "<test />"
|
10
|
+
# A tag that does not need to close:
|
11
|
+
# Tag.new('test', :none).render('test this') # => "<test>test this\n"
|
12
|
+
# And a regular tag:
|
13
|
+
# Tag.new('test').render # => "<test></test>"
|
14
|
+
def initialize(name, close_type = nil)
|
15
|
+
@name, @close_type = name, close_type
|
16
|
+
end
|
17
|
+
|
18
|
+
# Generates a string from this tag. inside should be the contents to
|
19
|
+
# put between the opening and closing tags (if any), and opts are the
|
20
|
+
# HTML options to put in the tag. For example,
|
21
|
+
# Tag.new('div').render('Click for an alert.',
|
22
|
+
# :onclick => "alert('Hi.');")
|
23
|
+
# gets you this:
|
24
|
+
# <div onclick="alert('Hi.');">Click for an alert.</div>
|
25
|
+
def render(inside = nil, opts = {})
|
26
|
+
inside = inside.to_s
|
27
|
+
|
28
|
+
s = "<#{name} #{opts.to_html_options}".strip
|
29
|
+
if inside.empty? && close_type == :self
|
30
|
+
return s << " />"
|
31
|
+
end
|
32
|
+
|
33
|
+
s << ">" << inside
|
34
|
+
|
35
|
+
if close_type == :none
|
36
|
+
s << "\n"
|
37
|
+
else
|
38
|
+
s << "</#{name}>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Hoshi::View
|
2
|
+
class HTML < self
|
3
|
+
# So, 'p' may look a little out of place here, but if you sub-class
|
4
|
+
# View(:html) directly and use permissive!, you'll end up with Kernel#p
|
5
|
+
# rather than a <p> tag. Almost never what you want.
|
6
|
+
tags *%w(html head body p)
|
7
|
+
|
8
|
+
def self.content_type
|
9
|
+
'text/html'
|
10
|
+
end
|
11
|
+
|
12
|
+
def cdata str
|
13
|
+
append! "<![CDATA[\n" + str + "\n]]>"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'hoshi/view/html'
|
2
|
+
|
3
|
+
class Hoshi::View
|
4
|
+
class HTML4 < HTML
|
5
|
+
dtd! "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " \
|
6
|
+
"\"http://www.w3.org/TR/html4/strict.dtd\">"
|
7
|
+
|
8
|
+
|
9
|
+
tags *%w(a address applet area base basefont bdo blockquote body br
|
10
|
+
button caption center col colgroup dd div dl dt fieldset font
|
11
|
+
form frame frameset h1 h2 h3 h4 h5 h6 head hr html iframe img
|
12
|
+
input isindex label legend li link map meta noframes noscript
|
13
|
+
object ol optgroup option p param pre q script select span
|
14
|
+
style table tbody textarea tfoot thead title tr ul)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'time' # Doesn't everything?
|
2
|
+
|
3
|
+
class Hoshi::View
|
4
|
+
class RSS2 < self
|
5
|
+
class InvalidItemError < ValidationError; end
|
6
|
+
class InvalidChannelError < ValidationError; end
|
7
|
+
|
8
|
+
tags *%w(author category channel cloud comments copyright description
|
9
|
+
docs enclosure generator guid item language lastBuildDate link
|
10
|
+
managingEditor pubDate rating skipDays skipHours source title
|
11
|
+
ttl webMaster rss image url )
|
12
|
+
|
13
|
+
attr_accessor :channel_block, :item_block, :items
|
14
|
+
|
15
|
+
dtd! "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
16
|
+
|
17
|
+
def self.content_type
|
18
|
+
'application/rss+xml'
|
19
|
+
end
|
20
|
+
|
21
|
+
def def_channel &b
|
22
|
+
self.channel_block = b
|
23
|
+
end
|
24
|
+
|
25
|
+
def def_item &b
|
26
|
+
self.item_block = b
|
27
|
+
end
|
28
|
+
|
29
|
+
# A small helper so you can just pass a date, time, or string rather
|
30
|
+
# than worrying about format.
|
31
|
+
def pub_date dt
|
32
|
+
dt =
|
33
|
+
case dt
|
34
|
+
when Time, Date, DateTime
|
35
|
+
dt.rfc822
|
36
|
+
else
|
37
|
+
dt
|
38
|
+
end
|
39
|
+
pubDate dt
|
40
|
+
end
|
41
|
+
|
42
|
+
def render
|
43
|
+
clear!
|
44
|
+
doctype
|
45
|
+
rss(:version => '2.0') {
|
46
|
+
channel {
|
47
|
+
channel_block.call
|
48
|
+
items.each { |i|
|
49
|
+
item { item_block.call i }
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
super()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'hoshi/view/xhtml'
|
2
|
+
|
3
|
+
class Hoshi::View
|
4
|
+
class XHTML1 < XHTML
|
5
|
+
dtd! "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 " \
|
6
|
+
"Strict//EN\" " \
|
7
|
+
"\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
|
8
|
+
tags *%w(a abbr acronym address applet area b base basefont bdo big
|
9
|
+
blockquote body br button caption center cite code col
|
10
|
+
colgroup dd del dfn dir div dl dt em fieldset font form h1 h2
|
11
|
+
h3 h4 h5 h6 head hr html i iframe img input ins isindex kbd
|
12
|
+
label legend li link map menu meta noframes noscript object ol
|
13
|
+
optgroup option p param pre q s samp script select small span
|
14
|
+
strike strong style sub sup table tbody td textarea tfoot th
|
15
|
+
thead title tr tt u ul var)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'hoshi/view/xhtml1'
|
2
|
+
|
3
|
+
class Hoshi::View
|
4
|
+
class XHTML1Frameset < XHTML1
|
5
|
+
dtd! "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 " \
|
6
|
+
"Frameset//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd\">"
|
7
|
+
tags *%w(a abbr acronym address applet area b base basefont bdo big
|
8
|
+
blockquote body br button caption center cite code col
|
9
|
+
colgroup dd del dfn dir div dl dt em fieldset font form frame
|
10
|
+
frameset h1 h2 h3 h4 h5 h6 head hr html i iframe img input ins
|
11
|
+
isindex kbd label legend li link map menu meta noframes
|
12
|
+
noscript object ol optgroup option p param pre q s samp script
|
13
|
+
select small span strike strong style sub sup table tbody td
|
14
|
+
textarea tfoot th thead title tr tt u ul var)
|
15
|
+
end
|
16
|
+
end
|
data/lib/hoshi/view.rb
ADDED
@@ -0,0 +1,205 @@
|
|
1
|
+
require 'metaid'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Hoshi
|
5
|
+
# The View class is the super-class for views you create with Hoshi. More
|
6
|
+
# likely, though, you'll be using one of View's many sub-classes as the
|
7
|
+
# super-class for your view, like this:
|
8
|
+
# class MyView < Hoshi::View :html4
|
9
|
+
# or
|
10
|
+
# class MyView < Hoshi::View::XHTML1Frameset
|
11
|
+
# Of course, using View[] is the preferred method for the sake of brevity.
|
12
|
+
# When you create a view class, you'll want to define one or more methods
|
13
|
+
# that eventually call View#render, which turns your view into HTML.
|
14
|
+
# (Private methods and methods that build up state do not need to do so.)
|
15
|
+
class View
|
16
|
+
class ValidationError < StandardError; end
|
17
|
+
|
18
|
+
# This creates an instance method for this view which appends a tag.
|
19
|
+
# Most of these are handled for you. The arguments to this method
|
20
|
+
# match those to Tag.new. See also View#permissive!.
|
21
|
+
# tag('h1')
|
22
|
+
# def show_an_h1
|
23
|
+
# h1 "I have been shown"
|
24
|
+
# end
|
25
|
+
def self.tag(name, close_type = nil)
|
26
|
+
class_eval <<-EOHACK
|
27
|
+
def #{name}(*opts, &b)
|
28
|
+
if b
|
29
|
+
tag #{name.inspect}, #{close_type.inspect}, *opts, &b
|
30
|
+
else
|
31
|
+
tag #{name.inspect}, #{close_type.inspect}, *opts, &b
|
32
|
+
end
|
33
|
+
end
|
34
|
+
EOHACK
|
35
|
+
end
|
36
|
+
|
37
|
+
# A short-hand for creating multiple tags via View.tag. For tags that
|
38
|
+
# do not require closing, see View.open_tags.
|
39
|
+
def self.tags *names
|
40
|
+
names.map &method(:tag)
|
41
|
+
end
|
42
|
+
|
43
|
+
# A short-hand for creating multiple tags that are left open.
|
44
|
+
def self.open_tags *names
|
45
|
+
names.map { |n| tag n, :none }
|
46
|
+
end
|
47
|
+
|
48
|
+
# This method choses, based on the provided doctype, the proper
|
49
|
+
# sub-class of View. Generally, you'll be using this rather than
|
50
|
+
# sub-classing View directly. The doctype argument is case- and
|
51
|
+
# underscore-insensitive, and valid arguments are names of View
|
52
|
+
# subclasses that are inside the View namespace.
|
53
|
+
def self.[] doctype
|
54
|
+
doctype = doctype.to_s.downcase.gsub('_', '')
|
55
|
+
const_get(constants.find { |c|
|
56
|
+
cl = const_get c
|
57
|
+
(cl.ancestors.include?(self) &&
|
58
|
+
c.to_s.downcase == doctype) rescue false
|
59
|
+
}) rescue nil
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets the doctype declaration for this class.
|
63
|
+
def self.dtd! dtd
|
64
|
+
dtd += "\n"
|
65
|
+
define_method(:doctype) { append! dtd }
|
66
|
+
end
|
67
|
+
def doctype
|
68
|
+
comment "No doctype defined; are you sub-classing View directly " \
|
69
|
+
"and not calling dtd!()?"
|
70
|
+
end
|
71
|
+
|
72
|
+
# Free-form tags. Basically, dynamic tag creation by method_missing.
|
73
|
+
def self.permissive!
|
74
|
+
@permissive = true
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns true if we add tags to this class on the fly.
|
78
|
+
def self.permissive?
|
79
|
+
@permissive
|
80
|
+
end
|
81
|
+
|
82
|
+
# Only the tags already specified are allowed. No dynamic tag
|
83
|
+
# creation. This is the default.
|
84
|
+
def self.strict!
|
85
|
+
@permissive = false
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns true if we do not add tags to the class on the fly.
|
89
|
+
def self.strict?
|
90
|
+
!permissive?
|
91
|
+
end
|
92
|
+
|
93
|
+
# Create and render a view via a block.
|
94
|
+
def self.build(&block)
|
95
|
+
c = new
|
96
|
+
c.instance_eval(&block)
|
97
|
+
c.render
|
98
|
+
end
|
99
|
+
|
100
|
+
# This is overridden in HTML/XHTML, and you'll definitely want to
|
101
|
+
# override it if you subclass View directly.
|
102
|
+
def self.content_type
|
103
|
+
'application/octet-stream'
|
104
|
+
end
|
105
|
+
|
106
|
+
# Most of these files depend on the above method definitions.
|
107
|
+
Dir["#{File.dirname(__FILE__)}/view/*.rb"].each &method(:require)
|
108
|
+
|
109
|
+
def initialize
|
110
|
+
clear!
|
111
|
+
end
|
112
|
+
|
113
|
+
# Clears the current state of this view.
|
114
|
+
def clear!
|
115
|
+
self.tree = []
|
116
|
+
self.current = tree
|
117
|
+
end
|
118
|
+
|
119
|
+
# Adds a comment.
|
120
|
+
def comment(*a)
|
121
|
+
if a.include?('--')
|
122
|
+
raise ValidationError, "Comments can't include '--'."
|
123
|
+
else
|
124
|
+
append! "<!-- #{a} -->"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Appends a tag to the current document, for when a tag is only needed
|
129
|
+
# once or has a name that is not a valid method name.
|
130
|
+
def tag(tname, close_type = nil, *opts, &b)
|
131
|
+
t = Tag.new(tname, close_type)
|
132
|
+
|
133
|
+
if b
|
134
|
+
old, self.current = current, []
|
135
|
+
b.call
|
136
|
+
inside, self.current = current.map { |i| i.to_s }.join, old
|
137
|
+
else
|
138
|
+
inside = opts.shift if opts.first.kind_of?(String)
|
139
|
+
end
|
140
|
+
|
141
|
+
append! t.render(inside, opts.first || {})
|
142
|
+
end
|
143
|
+
|
144
|
+
# Appends something to the document. The comment, decl, and various
|
145
|
+
# tag methods call this.
|
146
|
+
def append! x
|
147
|
+
current << x
|
148
|
+
x
|
149
|
+
end
|
150
|
+
|
151
|
+
# If you're tired of typing "doctype\nhtml" every single time.
|
152
|
+
def doc &b
|
153
|
+
doctype
|
154
|
+
html &b
|
155
|
+
end
|
156
|
+
|
157
|
+
# Turns things in to strings, properly escapes them, and appends them
|
158
|
+
# to the document.
|
159
|
+
def safe *things
|
160
|
+
append! CGI.escapeHTML(things.map { |i| i.to_s }.join("\n"))
|
161
|
+
end
|
162
|
+
|
163
|
+
# Appends one or more non-escaped strings to the document.
|
164
|
+
def raw *things
|
165
|
+
append! things.join
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the string representation of the document. This is what you
|
169
|
+
# want to eventually call.
|
170
|
+
def render
|
171
|
+
tree.flatten.map { |i| i.to_s }.join
|
172
|
+
end
|
173
|
+
|
174
|
+
# Prints the string representation of the docutment, with HTTP headers.
|
175
|
+
# Useful for one-off CGI scripts. Takes an optional hash argument for
|
176
|
+
# headers (Content-Type and Status are set by default). See CGI#header
|
177
|
+
# for information on how the header hash should look.
|
178
|
+
def render_cgi(extra_headers = {})
|
179
|
+
h = {
|
180
|
+
'type' => self.class.content_type,
|
181
|
+
'status' => 'OK',
|
182
|
+
}.merge(extra_headers)
|
183
|
+
|
184
|
+
CGI.new.out(h) { render }
|
185
|
+
end
|
186
|
+
|
187
|
+
# Dynamically add tags if the view class for this object is permissive.
|
188
|
+
def method_missing(mname, *args, &b)
|
189
|
+
if self.class.permissive?
|
190
|
+
self.class.tag mname
|
191
|
+
if b
|
192
|
+
send mname, *args, &b
|
193
|
+
else
|
194
|
+
send mname, *args
|
195
|
+
end
|
196
|
+
else
|
197
|
+
super
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
attr_accessor :tree, :current
|
204
|
+
end
|
205
|
+
end
|
data/lib/hoshi.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'hoshi/monkey_patches'
|
2
|
+
|
3
|
+
require 'hoshi/tag'
|
4
|
+
require 'hoshi/view'
|
5
|
+
|
6
|
+
# This is the namespace for all of Hoshi, which currently only includes
|
7
|
+
# Hoshi::View and Hoshi::Tag . For an overview, see doc/README . For
|
8
|
+
# specifics, check out Hoshi::View .
|
9
|
+
module Hoshi
|
10
|
+
# This is a cosmetic method; you may do Hoshi::View[:type],
|
11
|
+
# Hoshi::View(:type), or Hoshi::View :type
|
12
|
+
def self.View(*a, &b)
|
13
|
+
klass = View[*a]
|
14
|
+
if b
|
15
|
+
klass.build &b
|
16
|
+
else
|
17
|
+
klass
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/html2hoshi.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'hpricot'
|
2
|
+
|
3
|
+
module Hoshi
|
4
|
+
# A semi-hacky method for converting HTML text to a Hoshi method. It takes
|
5
|
+
# an HTML document as a string, and optionally the string to use for each
|
6
|
+
# level of indentation.
|
7
|
+
def self.from_html(html, indent = "\t")
|
8
|
+
"#{indent}def page" << tree_to_hoshi(parse(html), indent, 2) <<
|
9
|
+
"\n#{indent * 2}render\n#{indent}end"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.parse(doc)
|
15
|
+
Hpricot.method(
|
16
|
+
if doc.include?('<?xml') # Seems like a safe assumption.
|
17
|
+
:XML
|
18
|
+
else
|
19
|
+
:parse
|
20
|
+
end).call(doc)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.tree_to_hoshi(tree, indent = "\t", indentlevel = 0)
|
24
|
+
idt = indent * indentlevel
|
25
|
+
str = ''
|
26
|
+
tree.each_child { |i|
|
27
|
+
str << ("\n#{idt}" <<
|
28
|
+
case i
|
29
|
+
when Hpricot::Elem
|
30
|
+
# Something simple that resembles rules for ruby method naming.
|
31
|
+
# I know that both XML and Ruby allow most valid unicode
|
32
|
+
# characters in tags/method names, but this is Good Enough.
|
33
|
+
using_tag = !/^[a-z][a-zA-Z0-9_]*[!?]?$/.match(i.name)
|
34
|
+
|
35
|
+
args, block = [], nil
|
36
|
+
s = if using_tag
|
37
|
+
args << i.name.inspect << 'nil'
|
38
|
+
'tag'
|
39
|
+
else
|
40
|
+
i.name
|
41
|
+
end
|
42
|
+
|
43
|
+
if i.children.size == 1 &&
|
44
|
+
i.children.first.kind_of?(Hpricot::Text)
|
45
|
+
fc = i.children.first.to_s.strip
|
46
|
+
args << fc.inspect unless fc.empty?
|
47
|
+
elsif !i.children.empty?
|
48
|
+
block = ' {' <<
|
49
|
+
tree_to_hoshi(i, indent, indentlevel + 1) <<
|
50
|
+
"\n#{idt}}"
|
51
|
+
end
|
52
|
+
|
53
|
+
unless i.attributes.empty?
|
54
|
+
args << i.attributes.inspect.sub(/^\{/, '').sub(/\}$/, '')
|
55
|
+
end
|
56
|
+
|
57
|
+
unless args.empty?
|
58
|
+
arg_s = args.join(', ')
|
59
|
+
if block
|
60
|
+
s << "(#{arg_s})"
|
61
|
+
else
|
62
|
+
s << " #{arg_s}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
s << block if block
|
67
|
+
|
68
|
+
s
|
69
|
+
when Hpricot::Comment
|
70
|
+
"comment #{i.content.inspect}"
|
71
|
+
when Hpricot::BogusETag
|
72
|
+
"raw #{i.to_s.inspect} # Dangling end tag."
|
73
|
+
when Hpricot::DocType, Hpricot::ProcIns, Hpricot::XMLDecl,
|
74
|
+
Hpricot::Text, Object
|
75
|
+
x = i.to_s
|
76
|
+
if /\A\s*\Z/.match(x)
|
77
|
+
next
|
78
|
+
else
|
79
|
+
"raw #{i.to_s.strip.inspect}"
|
80
|
+
end
|
81
|
+
end)
|
82
|
+
}
|
83
|
+
str
|
84
|
+
end
|
85
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hoshi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.186
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pete Elmore
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-10 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: metaid
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: hpricot
|
27
|
+
type: :runtime
|
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:
|
36
|
+
email: pete.elmore@gmail.com
|
37
|
+
executables:
|
38
|
+
- html2hoshi
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- doc/README
|
43
|
+
- doc/TODO
|
44
|
+
- doc/LICENSE
|
45
|
+
files:
|
46
|
+
- lib/html2hoshi.rb
|
47
|
+
- lib/hoshi.rb
|
48
|
+
- lib/hoshi
|
49
|
+
- lib/hoshi/monkey_patches.rb
|
50
|
+
- lib/hoshi/view
|
51
|
+
- lib/hoshi/view/html.rb
|
52
|
+
- lib/hoshi/view/xhtml1_strict.rb
|
53
|
+
- lib/hoshi/view/xhtml1_frameset.rb
|
54
|
+
- lib/hoshi/view/html4_transitional.rb
|
55
|
+
- lib/hoshi/view/xhtml.rb
|
56
|
+
- lib/hoshi/view/html3.rb
|
57
|
+
- lib/hoshi/view/html4_frameset.rb
|
58
|
+
- lib/hoshi/view/xhtml1.rb
|
59
|
+
- lib/hoshi/view/html4.rb
|
60
|
+
- lib/hoshi/view/xhtml2.rb
|
61
|
+
- lib/hoshi/view/xhtml1_transitional.rb
|
62
|
+
- lib/hoshi/view/rss2.rb
|
63
|
+
- lib/hoshi/tag.rb
|
64
|
+
- lib/hoshi/view.rb
|
65
|
+
- doc/README
|
66
|
+
- doc/examples
|
67
|
+
- doc/examples/layouts.rb
|
68
|
+
- doc/examples/trivial.rb
|
69
|
+
- doc/examples/blocks.rb
|
70
|
+
- doc/examples/env.cgi
|
71
|
+
- doc/examples/feed.rb
|
72
|
+
- doc/examples/rss2.rb
|
73
|
+
- doc/TODO
|
74
|
+
- doc/LICENSE
|
75
|
+
- bin/html2hoshi
|
76
|
+
- Rakefile
|
77
|
+
has_rdoc: true
|
78
|
+
homepage: http://debu.gs/hoshi
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
version:
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
requirements: []
|
97
|
+
|
98
|
+
rubyforge_project: hoshi-view
|
99
|
+
rubygems_version: 1.3.1
|
100
|
+
signing_key:
|
101
|
+
specification_version: 2
|
102
|
+
summary: Nice, object-oriented, first-class views.
|
103
|
+
test_files: []
|
104
|
+
|