<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>.gitignore</filename>
    </added>
    <added>
      <filename>AUTHORS</filename>
    </added>
    <added>
      <filename>CHANGELOG</filename>
    </added>
    <added>
      <filename>COPYING</filename>
    </added>
    <added>
      <filename>INSTALL</filename>
    </added>
    <added>
      <filename>LICENSE</filename>
    </added>
    <added>
      <filename>rrbd.gemspec</filename>
    </added>
    <added>
      <filename>setup.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -0,0 +1,22 @@
+= Read Me
+
+by James Edward Gray II
+
+== Description
+
+RRDB is a simple wrapper around RRDtool for managing round robin database files.
+Methods are provided to create new databases, update them with new data, and
+fetch archives back out.
+
+== Documentation
+
+See RRDB for documentation.
+
+== Installing
+
+See the INSTALL file for instructions.
+
+== Questions and/or Comments
+
+Feel free to email {James Edward Gray II}[mailto:james@graysoftinc.com] with any
+questions.</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -1,10 +1,7 @@
 #!/usr/bin/env rake
 
 require &quot;rake/testtask&quot;
-
-dir     = File.dirname(__FILE__)
-lib     = File.join(dir, &quot;lib&quot;, &quot;rrdb.rb&quot;)
-version = File.read(lib)[/^\s*VERSION\s*=\s*(['&quot;])(\d\.\d\.\d)\1/, 2]
+require &quot;rake/rdoctask&quot;
 
 task :default =&gt; [:test]
 
@@ -13,3 +10,12 @@ Rake::TestTask.new do |test|
 	test.test_files =  %w[test/ts_all.rb]
 	test.verbose    =  true
 end
+
+Rake::RDocTask.new do |rdoc|
+	rdoc.main     = &quot;README&quot;
+	rdoc.rdoc_dir = &quot;doc/html&quot;
+	rdoc.title    = &quot;RRDB Documentation&quot;
+	rdoc.rdoc_files.include( *%w[ README  INSTALL CHANGELOG
+	                              AUTHORS COPYING LICENSE
+	                              lib/ ] )
+end</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,9 +1,39 @@
 #!/usr/bin/env ruby -wKU
 
+# = rrdb.rb -- A Round Robin Database Wrapper
+#
+#  Created by James Edward Gray II on 2008-06-19.
+#  Copyright 2008 Gray Productions Software Inc., all rights reserved.
+# 
+# See RRDB for documentation.
+
+#
+# This class wraps an .rrd file on the disk by shelling out to rrdtool to
+# perform read and write actions on the database.  The primary features of this
+# simple wrapper are:
+# 
+# * Each instance manages a separate database keyed on unique ID's you provide
+# * Database creation is delayed until the first update so fields will be known
+# * Extra fields can be reserved in the database and the wrapper will
+#   automatically claim them as needed when new fields appear in future updates
+# * Field names are safely mapped to names acceptable to rrdtool whenever
+#   possible and a method is provided to help you map them back to your
+#   preferred names
+# * Fetch operations return data in time slots, by field name
+# 
+# This class is not multiprocessing safe for write operations.
+# 
 class RRDB
+  # The version number for this release of the code.
   VERSION = &quot;0.0.1&quot;
   
-  def self.const_missing(error_name)
+  #
+  # This method generates Exception subclasses, as needed.  When the code
+  # references any constant ending in Error, a subclass of RuntimeError is built
+  # and assigned to that name.  See the documentation for each method for a list
+  # of the errors it can raise.
+  # 
+  def self.const_missing(error_name)  # :nodoc:
     if error_name.to_s =~ /Error\z/
       const_set(error_name, Class.new(RuntimeError))
     else
@@ -11,6 +41,13 @@ class RRDB
     end
   end
   
+  #
+  # This helper is used to shell out to external command, like rrdtool.  It runs
+  # the command with STDOUT and STDERR merged into a single stream.  If the
+  # command exits successfully, the output from this combined stream is
+  # returned.  Otherwise, +nil+ is returned and you can call last_error() to
+  # retrieve the contents of the stream.
+  # 
   def self.run_command(command)
     output = `#{command} 2&gt;&amp;1`
     if $?.success?
@@ -24,10 +61,56 @@ class RRDB
     nil
   end
   
+  # 
+  # Returns the contents of the combined STDOUT and STDERR stream after a call
+  # to run_command() where the command reported a non-success exit status.  This
+  # method will always return +nil+ after a successful call to run_command().
+  # 
   def self.last_error
     @last_error
   end
   
+  # 
+  # :call-seq:
+  #   config         =&gt; config_hash
+  #   config( hash ) =&gt; updated_config_hash
+  #   config( key  ) =&gt; config_value
+  # 
+  # This method allows you to read and write configuration settings for this
+  # class.  Just pass in a Hash of new settings to have them merged into the
+  # existing configuration.  Recognized settings are:
+  # 
+  # &lt;tt&gt;:rrdtool_path&lt;/tt&gt;::         The path to the rrdtool executable.  This
+  #                                  library will attempt to find it on load,
+  #                                  but you may need to help it along under
+  #                                  some circumstances.
+  # &lt;tt&gt;:database_directory&lt;/tt&gt;::   The directory .rrd files will be stored in.
+  #                                  This defaults to the working directory.
+  # &lt;tt&gt;:reserve_fields&lt;/tt&gt;::       The total number of fields the database
+  #                                  is expected to have.  A number of fields
+  #                                  will be reserved in all databases created
+  #                                  equal to this count minus the count of
+  #                                  fields in the first update for that
+  #                                  database.  These fields will be claimed as
+  #                                  needed by future updates.  Defaults to
+  #                                  &lt;tt&gt;10&lt;/tt&gt;.
+  # &lt;tt&gt;:data_sources&lt;/tt&gt;::         If set to a String, this value will be used
+  #                                  as the Data Source Type for all fields
+  #                                  created.  Alternately, you may set this to
+  #                                  any object with a &lt;tt&gt;[]&lt;/tt&gt; method that
+  #                                  looks up the field and returns a DST String
+  #                                  (Hash and Proc are good examples).  This
+  #                                  defaults to &lt;tt&gt;&quot;GAUGE:600:U:U&quot;&lt;/tt&gt;.
+  # &lt;tt&gt;:round_robin_archives&lt;/tt&gt;:: An Array of RRA statements added to all
+  #                                  databases generated by this library.  (You
+  #                                  don't need to include the &quot;RRA:&quot; prefix.)
+  #                                  This field defaults to an empty Array and 
+  #                                  thus must be set or overriden by your code.
+  # &lt;tt&gt;:database_step&lt;/tt&gt;::        The optional step parameter passed to all
+  #                                  databases created.
+  # &lt;tt&gt;:database_start&lt;/tt&gt;::       If set, this will override the start time
+  #                                  for all databases created.
+  # 
   def self.config(hash_or_key = nil)
     case hash_or_key
     when nil
@@ -39,6 +122,7 @@ class RRDB
     end
   end
   
+  # Default configuration.
   config :rrdtool_path         =&gt; ( run_command(&quot;which rrdtool&quot;) ||
                                     &quot;rrdtool&quot; ).strip,
          :database_directory   =&gt; &quot;.&quot;,
@@ -46,20 +130,43 @@ class RRDB
          :data_sources         =&gt; &quot;GAUGE:600:U:U&quot;,
          :round_robin_archives =&gt; Array.new
   
+  #
+  # Given a field name used in a call to update(), this method will return the
+  # name used inside the .rrd file.  This is helpful for mapping field back to
+  # the values your application prefers.
+  # 
   def self.field_name(name)
     name.to_s.delete(&quot;^a-zA-Z0-9_&quot;)[0..18]
   end
   
+  #
+  # This constructor build a new instance to wrap a round robin database with
+  # the provided unique +id+.  This +id+ will be part of the file name used to
+  # store this database.  If a database with the +id+ already exists, it will
+  # be used for all interactions with the object.  Otherwise, a new database
+  # will be created on the first call to update().
+  # 
   def initialize(id)
     @id = id
   end
   
+  # The unique +id+ for this database instance.
   attr_reader :id
   
+  # 
+  # The path to the disk file representation of this database.  Be warned that
+  # this may not exist yet for a new +id+ where update() has not yet been
+  # called.
+  # 
   def path
     File.join(self.class.config[:database_directory], &quot;#{id}.rrd&quot;)
   end
   
+  #
+  # Returns an Array of field names used in the database, if +include_types+ is
+  # +false+.  When +true+, a Hash is returned mapping field names to their DST.
+  # An empty Array or Hash is returned for uncreated databases.
+  # 
   def fields(include_types = false)
     schema = rrdtool(:info).to_s
     fields = schema.scan(/^ds\[([^\]]+)\]/).flatten.uniq
@@ -77,12 +184,47 @@ class RRDB
     include_types ? Hash.new : Array.new
   end
   
+  #
+  # Returns the step used in this database, or the default 300 for an uncreated
+  # database.
+  # 
   def step
     (rrdtool(:info).to_s[/^step\s+=\s+(\d+)/, 1] || 300).to_i
   rescue InfoError
     300
   end
   
+  #
+  # This method is the interface for adding data to the database.  You pass a
+  # +time+ the data should be recorded under and a +data+ Hash of fields you
+  # wish to store in the database.
+  # 
+  # The first time this method is called for a new database, the database will
+  # be generated to contain the needed fields (plus any extras reserved by the
+  # configuration).  Future calls will claim reserved fields if needed, to
+  # support new field names.  Either way, both types of calls end with the data
+  # being pushed into the database.
+  # 
+  # This method can raise the following errors:
+  # 
+  # &lt;tt&gt;FieldNameConflictError&lt;/tt&gt;:: This error signals that your field names
+  #                                   cannot be cleanly converted into names
+  #                                   RRDtool will accept.  It's possible that
+  #                                   cleaning them resulted in an unacceptable
+  #                                   size or that cleaning them led to
+  #                                   duplicate names.
+  # &lt;tt&gt;FieldsExhaustedError&lt;/tt&gt;::   An attempt to claim new fields was made,
+  #                                   but there are not enough reserved fields
+  #                                   in the database to satisfy the request.
+  # &lt;tt&gt;CreateError&lt;/tt&gt;::            A database could not be created, likely
+  #                                   due to a malformed schema taken from the
+  #                                   configuration settings.
+  # &lt;tt&gt;TuneError&lt;/tt&gt;::              A database could not be modified, again
+  #                                   probably because of a malformed schema.
+  # &lt;tt&gt;UpdateError&lt;/tt&gt;::            The attempt to add data to the database
+  #                                   failed for whatever reason (a time before
+  #                                   the previous update, for example).
+  # 
   def update(time, data)
     safe_data = Hash[*data.map { |f, v| [self.class.field_name(f), v] }.flatten]
     if safe_data.size != data.size or
@@ -102,6 +244,16 @@ class RRDB
     rrdtool(:update, &quot;'#{time.to_i}:#{params.join(':')}'&quot;)
   end
   
+  #
+  # This method is the primary interface for reading data out of the database.
+  # Pass into +field+ the name of the consolidation function you wish to pull
+  # data from.  You may also pass standard RRDtool fetch options in the +range+
+  # Hash (&lt;tt&gt;:start&lt;/tt&gt;, &lt;tt&gt;:end&lt;/tt&gt;, and &lt;tt&gt;:resolution&lt;/tt&gt;).  The return
+  # value is a Hash, keyed by times, where the value for each time is a nested
+  # Hash of fields and their values at that time.
+  # 
+  # This method can raise a FetchError if data cannot be read for any reason.
+  # 
   def fetch(field, range = Hash.new)
     params = &quot;'#{field}' &quot;
     %w[start end resolution].each do |option|
@@ -121,6 +273,15 @@ class RRDB
   
   private
   
+  # 
+  # This method is called by update() to create a non-existent database.  It
+  # requires the starting +time+ for the database as well as the +field_names+
+  # that should be added to the database.  It will use the current configuration
+  # to build DST's, add RRA's, reserve fields, and set a step for the database.
+  # 
+  # This method can raise a CreateError if the database cannot be created due to
+  # an illegal schema.
+  # 
   def create_database(time, field_names)
     schema = String.new
     %w[step start].each do |option|
@@ -141,6 +302,17 @@ class RRDB
     rrdtool(:create, schema.strip)
   end
   
+  # 
+  # This method is called by update() before each attempt to add data to an
+  # existing database.  The +field_names+ for this update will be compared with
+  # the existing fields for the database, and reserved fields are claimed to
+  # make up any differences.  The current configuration will be used to generate
+  # DST's.
+  # 
+  # This method can raise a FieldsExhaustedError if this update() would require
+  # more fields than are currently reserved or a TuneError if the database
+  # schema for the new fields is invalid.
+  # 
   def claim_new_fields(field_names)
     old_fields = fields
     new_fields = field_names - old_fields
@@ -160,6 +332,11 @@ class RRDB
     end
   end
   
+  # 
+  # This helper returns a DST for the passed +field_name+ based on the current
+  # configuration.  If no type is provided by the configuration,
+  # &lt;tt&gt;GAUGE:600:U:U&lt;/tt&gt; will be given as a default.
+  # 
   def field_type(field_name)
     if (setting = self.class.config[:data_sources]).is_a? String
       setting
@@ -168,6 +345,15 @@ class RRDB
     end
   end
   
+  #
+  # This helper shells out to the rrdtool program.  The first argument is the
+  # +command+ to invoke and +params+ is an optional String of command-line
+  # arguments to pass to on.
+  # 
+  # This command generates errors based on the +command+ run.  For example, if
+  # called with the &lt;tt&gt;:create&lt;/tt&gt; command, failures will be raised as
+  # CreateError objects.
+  # 
   def rrdtool(command, params = nil)
     self.class.run_command(
       &quot;#{self.class.config[:rrdtool_path]} #{command} '#{path}' #{params}&quot;</diff>
      <filename>lib/rrdb.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>675ec688a6edb56d3a2a5a1e12b43b496094cf85</id>
    </parent>
  </parents>
  <author>
    <name>James Edward Gray II</name>
    <email>james@graysoftinc.com</email>
  </author>
  <url>http://github.com/JEG2/rrdb/commit/2cbfc46b6a88fc4354018b69a2f89dde65585723</url>
  <id>2cbfc46b6a88fc4354018b69a2f89dde65585723</id>
  <committed-date>2008-06-19T08:43:53-07:00</committed-date>
  <authored-date>2008-06-19T08:43:53-07:00</authored-date>
  <message>Documentation and gem specification.</message>
  <tree>961f9bc7804df0a6f45dec6d06cf68b5884fe8cf</tree>
  <committer>
    <name>James Edward Gray II</name>
    <email>james@graysoftinc.com</email>
  </committer>
</commit>
