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

Commit

Permalink
Merge pull request #25 from shantytown/11-plugin-options
Browse files Browse the repository at this point in the history
Plugin config
  • Loading branch information
nathankleyn committed Sep 28, 2015
2 parents cc80c66 + a6ac2dd commit f2cd66c
Show file tree
Hide file tree
Showing 25 changed files with 304 additions and 43 deletions.
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ require: rubocop-rspec
LineLength:
Max: 120

Metrics/ModuleLength:
Max: 150

# This is a stupid lint. Yes, module_function is more explanatory than extend self for most use cases. However, the
# former does not work if you want the methods to be able to call private methods; the latter does. See
# https://practicingruby.com/articles/ruby-and-the-singleton-pattern-dont-get-along for a detailed explanation of the
Expand Down
18 changes: 17 additions & 1 deletion lib/shanty/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
require 'i18n'
require 'shanty/info'
require 'shanty/task_set'
require 'shanty/env'
require 'shenanigans/hash/to_ostruct'
require 'deep_merge'

module Shanty
# Public: Handle the CLI interface between the user and the registered tasks
Expand All @@ -11,6 +14,8 @@ class Cli

attr_reader :task_sets

CONFIG_FORMAT = '[plugin]:[key] [value]'

def initialize(task_sets)
@task_sets = task_sets
end
Expand All @@ -28,11 +33,22 @@ def run
program(:description, Info::DESCRIPTION)

setup_tasks
global_config
run!
end

private

def global_config
global_option('-c', '--config [CONFIG]', "Add config via command line in the format #{CONFIG_FORMAT}") do |config|
if (match = config.match(/(?<plugin>\S+):(?<key>\S+)\s+(?<value>\S+)/))
Env.config.merge!(match[:plugin] => { match[:key] => match[:value] })
else
fail(I18n.t('cli.invalid_config_param', actual: config, expected: CONFIG_FORMAT))
end
end
end

def setup_tasks
tasks.each do |name, task|
setup_task(name, task)
Expand Down Expand Up @@ -108,7 +124,7 @@ def enforce_required_options(task, options)
option[:required] && options.send(option_name).nil?
end.keys.join(', ')

abort("missing required params: #{missing}. Use --help for more information.") unless missing.empty?
fail(I18n.t('cli.params_missing', missing: missing)) unless missing.empty?
end
end
end
47 changes: 47 additions & 0 deletions lib/shanty/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
require 'shenanigans/hash/to_ostruct'
require 'deep_merge'

module Shanty
# Public: Configuration class for shanty
class Config
CONFIG_FILE = '.shanty.yml'

def initialize(root, environment)
@root = root
@environment = environment
end

def merge!(new_config)
@config = config_hash.clone.deep_merge(new_config)
end

def [](key)
to_ostruct[key]
end

def method_missing(m)
to_ostruct.send(m) || OpenStruct.new
end

def respond_to?(method_sym, include_private = false)
super || to_ostruct.respond_to?(method_sym, include_private)
end

def to_ostruct
config_hash.to_ostruct
end

private

def config_hash
return @config unless @config.nil?
return @config = {} unless File.exist?(config_path)
config = YAML.load_file(config_path) || {}
@config = config[@environment] || {}
end

def config_path
"#{@root}/#{CONFIG_FILE}"
end
end
end
16 changes: 7 additions & 9 deletions lib/shanty/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
require 'logger'
require 'pathname'
require 'yaml'
require 'shenanigans/hash/to_ostruct'

require 'shanty/config'
require 'shanty/project_tree'

module Shanty
Expand All @@ -28,7 +30,7 @@ def clear!

def require!
Dir.chdir(root) do
(config['require'] || {}).each do |path|
(config['require'] || []).each do |path|
requires_in_path(path).each { |f| require File.join(root, f) }
end
end
Expand Down Expand Up @@ -59,10 +61,10 @@ def project_tree
end

def config
return @@config unless @@config.nil?
return @@config = {} unless File.exist?(config_path)
config = YAML.load_file(config_path) || {}
@@config = config[environment] || {}
@@config ||= Config.new(root, environment)
rescue RuntimeError
# Create config object without .shanty.yml if the project root cannot be resolved
@@config ||= Config.new(nil, environment)
end

private
Expand All @@ -77,10 +79,6 @@ def requires_in_path(path)
end
end

def config_path
"#{root}/#{CONFIG_FILE}"
end

def find_root
fail I18n.t('missing_root', config_file: CONFIG_FILE) if root_dir.nil?
root_dir
Expand Down
19 changes: 18 additions & 1 deletion lib/shanty/plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ module Shanty
class Plugin
include CallMeRuby
include Env
extend Env

def self.inherited(plugin_class)
@plugins ||= []
@plugins << plugin_class.new
end

def self.plugins
(@plugins || [])
end

def self.all_projects
(@plugins || []).flat_map(&:projects).uniq
end
Expand All @@ -22,7 +27,15 @@ def self.all_with_graph(graph)
end

def self.tags(*args)
(@tags ||= []).concat(args.map(&:to_sym))
(@tags ||= [name]).concat(args.map(&:to_sym))
end

def self.option(option, default = nil)
config[name][option] = default if config[name][option].nil?
end

def self.options
config[name]
end

def self.projects(*globs_or_syms)
Expand All @@ -33,6 +46,10 @@ def self.with_graph(&block)
(@with_graph_callbacks ||= []) << block
end

