Skip to content
This repository has been archived by the owner on Apr 6, 2021. It is now read-only.

Commit

Permalink
Shanty configuration and plugin documentation
Browse files Browse the repository at this point in the history
- create mixin to add configuration to implementing classes
- create class for merging configuration from multiple sources
- add info method to plugin which returns name, description, tags, subscribed tasks and options
- add option to print out plugin information on the command line
  • Loading branch information
janstenpickle committed Oct 5, 2015
1 parent aa266ac commit be273ab
Show file tree
Hide file tree
Showing 23 changed files with 273 additions and 25 deletions.
16 changes: 16 additions & 0 deletions lib/shanty/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Shanty
# Public: Config class for combining configurations from multiple sources
class Config
attr_reader :project_config, :env_config, :plugin_config

def initialize(project_config, env_config, plugin_config)
@project_config = project_config || {}
@env_config = env_config || {}
@plugin_config = plugin_config || {}
end

def [](key)
project_config[key] || env_config[key] || plugin_config[key]
end
end
end
10 changes: 10 additions & 0 deletions lib/shanty/config_mixin.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'active_support/core_ext/hash/indifferent_access'

module Shanty
# Public: Config mixin for use with any class which is configurable
module ConfigMixin
def config
@configuration ||= HashWithIndifferentAccess.new { |h, k| h[k] = HashWithIndifferentAccess.new }
end
end
end
7 changes: 3 additions & 4 deletions lib/shanty/env.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'i18n'
require 'logger'
require 'pathname'
require 'shanty/config_mixin'
require 'shanty/file_tree'
require 'shanty/plugin'
require 'shanty/plugins/shantyfile_plugin'
Expand All @@ -11,6 +12,8 @@
module Shanty
# RWS Monad pattern.
class Env
include ConfigMixin

CONFIG_FILE = 'Shantyconfig'

def root
Expand All @@ -33,10 +36,6 @@ def projects
@projects ||= {}
end

def config
@config ||= Hash.new { |h, k| h[k] = {} }
end

def require(*args)
new_plugins = []
new_task_sets = []
Expand Down
17 changes: 17 additions & 0 deletions lib/shanty/plugin.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
require 'call_me_ruby'
require 'shanty/config'
require 'shanty/config_mixin'
require 'shanty/plugin_dsl'
require 'shanty/project'

module Shanty
# Some basic functionality for every plugin.
class Plugin
include CallMeRuby
extend ConfigMixin
extend PluginDsl

attr_reader :project, :env
Expand All @@ -32,6 +35,16 @@ def self.name
to_s.split('::').last.downcase.gsub('plugin', '').to_sym
end

def self.info
{
name: name,
description: @description,
tags: tags,
subscribes: class_callbacks.keys,
config: config
}
end

# Outside builders

def self.projects(env)
Expand All @@ -54,5 +67,9 @@ def self.find_or_create_project(path, env)
def artifacts(_)
[]
end

def config
@config ||= Config.new(project.config[self.class.name], env.config[self.class.name], self.class.config)
end
end
end
9 changes: 9 additions & 0 deletions lib/shanty/plugin_dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ module Shanty
# Small mixin providing the DSL style class methods that plugin authors will rely on. This module is expected to be
# mixed into a Plugin class.
module PluginDsl
def description(desc)
@description = desc
end

def provides_projects(*syms)
project_providers.concat(syms.map(&:to_sym))
end
Expand All @@ -13,5 +17,10 @@ def provides_projects_containing(*globs)
def provides_tags(*args)
tags.concat(args.map(&:to_sym))
end

def provides_config(key, default = nil)
fail "Default config value for key #{key} is not a string" unless default.is_a?(String) || default.nil?
config[key] = default
end
end
end
1 change: 1 addition & 0 deletions lib/shanty/plugins/bundler_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module Plugins
class BundlerPlugin < Plugin
provides_projects_containing '**/Gemfile'
subscribe :build, :bundle_install
description 'Installs Rubygem dependencies for Ruby projects'

def bundle_install
Bundler.with_clean_env do
Expand Down
1 change: 1 addition & 0 deletions lib/shanty/plugins/cucumber_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class CucumberPlugin < Plugin
# being used from the presence of a spec directory alone (this can be many other testing frameworks!)
provides_projects :cucumber_projects
subscribe :test, :cucumber
description 'Discovers and runs Cucumber tests on Ruby projects'

