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