webby 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/Manifest.txt +45 -2
- data/Rakefile +1 -2
- data/examples/webby/content/css/site.css +1 -1
- data/examples/webby/content/learn/index.txt +1 -1
- data/examples/webby/content/release-notes/index.txt +21 -0
- data/examples/webby/content/release-notes/rel-0-9-0/index.txt +1 -0
- data/examples/webby/content/release-notes/rel-0-9-1/index.txt +93 -0
- data/examples/webby/content/tips_and_tricks/index.txt +14 -13
- data/examples/webby/content/tutorial/index.txt +13 -9
- data/examples/webby/content/user-manual/index.txt +8 -8
- data/examples/webby/layouts/default.txt +1 -1
- data/lib/webby.rb +2 -1
- data/lib/webby/apps/main.rb +25 -13
- data/lib/webby/auto_builder.rb +2 -0
- data/lib/webby/builder.rb +2 -5
- data/lib/webby/filters.rb +5 -13
- data/lib/webby/renderer.rb +7 -5
- data/lib/webby/resources.rb +54 -14
- data/lib/webby/resources/db.rb +4 -4
- data/lib/webby/resources/layout.rb +14 -23
- data/lib/webby/resources/meta_file.rb +208 -0
- data/lib/webby/resources/page.rb +21 -58
- data/lib/webby/resources/partial.rb +10 -4
- data/lib/webby/resources/resource.rb +68 -27
- data/lib/webby/resources/static.rb +6 -22
- data/lib/webby/stelan/paginator.rb +17 -2
- data/spec/data/Sitefile +9 -0
- data/spec/data/content/_partial.txt +10 -0
- data/spec/data/content/css/coderay.css +111 -0
- data/spec/data/content/css/site.css +67 -0
- data/spec/data/content/css/tumblog.css +308 -0
- data/spec/data/content/images/tumblog/permalink.gif +0 -0
- data/spec/data/content/images/tumblog/rss.gif +0 -0
- data/spec/data/content/index.txt +19 -0
- data/spec/data/content/photos.txt +21 -0
- data/spec/data/content/tumblog/200806/the-noble-chicken/index.txt +12 -0
- data/spec/data/content/tumblog/200807/historical-perspectives-on-the-classic-chicken-joke/index.txt +12 -0
- data/spec/data/content/tumblog/200807/mad-city-chickens/index.txt +10 -0
- data/spec/data/content/tumblog/200807/the-wisdom-of-the-dutch/index.txt +11 -0
- data/spec/data/content/tumblog/200807/up-a-tree/index.txt +13 -0
- data/spec/data/content/tumblog/index.txt +37 -0
- data/spec/data/content/tumblog/rss.txt +37 -0
- data/spec/data/hooligans/bad_meta_data_1.txt +34 -0
- data/spec/data/hooligans/bad_meta_data_2.txt +34 -0
- data/spec/data/layouts/default.txt +58 -0
- data/spec/data/layouts/tumblog/default.txt +44 -0
- data/spec/data/layouts/tumblog/post.txt +15 -0
- data/spec/data/lib/breadcrumbs.rb +28 -0
- data/spec/data/lib/tumblog_helper.rb +32 -0
- data/spec/data/tasks/tumblog.rake +30 -0
- data/spec/data/templates/_partial.erb +10 -0
- data/spec/data/templates/atom_feed.erb +40 -0
- data/spec/data/templates/page.erb +18 -0
- data/spec/data/templates/presentation.erb +40 -0
- data/spec/data/templates/tumblog/conversation.erb +12 -0
- data/spec/data/templates/tumblog/link.erb +10 -0
- data/spec/data/templates/tumblog/photo.erb +13 -0
- data/spec/data/templates/tumblog/post.erb +12 -0
- data/spec/data/templates/tumblog/quote.erb +11 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/webby/apps/generator_spec.rb +4 -0
- data/spec/webby/apps/main_spec.rb +16 -3
- data/spec/webby/filters/textile_spec.rb +20 -0
- data/spec/webby/renderer_spec.rb +139 -0
- data/spec/webby/resources/db_spec.rb +250 -0
- data/spec/webby/resources/layout_spec.rb +83 -0
- data/spec/webby/resources/meta_file_spec.rb +157 -0
- data/spec/webby/resources/page_spec.rb +111 -0
- data/spec/webby/resources/partial_spec.rb +58 -0
- data/spec/webby/resources/resource_spec.rb +214 -0
- data/spec/webby/resources/static_spec.rb +49 -0
- data/spec/webby/resources_spec.rb +55 -3
- metadata +64 -6
- data/lib/webby/resources/file.rb +0 -221
- data/spec/webby/resources/file_spec.rb +0 -104
data/lib/webby.rb
CHANGED
@@ -18,9 +18,10 @@ Logging::Appender.stdout.layout = Logging::Layouts::Pattern.new(
|
|
18
18
|
module Webby
|
19
19
|
|
20
20
|
# :stopdoc:
|
21
|
-
VERSION = '0.9.
|
21
|
+
VERSION = '0.9.1' # :nodoc:
|
22
22
|
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
23
23
|
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
24
|
+
YAML_SEP = '---'
|
24
25
|
# :startdoc:
|
25
26
|
|
26
27
|
class Error < StandardError; end # :nodoc:
|
data/lib/webby/apps/main.rb
CHANGED
@@ -37,10 +37,12 @@ class Main
|
|
37
37
|
opts.banner = 'Usage: webby [options] target [target args]'
|
38
38
|
|
39
39
|
opts.separator ''
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
|
41
|
+
desired_opts = %[--describe --prereqs --tasks --trace]
|
42
|
+
app.standard_rake_options.each do |options|
|
43
|
+
next unless desired_opts.include?(options.first)
|
44
|
+
opts.on(*options)
|
45
|
+
end
|
44
46
|
|
45
47
|
opts.separator ''
|
46
48
|
opts.separator 'common options:'
|
@@ -75,9 +77,10 @@ class Main
|
|
75
77
|
#
|
76
78
|
def init( args )
|
77
79
|
# Make sure we're in a folder with a Sitefile
|
78
|
-
app.
|
79
|
-
|
80
|
-
|
80
|
+
options = app.standard_rake_options
|
81
|
+
[['--rakefile', 'Sitefile'],
|
82
|
+
['--no-search', nil],
|
83
|
+
['--silent', nil]].each {|opt, value| options.assoc(opt).last.call(value)}
|
81
84
|
|
82
85
|
unless app.have_rakefile
|
83
86
|
raise RuntimeError, "Sitefile not found"
|
@@ -104,6 +107,12 @@ class Main
|
|
104
107
|
Rake.application
|
105
108
|
end
|
106
109
|
|
110
|
+
# Returns the options hash from the Rake application object.
|
111
|
+
#
|
112
|
+
def options
|
113
|
+
app.options
|
114
|
+
end
|
115
|
+
|
107
116
|
# Search for the "Sitefile" starting in the current directory and working
|
108
117
|
# upwards through the filesystem until the root of the filesystem is
|
109
118
|
# reached. If a "Sitefile" is not found, a RuntimeError is raised.
|
@@ -144,12 +153,15 @@ class Main
|
|
144
153
|
dir = ::File.dirname(dashed)
|
145
154
|
|
146
155
|
args.dir = ('.' == dir ? '' : dir)
|
147
|
-
args.slug = ::Webby::Resources
|
148
|
-
args.title = ::Webby::Resources
|
156
|
+
args.slug = ::Webby::Resources.basename(dashed).to_url
|
157
|
+
args.title = ::Webby::Resources.basename(spaced).titlecase
|
149
158
|
|
150
159
|
# page should be dir/slug without leading /
|
151
160
|
args.page = ::File.join(args.dir, args.slug).gsub(/^\//, '')
|
152
161
|
|
162
|
+
ext = ::File.extname(dashed)
|
163
|
+
args.page << ext unless ext.empty?
|
164
|
+
|
153
165
|
::Webby.site.args = args
|
154
166
|
Object.const_set(:SITE, Webby.site)
|
155
167
|
args
|
@@ -184,10 +196,10 @@ class Rake::Application
|
|
184
196
|
end
|
185
197
|
else
|
186
198
|
width = displayable_tasks.collect { |t| t.name_with_args.length }.max || 10
|
187
|
-
max_column =
|
199
|
+
max_column = truncate_output? ? terminal_width - name.size - width - 7 : nil
|
188
200
|
displayable_tasks.each do |t|
|
189
201
|
printf "#{name} %-#{width}s # %s\n",
|
190
|
-
t.name_with_args, truncate(t.comment, max_column)
|
202
|
+
t.name_with_args, max_column ? truncate(t.comment, max_column) : t.comment
|
191
203
|
end
|
192
204
|
end
|
193
205
|
end
|
@@ -199,12 +211,12 @@ class Rake::Application
|
|
199
211
|
rescue SystemExit => ex
|
200
212
|
# Exit silently with current status
|
201
213
|
exit(ex.status)
|
202
|
-
rescue SystemExit,
|
214
|
+
rescue SystemExit, OptionParser::InvalidOption => ex
|
203
215
|
# Exit silently
|
204
216
|
exit(1)
|
205
217
|
rescue Exception => ex
|
206
218
|
# Exit with error message
|
207
|
-
$stderr.puts "
|
219
|
+
$stderr.puts "#{name} aborted!"
|
208
220
|
$stderr.puts ex.message
|
209
221
|
if options.trace
|
210
222
|
$stderr.puts ex.backtrace.join("\n")
|
data/lib/webby/auto_builder.rb
CHANGED
data/lib/webby/builder.rb
CHANGED
@@ -35,8 +35,8 @@ class Builder
|
|
35
35
|
tmpl = opts[:from]
|
36
36
|
raise Error, "template not given" unless tmpl
|
37
37
|
|
38
|
-
name = ::Webby::Resources
|
39
|
-
ext = ::Webby::Resources
|
38
|
+
name = ::Webby::Resources.basename(page)
|
39
|
+
ext = ::Webby::Resources.extname(page)
|
40
40
|
dir = ::File.dirname(page)
|
41
41
|
dir = '' if dir == '.'
|
42
42
|
|
@@ -157,9 +157,6 @@ class Builder
|
|
157
157
|
nil
|
158
158
|
end
|
159
159
|
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
160
|
# Scan the <code>layouts/</code> folder and the <code>content/</code>
|
164
161
|
# folder and create a new Resource object for each file found there.
|
165
162
|
#
|
data/lib/webby/filters.rb
CHANGED
@@ -5,7 +5,7 @@ module Filters
|
|
5
5
|
|
6
6
|
# Register a handler for a filter
|
7
7
|
def register( filter, &block )
|
8
|
-
|
8
|
+
_handlers[filter.to_s] = block
|
9
9
|
end
|
10
10
|
|
11
11
|
# Process input through filters
|
@@ -16,15 +16,11 @@ module Filters
|
|
16
16
|
|
17
17
|
# Access a filter handler
|
18
18
|
def []( name )
|
19
|
-
|
19
|
+
_handlers[name]
|
20
20
|
end
|
21
21
|
|
22
|
-
#######
|
23
|
-
private
|
24
|
-
#######
|
25
|
-
|
26
22
|
# The registered filter handlers
|
27
|
-
def
|
23
|
+
def _handlers
|
28
24
|
@handlers ||= {}
|
29
25
|
end
|
30
26
|
|
@@ -53,7 +49,7 @@ module Filters
|
|
53
49
|
raise ::Webby::Error, "unknown filter: #{filter.inspect}" if handler.nil?
|
54
50
|
|
55
51
|
args = [result, self][0, handler.arity]
|
56
|
-
|
52
|
+
_handle(filter, handler, *args)
|
57
53
|
end
|
58
54
|
ensure
|
59
55
|
@renderer.instance_variable_set(:@_cursor, @prev_cursor)
|
@@ -69,12 +65,8 @@ module Filters
|
|
69
65
|
filters[@processed]
|
70
66
|
end
|
71
67
|
|
72
|
-
#######
|
73
|
-
private
|
74
|
-
#######
|
75
|
-
|
76
68
|
# Process arguments through a single filter
|
77
|
-
def
|
69
|
+
def _handle(filter, handler, *args)
|
78
70
|
result = handler.call(*args)
|
79
71
|
@processed += 1
|
80
72
|
result
|
data/lib/webby/renderer.rb
CHANGED
@@ -120,6 +120,8 @@ class Renderer
|
|
120
120
|
::Webby::Renderer.new(resource)._render_page
|
121
121
|
when Resources::Partial
|
122
122
|
_render_partial(resource, opts)
|
123
|
+
when Resources::Static
|
124
|
+
resource._read
|
123
125
|
else
|
124
126
|
raise ::Webby::Error, "expecting a page or a partial but got '#{resource.class.name}'"
|
125
127
|
end
|
@@ -189,7 +191,7 @@ class Renderer
|
|
189
191
|
#
|
190
192
|
def _render_page
|
191
193
|
_track_rendering(@page.path) {
|
192
|
-
Filters.process(self, @page,
|
194
|
+
Filters.process(self, @page, @page._read)
|
193
195
|
}
|
194
196
|
end
|
195
197
|
|
@@ -203,7 +205,7 @@ class Renderer
|
|
203
205
|
def _render_partial( part, opts = {} )
|
204
206
|
_track_rendering(part.path) {
|
205
207
|
_configure_locals(opts[:locals])
|
206
|
-
Filters.process(self, part,
|
208
|
+
Filters.process(self, part, part._read)
|
207
209
|
}
|
208
210
|
end
|
209
211
|
|
@@ -248,8 +250,7 @@ class Renderer
|
|
248
250
|
return if lyt.nil?
|
249
251
|
|
250
252
|
_track_rendering(lyt.path) {
|
251
|
-
@content = Filters.process(
|
252
|
-
self, lyt, ::Webby::Resources::File.read(lyt.path))
|
253
|
+
@content = Filters.process(self, lyt, lyt._read)
|
253
254
|
_render_layout_for(lyt)
|
254
255
|
}
|
255
256
|
end
|
@@ -270,7 +271,8 @@ class Renderer
|
|
270
271
|
@_content_for.clear
|
271
272
|
@_bindings.clear
|
272
273
|
else
|
273
|
-
@
|
274
|
+
@pager.pager.done
|
275
|
+
@pager = nil
|
274
276
|
return false
|
275
277
|
end
|
276
278
|
|
data/lib/webby/resources.rb
CHANGED
@@ -44,25 +44,36 @@ module Webby::Resources
|
|
44
44
|
end
|
45
45
|
|
46
46
|
# see if we are dealing with a partial
|
47
|
-
filename =
|
47
|
+
filename = self.basename(fn)
|
48
48
|
if %r/\A_/o =~ filename
|
49
49
|
r = ::Webby::Resources::Partial.new(fn)
|
50
50
|
self.partials << r
|
51
51
|
return r
|
52
52
|
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
begin
|
55
|
+
fd = ::File.open(fn, 'r')
|
56
|
+
mf = MetaFile.new fd
|
57
|
+
|
58
|
+
# see if we are dealing with a static resource
|
59
|
+
unless mf.meta_data?
|
60
|
+
r = ::Webby::Resources::Static.new(fn)
|
61
|
+
self.pages << r
|
62
|
+
return r
|
63
|
+
end
|
64
|
+
|
65
|
+
# this is a renderable page
|
66
|
+
mf.each do |meta_data|
|
67
|
+
r = ::Webby::Resources::Page.new(fn, meta_data)
|
68
|
+
self.pages << r
|
69
|
+
r
|
70
|
+
end
|
71
|
+
rescue MetaFile::Error => err
|
72
|
+
logger.error "error loading file #{fn.inspect}"
|
73
|
+
logger.error err
|
74
|
+
ensure
|
75
|
+
fd.close if fd
|
60
76
|
end
|
61
|
-
|
62
|
-
# this is a renderable page
|
63
|
-
r = ::Webby::Resources::Page.new(fn)
|
64
|
-
self.pages << r
|
65
|
-
return r
|
66
77
|
end
|
67
78
|
|
68
79
|
# Returns a normalized path for the given filename.
|
@@ -77,7 +88,7 @@ module Webby::Resources
|
|
77
88
|
def find_layout( filename )
|
78
89
|
return if filename.nil?
|
79
90
|
|
80
|
-
fn =
|
91
|
+
fn = self.basename(filename)
|
81
92
|
dir = ::File.dirname(filename)
|
82
93
|
dir = '.' == dir ? '' : dir
|
83
94
|
|
@@ -87,8 +98,37 @@ module Webby::Resources
|
|
87
98
|
raise Webby::Error, "could not find layout #{filename.inspect}"
|
88
99
|
end
|
89
100
|
|
90
|
-
|
101
|
+
# Returns the directory component of the _filename_ with the content
|
102
|
+
# directory removed from the beginning if it is present.
|
103
|
+
#
|
104
|
+
def dirname( filename )
|
105
|
+
rgxp = %r/\A(?:#{::Webby.site.content_dir}|#{::Webby.site.layout_dir})\//o
|
106
|
+
dirname = ::File.dirname(filename)
|
107
|
+
dirname << '/' if dirname.index(?/) == nil
|
108
|
+
dirname.sub(rgxp, '')
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the last component of the _filename_ with any extension
|
112
|
+
# information removed.
|
113
|
+
#
|
114
|
+
def basename( filename )
|
115
|
+
::File.basename(filename, '.*')
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the extension (the portion of file name in path after the
|
119
|
+
# period). This method excludes the period from the extension name.
|
120
|
+
#
|
121
|
+
def extname( filename )
|
122
|
+
::File.extname(filename).tr('.', '')
|
123
|
+
end
|
91
124
|
|
125
|
+
# :stopdoc:
|
126
|
+
def logger
|
127
|
+
@logger ||= ::Logging::Logger[self]
|
128
|
+
end
|
129
|
+
# :startdoc:
|
130
|
+
|
131
|
+
end # class << self
|
92
132
|
end # module Webby::Resources
|
93
133
|
|
94
134
|
Webby.require_all_libs_relative_to(__FILE__)
|
data/lib/webby/resources/db.rb
CHANGED
@@ -23,7 +23,7 @@ class DB
|
|
23
23
|
# time if it already exists in the database.
|
24
24
|
#
|
25
25
|
def add( page )
|
26
|
-
ary = @db[page.
|
26
|
+
ary = @db[page.directory]
|
27
27
|
|
28
28
|
# make sure we don't duplicate pages
|
29
29
|
ary.delete page if ary.include? page
|
@@ -183,7 +183,7 @@ class DB
|
|
183
183
|
# Reverse the order of the results
|
184
184
|
#
|
185
185
|
def siblings( page, opts = {} )
|
186
|
-
ary = @db[page.
|
186
|
+
ary = @db[page.directory].dup
|
187
187
|
ary.delete page
|
188
188
|
return ary unless opts.has_key? :sort_by
|
189
189
|
|
@@ -207,7 +207,7 @@ class DB
|
|
207
207
|
# Reverse the order of the results
|
208
208
|
#
|
209
209
|
def children( page, opts = {} )
|
210
|
-
rgxp = Regexp.new "\\A#{page.
|
210
|
+
rgxp = Regexp.new "\\A#{page.directory}/[^/]+"
|
211
211
|
|
212
212
|
keys = @db.keys.find_all {|k| rgxp =~ k}
|
213
213
|
ary = keys.map {|k| @db[k]}
|
@@ -229,7 +229,7 @@ class DB
|
|
229
229
|
# of the current directory or the next directory up the chain.
|
230
230
|
#
|
231
231
|
def parent_of( page )
|
232
|
-
dir = page.
|
232
|
+
dir = page.directory
|
233
233
|
|
234
234
|
loop do
|
235
235
|
if @db.has_key? dir
|
@@ -16,41 +16,32 @@ class Layout < Resource
|
|
16
16
|
def initialize( fn )
|
17
17
|
super
|
18
18
|
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
19
|
+
@_meta_data = MetaFile.meta_data(@path)
|
20
|
+
@_meta_data ||= {}
|
21
|
+
@_meta_data.sanitize!
|
22
22
|
end
|
23
23
|
|
24
|
-
# call-seq:
|
25
|
-
# destination => string
|
26
|
-
#
|
27
|
-
# The output file destination for the layout. This is the ".cairn" file in
|
28
|
-
# the output folder. It is used to determine if the layout is newer than
|
29
|
-
# the build products.
|
30
|
-
#
|
31
|
-
def destination
|
32
|
-
::Webby.cairn
|
33
|
-
end
|
34
|
-
|
35
|
-
# call-seq:
|
36
|
-
# extension => string or nil
|
37
|
-
#
|
38
24
|
# Returns the extension to be applied to output files rendered by the
|
39
25
|
# layotut. This will either be a string or +nil+ if the layout does not
|
40
26
|
# specify an extension to use.
|
41
27
|
#
|
42
28
|
def extension
|
43
|
-
return
|
29
|
+
return _meta_data['extension'] if _meta_data.has_key? 'extension'
|
44
30
|
|
45
|
-
if
|
46
|
-
lyt = ::Webby::Resources.find_layout(
|
47
|
-
|
31
|
+
if _meta_data.has_key? 'layout'
|
32
|
+
lyt = ::Webby::Resources.find_layout(_meta_data['layout'])
|
33
|
+
lyt ? lyt.extension : nil
|
48
34
|
end
|
49
35
|
end
|
50
36
|
|
51
|
-
#
|
52
|
-
#
|
37
|
+
# The output file destination for the layout. This is the ".cairn" file in
|
38
|
+
# the output folder. It is used to determine if the layout is newer than
|
39
|
+
# the build products.
|
53
40
|
#
|
41
|
+
def destination
|
42
|
+
::Webby.cairn
|
43
|
+
end
|
44
|
+
|
54
45
|
# Layouts do not have a URL. This method will alwasy return +nil+.
|
55
46
|
#
|
56
47
|
def url
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Webby::Resources
|
4
|
+
|
5
|
+
# The MetaFile class is used to read meta-data and content from files. The
|
6
|
+
# meta-data is in a YAML block located at the top of the file. The content
|
7
|
+
# is the remainder of the file (everything after the YAML block).
|
8
|
+
#
|
9
|
+
# The meta-data data must be found between two YAML block separators "---",
|
10
|
+
# each on their own line.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# ---
|
15
|
+
# layout: blog
|
16
|
+
# filter: markdown
|
17
|
+
# tags:
|
18
|
+
# - ruby
|
19
|
+
# - web development
|
20
|
+
# ---
|
21
|
+
# This is a blog entry formatted using MarkDown and tagged as "ruby" and
|
22
|
+
# "web development". The layout being used is the "blog" format.
|
23
|
+
#
|
24
|
+
class MetaFile
|
25
|
+
|
26
|
+
class Error < StandardError; end
|
27
|
+
|
28
|
+
META_SEP = %r/\A---\s*(?:\r\n|\n)?\z/ # :nodoc:
|
29
|
+
ERR_MSG = "corrupt meta-data (perhaps there is an errant YAML marker '---' in the file)" # :nodoc:
|
30
|
+
|
31
|
+
# call-seq:
|
32
|
+
# MetaFile.read( filename ) => string
|
33
|
+
#
|
34
|
+
# Opens the file identified by _filename_ and returns the contents of the
|
35
|
+
# file as a string. Any meta-data at the top of the file is skipped and
|
36
|
+
# is not included in the returned string. If the file contains no
|
37
|
+
# meta-data, then this method behaves the same as File#read.
|
38
|
+
#
|
39
|
+
def self.read( name )
|
40
|
+
::File.open(name, 'r') {|fd| MetaFile.new(fd).read}
|
41
|
+
end
|
42
|
+
|
43
|
+
# call-seq:
|
44
|
+
# MetaFile.meta_data( filename ) => object or nil
|
45
|
+
#
|
46
|
+
# Opens the file identified by _filename_ and returns the meta-data
|
47
|
+
# located at the top of the file. If the file contains no meta-data, then
|
48
|
+
# +nil+ is returned.
|
49
|
+
#
|
50
|
+
def self.meta_data( name )
|
51
|
+
::File.open(name, 'r') {|fd| MetaFile.new(fd).meta_data}
|
52
|
+
end
|
53
|
+
|
54
|
+
# call-seq:
|
55
|
+
# MetaFile.meta_data?( filename ) => true or false
|
56
|
+
#
|
57
|
+
# Opens the file identified by _filename_ and returns true if there is a
|
58
|
+
# meta-data block at the top of the file, and returns false if there is
|
59
|
+
# not a meta-data block at the top of the file.
|
60
|
+
#
|
61
|
+
def self.meta_data?( name )
|
62
|
+
::File.open(name, 'r') {|fd| MetaFile.new(fd).meta_data?}
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates a new MetaFile parser that will read from the given _io_ stream.
|
66
|
+
#
|
67
|
+
def initialize( io )
|
68
|
+
raise ArgumentError, "expecting an IO stream" unless io.respond_to? :gets
|
69
|
+
@io = io
|
70
|
+
@meta_count = 0
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the entire contents of the IO stream exluding any meta-data
|
74
|
+
# found at the beginning of the stream.
|
75
|
+
#
|
76
|
+
def read
|
77
|
+
@io.seek(meta_end || 0)
|
78
|
+
@io.read
|
79
|
+
end
|
80
|
+
|
81
|
+
# Reads in each meta-data section and yields it to the given block. The
|
82
|
+
# first meta-data section is yielded "as is", but subsequent meta-data
|
83
|
+
# sections are merged with this first section and then yielded. This
|
84
|
+
# allows the user to define common items in the first meta-data section
|
85
|
+
# and only include items that are different in the subsequent sections.
|
86
|
+
#
|
87
|
+
# Example:
|
88
|
+
#
|
89
|
+
# ---
|
90
|
+
# title: First Title
|
91
|
+
# author: me
|
92
|
+
# directory: foo/bar/baz
|
93
|
+
# ---
|
94
|
+
# title: Second Title
|
95
|
+
# author: you
|
96
|
+
# ---
|
97
|
+
# title: Third Title
|
98
|
+
# author: them
|
99
|
+
# ---
|
100
|
+
#
|
101
|
+
# and parsing the meta-data above yields ...
|
102
|
+
#
|
103
|
+
# meta_file.each do |hash|
|
104
|
+
# pp hash
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# the following output
|
108
|
+
#
|
109
|
+
# { 'title' => 'First Title',
|
110
|
+
# 'author' => 'me',
|
111
|
+
# 'directory' => 'foo/bar/baz' }
|
112
|
+
#
|
113
|
+
# { 'title' => 'Second Title',
|
114
|
+
# 'author' => 'you',
|
115
|
+
# 'directory' => 'foo/bar/baz' }
|
116
|
+
#
|
117
|
+
# { 'title' => 'Third Title',
|
118
|
+
# 'author' => 'them',
|
119
|
+
# 'directory' => 'foo/bar/baz' }
|
120
|
+
#
|
121
|
+
# Even though the "directory" item only appears in the first meta-data
|
122
|
+
# block, it is copied to all the subsequent blocks.
|
123
|
+
#
|
124
|
+
def each
|
125
|
+
return unless meta_data?
|
126
|
+
|
127
|
+
first, count = nil, 0
|
128
|
+
@io.seek 0
|
129
|
+
|
130
|
+
buffer = @io.gets
|
131
|
+
while count < @meta_count
|
132
|
+
while (line = @io.gets) !~ META_SEP
|
133
|
+
buffer << line
|
134
|
+
end
|
135
|
+
|
136
|
+
h = YAML.load(buffer)
|
137
|
+
raise Error, ERR_MSG unless h.instance_of?(Hash)
|
138
|
+
|
139
|
+
if first then h = first.merge(h)
|
140
|
+
else first = h.dup end
|
141
|
+
|
142
|
+
buffer = line
|
143
|
+
count += 1
|
144
|
+
|
145
|
+
yield h
|
146
|
+
end
|
147
|
+
rescue ArgumentError => err
|
148
|
+
msg = ERR_MSG.dup << "\n\t-- " << err.message
|
149
|
+
raise Error, msg
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns the meta-data defined at the top of the file. Returns +nil+ if
|
153
|
+
# no meta-data is defined. The meta-data is returned as Ruby objects
|
154
|
+
#
|
155
|
+
# Meta-data is stored in YAML format between two YAML separators "---" on
|
156
|
+
# their own lines.
|
157
|
+
#
|
158
|
+
def meta_data
|
159
|
+
return if meta_end.nil?
|
160
|
+
|
161
|
+
@io.seek 0
|
162
|
+
return YAML.load(@io)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns true if the IO stream contains meta-data. Returns false if the
|
166
|
+
# IO stream does not contain meta-data.
|
167
|
+
#
|
168
|
+
def meta_data?
|
169
|
+
meta_end.nil? ? false : true
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns the number of meta-data blocks at the top of the file.
|
173
|
+
#
|
174
|
+
def meta_count
|
175
|
+
meta_end
|
176
|
+
@meta_count
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns the position in the IO stream where the meta-data ends and the
|
180
|
+
# regular data begins. If there is no meta-data in the stream, returns +nil+.
|
181
|
+
#
|
182
|
+
def meta_end
|
183
|
+
return @meta_end if defined? @meta_end
|
184
|
+
@meta_end = nil
|
185
|
+
|
186
|
+
@io.seek 0
|
187
|
+
line = @io.read(4)
|
188
|
+
return unless META_SEP =~ line
|
189
|
+
|
190
|
+
@io.seek 0
|
191
|
+
pos = nil
|
192
|
+
buffer = @io.gets.length
|
193
|
+
while line = @io.gets
|
194
|
+
buffer += line.length
|
195
|
+
if META_SEP =~ line
|
196
|
+
pos = buffer
|
197
|
+
@meta_count += 1
|
198
|
+
end
|
199
|
+
end
|
200
|
+
return if pos.nil?
|
201
|
+
|
202
|
+
@meta_end = pos
|
203
|
+
end
|
204
|
+
|
205
|
+
end # class MetaFile
|
206
|
+
end # module Webby::Resources
|
207
|
+
|
208
|
+
# EOF
|