class Readme

Readme is designed to parse a README file applying various hueristics in order to descern metadata about a project.

The heuristics are fairly simplistic at this point, but will improve with time and contribution.

Constants

FILE_PATTERN

File glob for matching README file.

VERSION

Attributes

file[R]

The ERADME file path, if provided.

text[R]

The README text.

Public Class Methods

cli(*argv) click to toggle source
# File lib/readme/cli.rb, line 5
def self.cli(*argv)
  format = nil
  name   = nil

  OptionParser.new do |opt|
    opt.banner = 'Usage: readme [option] [field]'
    opt.on('-y', '--yml', '--yaml', 'return in YAML format') do
      format = :yaml
    end
    opt.on('-j', '--json', 'return in JSON format') do
      format = :json
    end
    opt.on('-r', '--ruby', 'return in Ruby code format') do
      format = :ruby
    end
    opt.on_tail('-h', '--help', 'show this help message') do
      puts opt
      exit
    end
  end.parse!(argv)
 
  if argv.first
    name = argv.shift
    data = Readme.file[name]
  else
    if format
      data = Readme.file.to_h
    else
      data = Readme.file
    end
  end

  case format
  when :yaml
    require 'yaml'
    puts data.to_yaml
  when :json
    require 'json'
    puts data.to_json
  when :ruby
    data = {name => data} if name
    data.each do |k,v|
      case v
      when Hash
        puts "#{k} #{v.inspect[1...-1]}"
      else
        puts "#{k} #{v.inspect}"
      end
    end
  else
    puts data #Readme.file.to_s
  end
end
file(path=Dir.pwd) click to toggle source
# File lib/readme.rb, line 21
def self.file(path=Dir.pwd)
  if File.directory?(path)
    path = Dir.glob(File.join(path, FILE_PATTERN), File::FNM_CASEFOLD).first
  end
  if path
    new(File.read(path), path)
  else
    raise IOError, "no such README -- #{path}"
  end
end
new(text, file=nil) click to toggle source
# File lib/readme.rb, line 33
def initialize(text, file=nil)
  @text  = text
  @file  = file
  @data = {}
  parse
end

Public Instance Methods

[](name) click to toggle source

Access to underlying parse table.

# File lib/readme.rb, line 71
def [](name)
  @data[name.to_s]
  #return nil unless file
  #if respond_to?(name)
  #  __send__(name)
  #else
  #  nil
  #end
end
authors() click to toggle source
# File lib/readme.rb, line 107
def authors
  @data['authors']
end
description() click to toggle source
# File lib/readme.rb, line 92
def description
  @data['description']
end
extname() click to toggle source

Return file extension of README. Even if the file has no extension, this method will look at the contents and try to determine it.

@todo Improve type heuristics.

@return [String] Extension type, e.g. ‘.md`.

# File lib/readme.rb, line 139
def extname
  ext = File.extname(file)
  if ext.empty?
    ext = '.rdoc' if /^\=/ =~ text
    ext = '.md'   if /^\#/ =~ text
  end
  return ext
end
homepage() click to toggle source
# File lib/readme.rb, line 117
def homepage
  resources['home']
end
issues() click to toggle source
# File lib/readme.rb, line 127
def issues
  resources['issues']
end
license() click to toggle source
# File lib/readme.rb, line 97
def license
  @data['license']
end
name() click to toggle source
# File lib/readme.rb, line 82
def name
  @data['name']
end
resources() click to toggle source
# File lib/readme.rb, line 112
def resources
  @data['resources'] ||= {}
end
root() click to toggle source

Location of README file, if file was provided.

@return [String] Directory of README file

# File lib/readme.rb, line 55
def root
  File.dirname(file) if file
end
title() click to toggle source
# File lib/readme.rb, line 87
def title
  @data['title']
end
to_h() click to toggle source

Access to a copy of the underlying parse table.

@return [Hash] Copy of the underlying table.

# File lib/readme.rb, line 153
def to_h
  @data.dup
end
to_s() click to toggle source

The full README text.

@return [String] The complete README text.

# File lib/readme.rb, line 64
def to_s
  text.to_s
end
wiki() click to toggle source
# File lib/readme.rb, line 122
def wiki
  resources['wiki']
end

Private Instance Methods

parse() click to toggle source
# File lib/readme.rb, line 160
def parse
  parse_title
  parse_description
  parse_license
  parse_copyright
  parse_resources
end
parse_description() click to toggle source
# File lib/readme.rb, line 178
def parse_description
  if md = /[=#]+\s*(DESCRIPTION|ABSTRACT)[:]*(.*?)[=#]/mi.match(text)
    @data['description'] = md[2].strip #.sub("\n", ' ')  # unfold instead of sub?
  else
    d = []
    o = false
    text.split("\n").each do |line|
      if o
        if /^(\w|\s*$)/ !~ line
          break d
        else
          d << line
        end
      else
        if /^\w/ =~ line
          d << line
          o = true
        end
      end
    end
    @data['description'] = d.join(' ').strip
  end
end
parse_license() click to toggle source
# File lib/readme.rb, line 203
def parse_license
  if md = /[=]+\s*(LICENSE)/i.match(text)
    section = md.post_match
    @data['license'] = (
      case section
      when /LGPL/
        "LGPL"
      when /GPL/
        "GPL"
      when /MIT/
        "MIT"
      when /BSD/
        "BSD"
      end
    )
  end
end
parse_resources() click to toggle source
# File lib/readme.rb, line 236
def parse_resources
  @data['resources'] = {}

  scan_for_github
  scan_for_google_groups

  text.scan(/(\w+)\:\s*(http:.*?[\w\/])$/) do |m|
    @data['resources'][$1] = $2
  end
end
parse_title() click to toggle source
# File lib/readme.rb, line 169
def parse_title
  if md = /^[=#]\s*(.*?)$/m.match(text)
    title = md[1].strip
    @data['title'] = title
    @data['name']  = title.downcase.gsub(/\s+/, '_')
  end
end
scan_for_github() click to toggle source

TODO: Improve on github matching.

# File lib/readme.rb, line 249
def scan_for_github
  text.scan(/http\:.*?github\.com.*?[">)\s]/) do |m|
    case m
    when /wiki/
      @data['resources']['wiki'] = m[0...-1]
    when /issues/
      @data['resources']['issues'] = m[0...-1]
    else
      if m[0] =~ /:\/\/github/
        @data['resources']['code'] = m[0...-1]
      else
        @data['resources']['home'] = m[0...-1]
      end
    end
  end
end
scan_for_google_groups() click to toggle source
# File lib/readme.rb, line 267
def scan_for_google_groups
  if m = /http\:.*?groups\.google\.com.*?[">)\s]/.match(text)
    @data['resources']['mail'] = m[0][0...-1]
  end
end