def self.cucumber_projects(env)
env.file_tree.glob('**/{*.gemspec,Gemfile}').each_with_object([]) do |dependency_file, acc|
Expand Down
1 change: 1 addition & 0 deletions lib/shanty/plugins/rspec_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class RspecPlugin < Plugin
# being used from the presence of a spec directory alone (this can be many other testing frameworks!)
provides_projects :rspec_projects
subscribe :test, :rspec
description 'Discovers and runs RSpec tests on Ruby projects'

def self.rspec_projects(env)
env.file_tree.glob('**/{*.gemspec,Gemfile}').each_with_object([]) do |dependency_file, acc|
Expand Down
1 change: 1 addition & 0 deletions lib/shanty/plugins/rubocop_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Plugins
class RubocopPlugin < Plugin
provides_projects_containing '**/.rubocop.yml'
subscribe :test, :rubocop
description 'Discovers and runs Rubocop on Ruby projects where .rubocop.yml is present'

def rubocop
system 'rubocop'
Expand Down
1 change: 1 addition & 0 deletions lib/shanty/plugins/rubygem_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class RubygemPlugin < Plugin
provides_projects_containing '**/*.gemspec'
provides_tags :gem
subscribe :build, :build_gem
description 'Builds a Rubygem for projects with a gemspec file'

def build_gem
gemspec_files.each do |file|
Expand Down
1 change: 1 addition & 0 deletions lib/shanty/plugins/shantyfile_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Plugins
# Public: Plugin for finding all directories marked with a Shantyfile.
class ShantyfilePlugin < Plugin
provides_projects :shantyfile_projects
description 'Discovers and configures projects with a Shantyfile'

def self.shantyfile_projects(env)
env.file_tree.glob('**/Shantyfile').map do |shantyfile_path|
Expand Down
7 changes: 4 additions & 3 deletions lib/shanty/project.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
require 'acts_as_graph_vertex'
require 'call_me_ruby'
require 'shanty/config_mixin'
require 'shanty/logger'

module Shanty
# Public: Represents a project in the current repository.
class Project
include ActsAsGraphVertex
include ConfigMixin
include Logger

attr_accessor :path, :env, :name, :artifacts, :tags, :config
attr_accessor :path, :env, :name, :artifacts, :tags

# Public: Initialise the Project instance.
#
Expand All @@ -28,7 +30,6 @@ def initialize(path, env)
@artifacts = []
@plugins = []
@tags = []
@config = {}
end

def add_plugin(plugin)
Expand Down Expand Up @@ -96,7 +97,7 @@ def inspect
name: @name,
path: @path,
tags: all_tags,
config: @config,
config: config,
parents: parents.map(&:path)
}.inspect
end
Expand Down
24 changes: 22 additions & 2 deletions lib/shanty/task_sets/basic_task_set.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'erubis'
require 'fileutils'
require 'i18n'
require 'shanty/task_set'
Expand All @@ -15,11 +16,20 @@ def init

desc 'plugins', 'tasks.plugins.desc'
def plugins(_)
Plugin.plugins.each do |plugin|
puts plugin.class.name
env.plugins.each do |plugin|
puts plugin.name
end
end

desc 'plugin [--name PLUGIN]', 'tasks.plugin.desc'
option :name, desc: 'tasks.plugin.options.names'
def plugin(options)
plugin = plugin_index[options.name]

fail I18n.t('tasks.plugin.failed', plugins: env.plugins.map(&:name).join(', ')) if plugin.nil?
print_plugin_info(info)
end

desc 'projects [--tags TAG,TAG,...]', 'tasks.projects.desc'
option :tags, type: :array, desc: 'tasks.common.options.tags'
def projects(options)
Expand Down Expand Up @@ -63,6 +73,16 @@ def scoped_graph
return graph if Dir.pwd == env.root
graph.projects_within_path(Dir.pwd)
end

def plugin_index
@plugin_index ||= env.plugins.each_with_object({}) do |plugin, acc|
acc[plugin.name.to_s] = plugin
end
end

def print_plugin_info(info)
puts Erubis::Eruby.new(File.read(File.join(__dir__, 'plugin.erb'))).result(info)
end
end
end
end
22 changes: 22 additions & 0 deletions lib/shanty/task_sets/plugin.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<% require 'terminal-table' %>
<%= $terminal.color 'PLUGIN NAME', :bold %>:

