Binder is an experimental framework for writing basic data compilers. Binder provides hooks for custom parsers which control how filesystem data is converted to native Ruby objects.

Binder began as an attempt to create a light weight filesystem based CMS that works with GIT. It progressed into a more generic framework to accommodate other ideas for how compiling data from the filesystem could be useful. The system is designed to make it as easy as possible to prototype those ideas.

n.b. Please keep in mind, the term 'file' can refer to either a single file or a directory

How it works

The compiler takes care of traversing the files system, all you have to do is set rules for how it should parse specific kinds of files. You do this by defining a Parser. The definition requires two parameters, first a reference to your parser class and second a test block which will run against a file context. If the test block is run and returns true then your parser class is applied to the file and no further parser definitions are tested on that file. If no parser is matched then the file is effectively skipped.

Once a Parser has been applied its job is to obtain, format and output data from that file. If the parser does nothing, again the file is essentially skipped. Binder provides a small but powerful set of functionality to your parser. You have access to the file to read it into memory whatever way you like.

descend

If the file is a directory you can instruct the compiler to step in by calling 'descend'. It returns the full output data from that operation.

create_asset

You can also choose to add a file to your output as a reference rather than actual data by calling 'new_asset'.

add

Once you are done and have your output data you must 'add' it. From that point it is the responsibility of the the parent (folder) parser to pass that data down the chain.

Binder comes with some very basic default parsers defined which you are free to extend. These handle reading utf text files, descending folders and adding images files as assets. You can/should be overriding these by adding your own.

Tip: A parser that descends into a folder can itself create a whole set of local parser definitions only to be applied within the that folder and its children... powerful stuff ;)

Configure the compiler

You can configure the compiler with the following options:

BinderCore::Compiler.configure do
  config.content_folder =  "file/path/to/input/root/folder/"
  config.base_asset_folder =  "file/path/to/asset/output/folder/"
  config.base_asset_url = "/public/asset/base/path/"
  # size limit is a safety valve, 100 by default
  config.folder_size_limit_mb = 100
end

Define a Binder and add Parsers

Parsers are added with a test block. That test block is passed a FileContext during the compile process. If it returns true then this parser (and only this parser) is applied to that file.

BinderCore.define :my_feed do |config|
  config.add_parser MyCustomParsers::FeedRootFolderParser do |context|
    context.route == []
  end
end

Create a custom Parser

module MyCustomParsers
  class FeedRootFolderParser < BinderCore::Parser
    # implement a parse method
    def parse
      # this is a directory so tell the compiler to descend into it
      # it returns the compiled data
      data = descend do |config|
        # you can add parsers within parsers
        # these are only applied to descendent files/folder
        config.add_parser MyCustomMarkdownParser do |context|
          context.file.ext.downcase == ".markdown"
        end
      end
      # add the data object to the feed
      add data
    end
  end
end

Assets

You can add a file as an asset to the data object. After compiling an optional asset linking phase will replace all the asset references with a url string. Here is how to add an asset in your parse method:

def parse
  add new_asset
end

Your binder object provides you with an array of all the assets and you can handle those whatever way you want.

Compile a feed

feed_binder = BinderCore.compile :my_feed
feed_binder.link_assets
data = feed_binder.data
assets = feed_binder.assets

If you want more information about how this works, its all there in the source code. Any questions or feedback give me a shout.

Idea Chalkboard:

This is a TODO...