# $Id: file.rb 46 2007-11-27 03:31:29Z tim_pease $
require 'yaml'
module Webby
# The Webby::File class is identical to the core Ruby file class except for
# YAML meta-data stored at the top of the file. This meta-data is made
# available through the meta_data
and meta_data=
# functions.
#
# 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.
#
class File < ::File
META_SEP = %r/\A---\s*\r?\n\z/o # :nodoc:
class << self
# call-seq:
# Webby::File.read( name [, length [, offset]]) => string
#
# Opens the file, optionally seeks to the given _offset_, then returns
# _length_ bytes (defaulting to the rest of the file). +read+ ensures
# the file is closed before returning.
#
def read( name, *args )
fd = new name, 'r'
fd.read *args
ensure
fd.close unless fd.nil?
end
# call-seq:
# Webby::File.readlines( name, sep_string = $/ ) => array
#
# Reads the entire file specified by _name_ as individual lines, and
# returns those lines in an array. Lines are separated by _sep_string_.
# +readlines+ ensures the file is closed before returning.
#
def readlines( name, sep = $/ )
fd = new name, 'r'
fd.readlines sep
ensure
fd.close unless fd.nil?
end
# call-seq:
# Webby::File.meta_data( name ) => object or nil
#
# Reads the meta-data from the file specified by _name_. +meta_data+
# ensures the files is closed before returning.
#
def meta_data( name )
fd = new name, 'r'
fd.meta_data
ensure
fd.close unless fd.nil?
end
end
# call-seq:
# Webby::File.new( filename, mode = "r" ) => file
# Webby::File.new( filename [, mode [, perm]] ) => file
#
# Opens the file named by _filename_ according to _mode_ (default is 'r')
# and returns a new +Webby::File+ object. See the description of class
# +IO+ for a description of _mode_. The file _mode_ may optionally be
# specified as a +Fixnum+ by or-ing together the flags (+O_RDONLY+ etc,
# again described under +IO+). Optional permission bits may be given in
# _perm_. These _mode_ and permission bits are platform dependent; on Unix
# systems, see +open(2)+ for details.
#
# f = File.new("testfile", "r")
# f = File.new("newfile", "w+")
# f = File.new("newfile", File::CREAT|File::TRUNC|File::RDWR, 0644)
#
def initialize( *args )
super
@meta_end = end_of_meta_data
end
# call-seq:
# meta_data
#
# 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.
#
def meta_data
return if @meta_end.nil?
cur, meta_end, @meta_end = tell, @meta_end, nil
seek 0
return YAML.load(self)
ensure
@meta_end = meta_end if defined? meta_end and meta_end
seek cur if defined? cur and cur
end
# call-seq
# meta_data = object
#
# Stores the given _object_ as meta-data in YAML format at the top of the
# file. If the _objectc_ is +nil+, then the meta-data section will be
# removed from the file.
#
# Meta-data is stored in YAML format between two YAML separators "---" on
# their own lines.
#
def meta_data=( data )
return if data.nil? and @meta_end.nil?
seek 0
lines = readlines
truncate 0
unless data.nil?
write YAML.dump(data)
write "--- #$/"
end
lines.each {|line| write line}
ensure
@meta_end = end_of_meta_data
seek 0, IO::SEEK_END
end
%w(getc gets read read_nonblock readbytes readchar readline readlines readpartial scanf).each do |m|
self.class_eval <<-CODE
def #{m}(*a)
skip_meta_data
super
end
CODE
end
private
# Moves the file pointer to the end of the meta-data section. Does nothing
# if there is no meta-data section or if the file pointer is already past
# the meta-data section.
#
def skip_meta_data
return if @meta_end.nil?
return if tell >= @meta_end
seek @meta_end
end
# Returns the position in this file where the meta-data ends and the file
# data begins. If there is no meta-data in the file, returns +nil+.
#
def end_of_meta_data
cur = tell
seek 0
line = gets
return unless META_SEP =~ line
while line = gets
break if META_SEP =~ line
end
return if line.nil?
tell
ensure
seek cur
end
end # class File
end # module Webby
# EOF