<%= name %>
<% unless description.nil? %><%= $terminal.color 'DESCRIPTION', :bold %>:

<%= description %>
<% end %><% unless subscribes.empty? %><%= $terminal.color 'TASK SUBSCRIPTIONS', :bold %>:

<%= subscribes.join(', ') %>
<% end %><%= $terminal.color 'TAGS', :bold %>:

<%= tags.join(', ') %>
<% unless config.empty? %><%= $terminal.color 'CONFIGURATION', :bold %>:

<%= Terminal::Table.new(:headings => ['Config Key', 'Default Value'], rows: config) %>
<% end %>
3 changes: 3 additions & 0 deletions shanty.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ Gem::Specification.new do |gem|
gem.executables << 'shanty'
gem.files = Dir['**/*'].select { |d| d =~ %r{^(README|bin/|ext/|lib/)} }

gem.add_dependency 'activesupport', '~>4.2'
gem.add_dependency 'acts_as_graph_vertex', '~>1.0'
gem.add_dependency 'algorithms', '~>0.6'
gem.add_dependency 'bundler', '~>1.10'
gem.add_dependency 'call_me_ruby', '~>1.1'
gem.add_dependency 'commander', '~>4.3'
gem.add_dependency 'erubis', '~> 2.7'
gem.add_dependency 'gitignore_rb', '~>0.2.2'
gem.add_dependency 'i18n', '~>0.7'
gem.add_dependency 'terminal-table', '~>1.5'

gem.add_development_dependency 'coveralls', '~>0.8'
gem.add_development_dependency 'cucumber', '~>2.1'
Expand Down
11 changes: 0 additions & 11 deletions spec/fixtures/test_task_set.rb

This file was deleted.

5 changes: 0 additions & 5 deletions spec/fixtures/test_unused_plugin.rb

This file was deleted.

24 changes: 24 additions & 0 deletions spec/lib/shanty/config_mixin_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'active_support/core_ext/hash/indifferent_access'

RSpec.describe(Shanty::ConfigMixin) do
subject { test_config_class.new }
let(:test_config_class) { Class.new.tap { |klass| klass.include(described_class) } }

describe('#config') do
it('returns a hash with indifferent access') do
expect(subject.config).to be_a(HashWithIndifferentAccess)
end

it('creates a new hash when key does not exist') do
expect(subject.config[:nic]).to be_a(HashWithIndifferentAccess)
expect(subject.config[:nic]).to be_empty
end

it('allows setting of plugin config') do
subject.config[:nic][:kim] = 'cage'

expect(subject.config[:nic]).to eql('kim' => 'cage')
expect(subject.config[:nic][:kim]).to eql('cage')
end
end
end
56 changes: 56 additions & 0 deletions spec/lib/shanty/config_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'active_support/core_ext/hash/indifferent_access'
require 'shanty/config'

RSpec.describe(Shanty::Config) do
subject { described_class.new(project_config, env_config, plugin_config) }

describe('#[]') do
let(:project_config) { HashWithIndifferentAccess.new }
let(:env_config) { HashWithIndifferentAccess.new }
let(:plugin_config) { HashWithIndifferentAccess.new }

it('returns nothing when no config is present') do
expect(subject[:nic]).to be_nil
end

it('returns default config in plugin') do
plugin_config[:nic] = 'cage'

expect(subject[:nic]).to eql('cage')
end

it('returns config in environment') do
env_config[:nic] = 'cage'

expect(subject[:nic]).to eql('cage')
end

it('returns config in project') do
project_config[:nic] = 'cage'

expect(subject[:nic]).to eql('cage')
end

it('overrides environment config with plugin config') do
plugin_config[:nic] = 'copolla'
env_config[:nic] = 'cage'

expect(subject[:nic]).to eql('cage')
end

it('overrides environment config with project config') do
env_config[:nic] = 'copolla'
project_config[:nic] = 'cage'

expect(subject[:nic]).to eql('cage')
end

it('overrides environment and plugin config with project config') do
plugin_config[:nic] = 'kim'
env_config[:nic] = 'copolla'
project_config[:nic] = 'cage'

expect(subject[:nic]).to eql('cage')
end
end
end

0 comments on commit be273ab

Please sign in to comment.