hoshi 0.0.186
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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
|
+
|