Brief

Brief is a tool for building applications on top of collections of documents written in markdown.

Brief lets you define models very similar to how you would in ActiveRecord and Rails, but instead of rows in a database you are going to be working with files in a folder.

Getting Started

gem install brief
brief --help

Hypothetical Example

brief init my-cookbook
cd my-cookbook
brief generate model Recipe # View the DOCUMENTATION for what you can do with a model
brief write recipe # => opens up your $EDITOR with an example recipe

Brief treats each markdown file as an active record style model object. It treats a folder of markdown files like a database.

How does it work?

Brief takes a markdown file which looks like:

---
type: post
status: active
---

# An introduction to brief 
## Stop writing dead documents 

Brief is a tool which lets you define different patterns of markdown
headings (h1,h2,h3 etc). This lets you work with collections of your
writings, and treat each document as a database. 

And turns it into data, not only using the metadata up top, but also the information contained in the structure of the document itself. The headings, subheadings, pretty much anything you could query from the HTML using CSS can be turned into key value pairs that you can work with and build applications on top of.

With your enhanced writing, you can do things like:

# Find all the posts which are active:
posts = briefcase.posts.where(status:"published")

# Get their titles, and subheadings:
posts.map(&:title) #=>['An introduction to brief']
posts.map(&:subheading) #=>['Stop writing dead documents']

# Publish the post
posts.first.publish()

# Email the drafts to your editor
posts.where(status:"draft").each &:mail_to_editors

From the Command line we can:

brief publish posts 
brief write post #=> Opens your editor

This type of interactivity is made possible by the Brief::Model

Documents as models

define "Post" do
  meta do
    status
  end

  content do
    title "h1:first-of-type"
    subheading "h2:first-of-type"
    sample "p:first-of-type"
  end

  actions do
    def publish
      update_attributes(status: "published")
    end
  end
end

CLI Tool

Brief gives you a CLI called brief

This lets you run some general commands, but also gives you an intelligent interface to work with your models. In the above example, we defined some actions.

This will be available in the CLI:

brief publish posts ./docs/posts/*.md

This will find all of the matching documents, turn them into Post models, and then run the publish method on them. You can make your models as advanced as you want:

define "Post" do
  actions do
    def submit 
      update_attributes(status:"submitted to editors")
      mailer.send(:to => "[email protected]", :subject => "Please review: #{ self.title }")
    end
  end
end

YAML Frontmatter

Each markdown document can contain YAML frontmatter. This data will be available and associated with each document or model, and will also let you query, filter, and sort your documents.

CSS Structure Definition

Since markdown renders into HTML, and HTML Documents can be queried using CSS selectors, we use CSS selectors in our model definition DSL so that we can isolate certain parts of the document, and use the content it contains as metadata.

A real world example:

  content do
    title "h1:first-of-type"
    define_section "User Stories" do
      each("h2").has(:title     => "h2",
                     :paragraph => "p:first-of-type",
                     :components   => "p:first-of-type strong"
                    )

      each("h2").is_a :user_story
    end
  end

This lets me turn a markdown file like:

---
title: Epic Example
status: published
---
# Epic Example
# User Stories

## A user wants to do something
As a **User** I would like to **Do this** so that I can **succeed**

## A user wants to do something else
As a **User** I would like to **Do this** so that I can **succeed**

into:

{
  title: "Epic Example",
  status: "published",
  type: "epic",
  user_stories:[{
    title: "A user wants to do something",
    paragraph: "As a user I would like to do something so that I can succeed",
    goal: "I can succeed",
    persona: "user",
    behavior: "do something"
  },{
    title: "A user wants to do something else",
    paragraph: "As a user I would like to do something else so that I can succeed"
  }]
}

And we can even go in the other direction.