def self.name
to_s.split('::').last.downcase.gsub('plugin', '').to_sym
end

def projects
project_matchers = self.class.instance_variable_get(:@project_matchers)
return [] if project_matchers.nil? || project_matchers.empty?
Expand Down
1 change: 0 additions & 1 deletion lib/shanty/plugins/bundler_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
module Shanty
# Public: Bundler plugin for building ruby projects.
class BundlerPlugin < Plugin
tags :bundler
projects '**/Gemfile'
subscribe :build, :bundle_install

Expand Down
1 change: 0 additions & 1 deletion lib/shanty/plugins/cucumber_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
module Shanty
# Public: Cucumber plugin for testing ruby projects.
class CucumberPlugin < Plugin
tags :cucumber
subscribe :test, :cucumber
# By default, we'll detect Cucumber in a project if has a dependency on it in a Gemfile or *.gemspec file. If you
# don't use these files, you'll need to import the plugin manually using a Shantyfile as we can't tell if RSpec is
Expand Down
1 change: 0 additions & 1 deletion lib/shanty/plugins/rspec_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
module Shanty
# Public: Rspec plugin for testing ruby projects.
class RspecPlugin < Plugin
tags :rspec
subscribe :test, :rspec
# By default, we'll detect RSpec in a project if has a dependency on it in a Gemfile or *.gemspec file. If you don't
# use these files, you'll need to import the plugin manually using a Shantyfile as we can't tell if RSpec is being
Expand Down
1 change: 0 additions & 1 deletion lib/shanty/plugins/rubocop_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
module Shanty
# Public: Rubocop plugin for style checking ruby projects.
class RubocopPlugin < Plugin
tags :rubocop
projects '**/.rubocop.yml'
subscribe :test, :rubocop

Expand Down
2 changes: 1 addition & 1 deletion lib/shanty/plugins/rubygem_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ module Shanty
class RubygemPlugin < Plugin
ARTIFACT_EXTENSION = 'gem'

tags :rubygem
projects '**/*.gemspec'
subscribe :build, :build_gem
tags :gem

def build_gem(project)
gemspec_files(project).each do |file|
Expand Down
1 change: 0 additions & 1 deletion lib/shanty/plugins/shantyfile_plugin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
module Shanty
# Public: Plugin for finding all directories marked with a Shantyfile.
class ShantyfilePlugin < Plugin
tags :shantyfile
projects :shantyfile_projects

def shantyfile_projects
Expand Down
8 changes: 8 additions & 0 deletions lib/shanty/task_sets/basic_task_set.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'fileutils'
require 'i18n'
require 'shanty/task_set'
require 'shanty/plugin'

module Shanty
# Public: A set of basic tasks that can be applied to all projects and that
Expand All @@ -11,6 +12,13 @@ def init
FileUtils.touch(File.join(Dir.pwd, '.shanty.yml'))
end

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

desc 'projects [--tags TAG,TAG,...]', 'tasks.projects.desc'
option :tags, type: :array, desc: 'tasks.common.options.tags'
def projects(options)
Expand Down
3 changes: 2 additions & 1 deletion shanty.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ Gem::Specification.new do |gem|
gem.files = Dir['**/*'].select { |d| d =~ %r{^(README|bin/|ext/|lib/)} }

gem.add_dependency 'acts_as_graph_vertex', '~>1.0'
gem.add_dependency 'bundler', '~>1.10'
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 'deep_merge', '~>1.0'
gem.add_dependency 'gitignore_rb', '~>0.2.2'
gem.add_dependency 'i18n', '~>0.7'
gem.add_dependency 'shenanigans', '~>1.0'
Expand Down
32 changes: 31 additions & 1 deletion spec/lib/shanty/cli_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
require 'commander'
require 'spec_helper'
require 'i18n'
require 'shanty/cli'
require 'shanty/info'
require 'shanty/env'
require 'shenanigans/hash/to_ostruct'
require_fixture 'test_task_set'

# All classes referenced belong to the shanty project
Expand Down Expand Up @@ -94,7 +97,10 @@ module Shanty
end

it('fails to run a command when run without required options') do
expect(subject).to receive(:abort).with('missing required params: catweasel. Use --help for more information.')
# FIXME: Commander catches the exception and rethrows it with extra stuff, we should move away from commander
expect(Commander::Runner.instance).to receive(:abort).with(
include(I18n.t('cli.params_missing', missing: 'catweasel'))
)

ARGV.concat(%w(foo))

Expand All @@ -112,6 +118,30 @@ module Shanty

subject.run
end

it('fails to run a command with config options if config is in incorrect format') do
ARGV.concat(%w(-c nic foo))

expect do
subject.run
end.to raise_error(I18n.t('cli.invalid_config_param', actual: 'nic', expected: Cli::CONFIG_FORMAT))
end

it('runs a command with a config option') do
ARGV.concat(['-c nic:kim cage'])

subject.run

expect(Env.config.nic).to eql({ kim: 'cage' }.to_ostruct)
end

it('runs a command with multiple config options') do
ARGV.concat(['-c nic:kim cage', '-c nic:copolla cage'])

subject.run

expect(Env.config.nic).to eql({ kim: 'cage', copolla: 'cage' }.to_ostruct)
end
end
end
end

0 comments on commit f2cd66c

Please sign in to comment.