Class: Webby::Resources::MetaFile

Inherits:
Object
  • Object
show all
Defined in:
lib/webby/resources/meta_file.rb

Overview

The MetaFile class is used to read meta-data and content from files. The meta-data is in a YAML block located at the top of the file. The content is the remainder of the file (everything after the YAML block).

The meta-data data must be found between two YAML block separators “—”, each on their own line.

Example:

---
layout: blog
filter: markdown
tags:
  - ruby
  - web development
---
This is a blog entry formatted using MarkDown and tagged as "ruby" and
"web development". The layout being used is the "blog" format.

Defined Under Namespace

Classes: Error

Constant Summary collapse

META_SEP =

:nodoc:

%r/\A---\s*(?:\r\n|\n)?\z/
ERR_MSG =

:nodoc:

"corrupt meta-data (perhaps there is an errant YAML marker '---' in the file)"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(io) ⇒ MetaFile

Creates a new MetaFile parser that will read from the given io stream.

Raises:

  • (ArgumentError)


67
68
69
70
71
# File 'lib/webby/resources/meta_file.rb', line 67

def initialize( io )
  raise ArgumentError, "expecting an IO stream" unless io.respond_to? :gets
  @io = io
  @meta_count = 0
end

Class Method Details

.meta_data(name) ⇒ Object

call-seq:

MetaFile.( filename )    => object or nil

Opens the file identified by filename and returns the meta-data located at the top of the file. If the file contains no meta-data, then nil is returned.



50
51
52
# File 'lib/webby/resources/meta_file.rb', line 50

def self.( name )
  ::File.open(name, 'r') {|fd| MetaFile.new(fd).}
end

.meta_data?(name) ⇒ Boolean

call-seq:

MetaFile.meta_data?( filename )    => true or false

Opens the file identified by filename and returns true if there is a meta-data block at the top of the file, and returns false if there is not a meta-data block at the top of the file.

Returns:

  • (Boolean)


61
62
63
# File 'lib/webby/resources/meta_file.rb', line 61

def self.meta_data?( name )
  ::File.open(name, 'r') {|fd| MetaFile.new(fd).meta_data?}
end

.read(name) ⇒ Object

call-seq:

MetaFile.read( filename )    => string

Opens the file identified by filename and returns the contents of the file as a string. Any meta-data at the top of the file is skipped and is not included in the returned string. If the file contains no meta-data, then this method behaves the same as File#read.



39
40
41
# File 'lib/webby/resources/meta_file.rb', line 39

def self.read( name )
  ::File.open(name, 'r') {|fd| MetaFile.new(fd).read}
end

Instance Method Details

#eachObject

Reads in each meta-data section and yields it to the given block. The first meta-data section is yielded “as is”, but subsequent meta-data sections are merged with this first section and then yielded. This allows the user to define common items in the first meta-data section and only include items that are different in the subsequent sections.

Example:

---
title:      First Title
author:     me
directory:  foo/bar/baz
---
title:      Second Title
author:     you
---
title:      Third Title
author:     them
---

and parsing the meta-data above yields …

meta_file.each do |hash|
  pp hash
end

the following output

{ 'title' => 'First Title',
  'author' => 'me',
  'directory' => 'foo/bar/baz' }

{ 'title' => 'Second Title',
  'author' => 'you',
  'directory' => 'foo/bar/baz' }

{ 'title' => 'Third Title',
  'author' => 'them',
  'directory' => 'foo/bar/baz' }

Even though the “directory” item only appears in the first meta-data block, it is copied to all the subsequent blocks.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/webby/resources/meta_file.rb', line 126

def each
  return unless meta_data?

  first, count = nil, 0
  @io.seek 0

  buffer = @io.gets
  while count < @meta_count
    while (line = @io.gets) !~ META_SEP
      buffer << line
    end

    h = YAML.load(buffer)
    raise Error, ERR_MSG unless h.instance_of?(Hash)

    if first then h = first.merge(h)
    else first = h.dup end

    buffer = line
    count += 1

    yield h
  end
rescue ArgumentError => err
  msg = ERR_MSG.dup << "\n\t-- " << err.message
  raise Error, msg
end

#meta_countObject

Returns the number of meta-data blocks at the top of the file.



176
177
178
179
# File 'lib/webby/resources/meta_file.rb', line 176

def meta_count
  meta_end
  @meta_count
end

#meta_dataObject

Returns the meta-data defined at the top of the file. Returns nil if no meta-data is defined. The meta-data is returned as Ruby objects

Meta-data is stored in YAML format between two YAML separators “—” on their own lines.



160
161
162
163
164
165
# File 'lib/webby/resources/meta_file.rb', line 160

def 
  return if meta_end.nil?

  @io.seek 0
  return YAML.load(@io)
end

#meta_data?Boolean

Returns true if the IO stream contains meta-data. Returns false if the IO stream does not contain meta-data.

Returns:

  • (Boolean)


170
171
172
# File 'lib/webby/resources/meta_file.rb', line 170

def meta_data?
  meta_end.nil? ? false : true
end

#meta_endObject

Returns the position in the IO stream where the meta-data ends and the regular data begins. If there is no meta-data in the stream, returns nil.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/webby/resources/meta_file.rb', line 184

def meta_end
  return @meta_end if defined? @meta_end
  @meta_end = nil

  @io.seek 0
  line = @io.read(4)
  return unless META_SEP =~ line

  @io.seek 0
  @io.gets
  pos, count = nil, 1

  while line = @io.gets
    count += 1
    if META_SEP =~ line
      pos = count
      @meta_count += 1
    end
  end
  return if pos.nil?

  @meta_end = pos
end

#readObject

Returns the entire contents of the IO stream exluding any meta-data found at the beginning of the stream.



76
77
78
79
80
81
# File 'lib/webby/resources/meta_file.rb', line 76

def read
  count = meta_end
  @io.seek 0
  count.times {@io.gets} unless count.nil?
  @io.read
end