18F/weekly_snippets

View on GitHub
README.md

Summary

Maintainability
Test Coverage
## weekly_snippets Gem

[![Gem Version](https://badge.fury.io/rb/weekly_snippets.svg)](https://badge.fury.io/rb/weekly_snippets)
[![Build Status](https://travis-ci.org/18F/weekly_snippets.svg?branch=master)](https://travis-ci.org/18F/weekly_snippets)
[![Code Climate](https://codeclimate.com/github/18F/weekly_snippets/badges/gpa.svg)](https://codeclimate.com/github/18F/weekly_snippets)
[![Test Coverage](https://codeclimate.com/github/18F/weekly_snippets/badges/coverage.svg)](https://codeclimate.com/github/18F/weekly_snippets)

Standardizes different weekly snippet formats into a common format,
[munges](http://en.wikipedia.org/wiki/Mung_(computer_term)) snippet text
according to user-supplied rules, performs redaction of internal information,
and publishes snippets in plaintext or Markdown format.

Downloads and API docs are available on the [weekly_snippets RubyGems
page](https://rubygems.org/gems/weekly_snippets). API documentation is written
using [YARD markup](http://yardoc.org/).

Contributed by the 18F team, part of the United States General Services
Administration: https://18f.gsa.gov/

### Motivation

This gem was extracted from [the 18F Hub Joiner
plugin](https://github.com/18F/hub/blob/master/_plugins/joiner.rb). That
plugin manipulates [Jekyll-imported data](http://jekyllrb.com/docs/datafiles/)
by removing or promoting private data, building indices, and performing joins
between different data files so that the results appear as unified collections
in Jekyll's `site.data` object. It serves as the first stage in a pipeline
that also builds cross-references and canonicalizes data before generating
static HTML pages and other artifacts.

### Installation

Add this line to your application's Gemfile:

```ruby
gem 'weekly_snippets'
```

And then execute:
```
$ bundle
```

Or install it yourself as:
```
$ gem install weekly_snippets
```

### Usage

The [18F Hub](https://github.com/18F/hub) processes snippet data as [CSV
files](https://en.wikipedia.org/wiki/Comma-separated_values) harvested from a
web-based spreadsheet, stored using [timestamped
filenames](http://en.wikipedia.org/wiki/ISO_8601#Calendar_dates) in the
[Jekyll _data folder](http://jekyllrb.com/docs/datafiles/). Since we have
experimented with different CSV column formats, we keep the data files
corresponding to each version in separate directories:

```
$ ls -1d _data/private/snippets/*
_data/private/snippets/v1/
_data/private/snippets/v2/
_data/private/snippets/v3/
```

The content of the lattermost `v3` directory as of writing:

```
$ ls -1 _data/private/snippets/v3
20141208.csv
20141215.csv
20141222.csv
```

With this data in-place, the Hub performs the following steps:

#### Standardize versions

The [18F Hub joiner.rb
plugin](https://github.com/18F/hub/blob/master/_plugins/joiner.rb) defines
this map from version names to `Version` objects:

```ruby
# Used to standardize snippet data of different versions before joining
# and publishing.
SNIPPET_VERSIONS = {
  'v1' => WeeklySnippets::Version.new(
    version_name:'v1',
    field_map:{
      'Username' => 'username',
      'Timestamp' => 'timestamp',
      'Name' => 'full_name',
      'Snippets' => 'last-week',
      'No This Week' => 'this-week',
    }
  ),
  'v2' => WeeklySnippets::Version.new(
    version_name:'v2',
    field_map:{
      'Timestamp' => 'timestamp',
      'Public vs. Private' => 'public',
      'Last Week' => 'last-week',
      'This Week' => 'this-week',
      'Username' => 'username',
    },
    markdown_supported: true
  ),
  'v3' => WeeklySnippets::Version.new(
    version_name:'v3',
    field_map:{
      'Timestamp' => 'timestamp',
      'Public' => 'public',
      'Username' => 'username',
      'Last week' => 'last-week',
      'This week' => 'this-week',
    },
    public_field: 'public',
    public_value: 'Public',
    markdown_supported: true
  ),
}
```

This map is then used to standardize batches of weekly snippets, converting
each different version to a common format, before joining the data with team
member information:

```ruby
# Snippet data is expected to be stored in files matching the pattern:
# _data/@source/snippets/[version]/[YYYYMMDD].csv
#
# resulting in the initial structure:
# site.data[@source][snippets][version][YYYYMMDD] = Array<Hash>
#
# After this function returns, the `standardized` will be of the form:
# site.data[snippets][YYYYMMDD] = Array<Hash>
standardized = ::WeeklySnippets::Version.standardize_versions(
  @data[@source]['snippets'], snippet_versions)
```

#### Munge

To accommodate the preferred formats employed by some team members, the
[18F Hub snippets.rb plugin](https://github.com/18F/hub/blob/master/_plugins/snippets.rb)
defines a Ruby block to
[munge](http://en.wikipedia.org/wiki/Mung_(computer_term)) the snippet data
before converting it to a uniform
[Markdown](http://daringfireball.net/projects/markdown/syntax) representation:

```ruby
MARKDOWN_SNIPPET_MUNGER = Proc.new do |text|
  text.gsub!(/^::: (.*) :::$/, "#{HEADLINE} \\1") # For jtag. ;-)
  text.gsub!(/^\*\*\*/, HEADLINE) # For elaine. ;-)
end
```

This block is then passed as an argument to `WeeklySnippets::Publisher.new()`,
discussed in the **Publish** section below.

#### Redact internal info

Text that should be available when published internally, but redacted from
publicly-published snippets, can be surrounded by `{{` and `}}` tokens:

```ruby
> require 'weekly_snippets/publisher'

# Instantiate a publisher for internally-visible snippets
> publisher = WeeklySnippets::Publisher.new(
    headline: "\n####", public_mode: false)

> snippets = [
    '- Did stuff{{ including private details}}',
    '{{- Did secret stuff}}',
    '- Did more stuff',
    '{{- Did more secret stuff',
    '- Yet more secret stuff}}',
  ]

# For internally-visible snippets, the text inside the `{{` and `}}`
# tokens will be preserved.
> puts publisher.redact! snippets.join("\n")

- Did stuff including private details
- Did secret stuff
- Did more stuff
- Did more secret stuff
- Yet more secret stuff

# Instantiate a publisher for publicly-visible snippets
> publisher = WeeklySnippets::Publisher.new(
    headline: "\n####", public_mode: true)

# For publicly-visible snippets, the text inside the `{{` and `}}`
# tokens will be removed.
> puts publisher.redact! snippets.join("\n")

- Did stuff
- Did more stuff
```

`WeeklySnippets::Publisher::publish()` automatically calls
`WeeklySnippets::Publisher::redact!`, so it shouldn't be necessary to call it
directly.

#### Publish

This is how the 
[18F Hub snippets.rb plugin](https://github.com/18F/hub/blob/master/_plugins/snippets.rb)
creates a `WeeklySnippets::Publisher` object and uses its `publish()` method
to process snippets and store the result in the [Jekyll site.data
object](http://jekyllrb.com/docs/datafiles/):

```ruby
publisher = ::WeeklySnippets::Publisher.new(
  headline: HEADLINE, public_mode: site.config['public'],
  markdown_snippet_munger: MARKDOWN_SNIPPET_MUNGER)
site.data['snippets'] = publisher.publish site.data['snippets']
```

### Contributing

1. Fork the repo ( https://github.com/18F/weekly_snippets/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

Feel free to ping [@mbland](https://github.com/mbland) with any questions you
may have, especially if the current documentation should've addressed your
needs, but didn't.

### Public domain

This project is in the worldwide [public domain](LICENSE.md). As stated in
[CONTRIBUTING](CONTRIBUTING.md):

> This project is in the public domain within the United States, and copyright
> and related rights in the work worldwide are waived through the
> [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
>
> All contributions to this project will be released under the CC0 dedication.
> By submitting a pull request, you are agreeing to comply with this waiver of
> copyright interest.