captain_hoog 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +172 -0
  6. data/Rakefile +2 -0
  7. data/bin/githoog +110 -0
  8. data/captain_hoog.gemspec +38 -0
  9. data/features/failed_hook.feature +14 -0
  10. data/features/installing_hook.feature +35 -0
  11. data/features/moving_hooks.feature +34 -0
  12. data/features/passed_hook.feature +12 -0
  13. data/features/removing_hook.feature +33 -0
  14. data/features/support/env.rb +43 -0
  15. data/features/support/steps/hooks_steps.rb +63 -0
  16. data/features/support/world.rb +30 -0
  17. data/lib/captain_hoog/delegatable.rb +33 -0
  18. data/lib/captain_hoog/dependencies.rb +2 -0
  19. data/lib/captain_hoog/env.rb +17 -0
  20. data/lib/captain_hoog/errors/dsl_errors.rb +6 -0
  21. data/lib/captain_hoog/errors.rb +1 -0
  22. data/lib/captain_hoog/git.rb +72 -0
  23. data/lib/captain_hoog/helper_table.rb +23 -0
  24. data/lib/captain_hoog/plugin.rb +67 -0
  25. data/lib/captain_hoog/plugin_list.rb +29 -0
  26. data/lib/captain_hoog/pre_git.rb +125 -0
  27. data/lib/captain_hoog/templates/hoogfile.erb +39 -0
  28. data/lib/captain_hoog/templates/install.erb +20 -0
  29. data/lib/captain_hoog/version.rb +3 -0
  30. data/lib/captain_hoog.rb +14 -0
  31. data/spec/fixtures/code/helper.rb +13 -0
  32. data/spec/fixtures/plugins/shared/passing/simple_shared.rb +11 -0
  33. data/spec/fixtures/plugins/test_plugins/failing/simple.rb +15 -0
  34. data/spec/fixtures/plugins/test_plugins/passing/simple.rb +11 -0
  35. data/spec/fixtures/pre-commit-fail +25 -0
  36. data/spec/fixtures/pre-commit-pass +25 -0
  37. data/spec/lib/captain_hoog/env_spec.rb +31 -0
  38. data/spec/lib/captain_hoog/git_spec.rb +185 -0
  39. data/spec/lib/captain_hoog/plugin_list_spec.rb +42 -0
  40. data/spec/lib/captain_hoog/plugin_spec.rb +101 -0
  41. data/spec/lib/captain_hoog/pre_git_spec.rb +107 -0
  42. data/spec/spec_helper.rb +8 -0
  43. data/spec/support/matchers/be_subclass_of.rb +13 -0
  44. metadata +236 -0
@@ -0,0 +1,2 @@
1
+ require 'colorize'
2
+ require 'terminal-table'
@@ -0,0 +1,17 @@
1
+ module CaptainHoog
2
+ class Env < Hash
3
+
4
+ def method_missing(meth_name, *args, &block)
5
+ if self.has_key?(meth_name)
6
+ return self[meth_name]
7
+ else
8
+ super
9
+ end
10
+ end
11
+
12
+ def respond_to_missing?(meth_name, include_private = false)
13
+ self.has_key?(meth_name) || super
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ module CaptainHoog
2
+ module Errors
3
+ TestResultNotValidError = Class.new(StandardError)
4
+ MessageResultNotValidError = Class.new(TestResultNotValidError)
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ require_relative 'errors/dsl_errors'
@@ -0,0 +1,72 @@
1
+ module CaptainHoog
2
+ class Git
3
+
4
+ attr_accessor :env
5
+
6
+ def initialize
7
+ @helper_table = HelperTable.new
8
+ end
9
+
10
+ def test(&test_block)
11
+ if test_block
12
+ @test_block = test_block
13
+ end
14
+ end
15
+
16
+ def message
17
+ if block_given?
18
+ @message = yield
19
+ unless @message.is_a?(String)
20
+ raise CaptainHoog::Errors::MessageResultNotValidError
21
+ end
22
+ end
23
+ end
24
+
25
+ def helper(name,&block)
26
+ return if @helper_table.helper_defined?(name)
27
+ helper_proc = {}
28
+ helper_proc[name] = block
29
+ @helper_table.set(helper_proc)
30
+ end
31
+
32
+ def run(&run_block)
33
+ @test_result = true
34
+ @run_block = run_block
35
+ end
36
+
37
+ # Public: Renders a table.
38
+ #
39
+ # rows - An Array of row contents
40
+ # headings - An Array of headlines
41
+ #
42
+ # Returns the table as String.
43
+ def render_table(rows, headings = [])
44
+ table = ::Terminal::Table.new(headings: headings, rows: rows)
45
+
46
+ table.to_s
47
+ end
48
+
49
+ def method_missing(meth_name, *args, &block)
50
+ super unless @helper_table.helper_defined?(meth_name)
51
+ helper = @helper_table[meth_name]
52
+ fail ArgumentError unless helper[meth_name].arity == args.size
53
+ helper[meth_name].call(*args)
54
+ end
55
+
56
+ def respond_to_missing?(meth_name, include_private = false)
57
+ @helper_table.helper_defined?(meth_name) || super
58
+ end
59
+
60
+ def execute
61
+ if @test_block
62
+ @test_result = @test_block.call
63
+ else
64
+ # run #run
65
+ @run_block.call
66
+ end
67
+ unless @test_result.is_a?(FalseClass) or @test_result.is_a?(TrueClass)
68
+ raise CaptainHoog::Errors::TestResultNotValidError
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,23 @@
1
+ module CaptainHoog
2
+ class HelperTable < ::Array
3
+
4
+ def helper_defined?(helper_name)
5
+ defined_helpers.include?(helper_name)
6
+ end
7
+
8
+ def defined_helpers
9
+ self.map(&:keys).flatten
10
+ end
11
+
12
+ def [](helper_name)
13
+ self.detect do |helper|
14
+ helper.keys.include?(helper_name)
15
+ end
16
+ end
17
+ alias_method :get, :[]
18
+
19
+ def set(helper)
20
+ self << helper
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ module CaptainHoog
2
+ # Public: Class that evaluates a plugin from a given bunch of DSL code.
3
+ class Plugin
4
+ include Delegatable
5
+
6
+ attr_accessor :env
7
+
8
+ delegate_to :eigenplugin
9
+
10
+ # Public: Initializes the Plugin evaluator.
11
+ #
12
+ # code - the plugin code as String
13
+ # env - An instance of CaptainHoog::Env containing some accessible
14
+ # environment variables (context is limited to CaptainHoog)
15
+ def initialize(code,env)
16
+ @code = code
17
+ @env = env
18
+ @git = CaptainHoog::Git.new
19
+ end
20
+
21
+ # Public: Yields the code given in @code.
22
+ def git
23
+ eigenplugin
24
+ end
25
+
26
+ # Public: Evaluates the plugin by 'reading' the dsl. Did not execute
27
+ # anything.
28
+ #
29
+ # Returns nothing
30
+ def eval_plugin
31
+ instance_eval(@code)
32
+ end
33
+
34
+ # Public: Executes a plugin and stores the results in a Hash.
35
+ #
36
+ # Returns a Hash containing the test result and the failure message.
37
+ def execute
38
+ eigenplugin.execute
39
+ {
40
+ :result => @git.instance_variable_get(:@test_result),
41
+ :message => @git.instance_variable_get(:@message)
42
+ }
43
+ end
44
+
45
+ private
46
+
47
+ def eigenplugin
48
+ @eigenplugin ||= Class.new do
49
+ include Delegatable
50
+ attr_reader :plugin_name
51
+
52
+ delegate_to :git
53
+
54
+ def initialize(git)
55
+ @git = git
56
+ end
57
+
58
+ def describe(name)
59
+ @plugin_name = name
60
+ yield(@git) if block_given?
61
+ end
62
+
63
+ end.new(@git)
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,29 @@
1
+ module CaptainHoog
2
+ class PluginList
3
+
4
+ attr_reader :plugins
5
+
6
+ def initialize(type='pre-commit', config: {})
7
+ @config = config
8
+ @type = type
9
+ build_list
10
+ end
11
+
12
+ def has?(plugin)
13
+ @plugins.include?(plugin.plugin_name)
14
+ end
15
+
16
+ private
17
+
18
+ def build_list
19
+ plugins_for_type = @config.fetch(@type, [])
20
+ excluded_plugins = @config.fetch('exclude', [])
21
+
22
+ plugins_for_type = [] if plugins_for_type.nil?
23
+ excluded_plugins = [] if excluded_plugins.nil?
24
+
25
+ @plugins = plugins_for_type - excluded_plugins
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,125 @@
1
+ module CaptainHoog
2
+ # Public: Entry class for handling a Pre-Something with plugins.
3
+ class PreGit
4
+
5
+ class << self
6
+ attr_accessor :project_dir,
7
+ :plugins_dir,
8
+ :headline_on_success,
9
+ :headline_on_failure,
10
+ :suppress_headline
11
+ end
12
+
13
+ # Public: Runs the hook.
14
+ #
15
+ # Inits a new instance of self and evaluates the plugins (if found some)
16
+ #
17
+ # Returns an instance of self (CaptainHoog::PreGit)
18
+ def self.run(plugins_list: nil)
19
+ pre_git = self.new(plugins_list)
20
+ pre_git.plugins_eval
21
+ pre_git
22
+ end
23
+
24
+ # Public: Configures the hook by calling the class' accessors.
25
+ #
26
+ # Returns nothing.
27
+ def self.configure
28
+ yield(self) if block_given?
29
+ end
30
+
31
+ def initialize(plugins_list = nil)
32
+ env = prepare_env
33
+ @plugins = []
34
+ @plugins_list = plugins_list
35
+ if self.class.plugins_dir
36
+ read_plugins_from_dir(self.class.plugins_dir, env)
37
+ end
38
+ if shared_plugins_dir_present?
39
+ read_plugins_from_dir(shared_plugins_dir, env)
40
+ end
41
+ end
42
+
43
+ # Public: Evaluates all plugins that are found in plugins_dir.
44
+ #
45
+ # If any plugin contains a test that returns false (means it fails)
46
+ # it displays the plugins failure messages and exits with code 1
47
+ #
48
+ def plugins_eval
49
+ raw_results = available_plugins.inject([]) do |result, plugin|
50
+ result << plugin.execute
51
+ end
52
+ @results = raw_results.select{ |result| result.is_a?(Hash) }
53
+ tests = @results.map{ |result| result[:result] }
54
+ if tests.any?{ |test| not test }
55
+ message_on_failure
56
+ exit 1 unless ENV["PREGIT_ENV"] == "test"
57
+ else
58
+ message_on_success
59
+ exit 0 unless ENV["PREGIT_ENV"] == "test"
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def available_plugins
66
+ @plugins.inject([]) do |result, item|
67
+ item.eval_plugin
68
+ result << item if @plugins_list.has?(item)
69
+ result
70
+ end
71
+ end
72
+
73
+ def prepare_env
74
+ env = Env.new
75
+ env[:project_dir] = self.class.project_dir
76
+ env
77
+ end
78
+
79
+ def message_on_success
80
+ puts defined_message_on(:success).green unless self.class.suppress_headline
81
+ end
82
+
83
+ def message_on_failure
84
+ unless self.class.suppress_headline
85
+ puts defined_message_on(:failure).red
86
+ puts "\n"
87
+ @results.select{|result| not result[:result] }.each do |result|
88
+ puts result[:message].red
89
+ end
90
+ end
91
+ end
92
+
93
+ def defined_message_on(type)
94
+ if self.class.send("headline_on_#{type}")
95
+ self.class.send("headline_on_#{type}")
96
+ else
97
+ default_messages[type]
98
+ end
99
+ end
100
+
101
+ def default_messages
102
+ {
103
+ success: "All tests passed. No reason to prevent the commit.",
104
+ failure: "Commit failed. See errors below."
105
+ }
106
+ end
107
+
108
+ def shared_plugins_dir_present?
109
+ File.exists?(shared_plugins_dir)
110
+ end
111
+
112
+ def shared_plugins_dir
113
+ File.join(self.class.plugins_dir, "..", "shared")
114
+ end
115
+
116
+ def read_plugins_from_dir(dir, env)
117
+ Dir["#{dir}/**/**.rb"].each do |plugin|
118
+ code = File.read(plugin)
119
+ @plugins << Plugin.new(code,env)
120
+ end
121
+ end
122
+
123
+ end
124
+
125
+ end
@@ -0,0 +1,39 @@
1
+ # This file will be created after installing.
2
+ # It only includes
3
+ #
4
+ # * project_dir
5
+ # * plugin_dir
6
+ #
7
+ # at the beginning (if they're provided during installation).
8
+
9
+ # Exclude plugins you don't want to be executed within every
10
+ # hook type. This has no effect in v1.x
11
+ #
12
+ # The name is taken from the plugin's name identifier.
13
+ # exclude:
14
+ # - cucumber
15
+ # - log
16
+
17
+ # Specify plugins you want to have executed within a pre-commit hook.
18
+ # If no list is specfied, all plugins are executed (if the pre-commit
19
+ # hook is installed).
20
+ #
21
+ # The name is taken from the plugin's name identifier.
22
+ # pre-commit:
23
+ # - pry
24
+ # - rspec
25
+
26
+ # Specify plugins you want to have executed within a pre-push hook.
27
+ # If no list is specfied, all plugins are executed (if the pre-push
28
+ # hook is installed).
29
+ #
30
+ # The name is taken from the plugin's name identifier.
31
+ # pre-push:
32
+ # - mat
33
+
34
+ # path in which the hooks should be executed. It's Git root by default.
35
+ # This is going into env.
36
+ project_dir: "<%= config[:project_dir] %>"
37
+ # path to plugins
38
+ # This is going into env.
39
+ plugins_dir: "<%= config[:plugins_dir] %>"
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ require 'captain_hoog'
3
+ require 'yaml'
4
+
5
+ # read config
6
+ hoog_config = YAML.load_file(File.join('<%= config[:root_dir] %>',
7
+ 'hoogfile.yml'))
8
+
9
+ # Adjust settings
10
+ CaptainHoog::PreGit.configure do |config|
11
+ config.plugins_dir = hoog_config['plugins_dir']
12
+ config.project_dir = hoog_config['project_dir']
13
+ #config.headline_on_success = "All is fine."
14
+ #config.headline_on_failure = "Oops. Something went wrong"
15
+ end
16
+
17
+ plugin_list = CaptainHoog::PluginList.new('<%=config[:type] %>',
18
+ config: hoog_config)
19
+
20
+ CaptainHoog::PreGit.run(plugins_list: plugin_list)
@@ -0,0 +1,3 @@
1
+ module CaptainHoog
2
+ VERSION = "1.0"
3
+ end
@@ -0,0 +1,14 @@
1
+ require "captain_hoog/version"
2
+ require "captain_hoog/dependencies"
3
+ require "captain_hoog/errors"
4
+ require "captain_hoog/delegatable"
5
+ require "captain_hoog/helper_table"
6
+ require "captain_hoog/env"
7
+ require "captain_hoog/git"
8
+ require "captain_hoog/plugin"
9
+ require "captain_hoog/pre_git"
10
+ require "captain_hoog/plugin_list"
11
+
12
+ module CaptainHoog
13
+ # Your code goes here...
14
+ end
@@ -0,0 +1,13 @@
1
+ git.describe "helper" do |pre|
2
+ pre.helper :my_helper do
3
+ 12
4
+ end
5
+
6
+ pre.test do
7
+ false
8
+ end
9
+
10
+ pre.message do
11
+ "It's #{my_helper}."
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ git.describe "shared" do |pre|
2
+
3
+ pre.test do
4
+ true
5
+ end
6
+
7
+ pre.message do
8
+ "The shared test failed. Prevent you from doing anything."
9
+ end
10
+
11
+ end
@@ -0,0 +1,15 @@
1
+ git.describe "foo" do |pre|
2
+
3
+ pre.helper :test_helper do
4
+ env.project_dir
5
+ end
6
+
7
+ pre.test do
8
+ false
9
+ end
10
+
11
+ pre.message do
12
+ "The test failed in #{test_helper}. Prevent you from doing anything."
13
+ end
14
+
15
+ end
@@ -0,0 +1,11 @@
1
+ git.describe "simple" do |pre|
2
+
3
+ pre.test do
4
+ true
5
+ end
6
+
7
+ pre.message do
8
+ "The test failed. Prevent you from doing anything."
9
+ end
10
+
11
+ end
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ require 'captain_hoog'
3
+
4
+ plugins_path = File.expand_path(File.join(File.dirname(__FILE__),
5
+ "plugins",
6
+ "test_plugins",
7
+ "failing"))
8
+
9
+ hoog_config = {
10
+ 'exclude' => [],
11
+ 'plugins_dir' => plugins_path,
12
+ 'project_dir' => 'with_git',
13
+ 'pre-commit' => ['foo']
14
+ }
15
+
16
+ plugin_list = CaptainHoog::PluginList.new('pre-commit',
17
+ config: hoog_config)
18
+
19
+ # Adjust settings
20
+ CaptainHoog::PreGit.configure do |config|
21
+ config.plugins_dir = hoog_config['plugins_dir']
22
+ config.project_dir = hoog_config['project_dir']
23
+ end
24
+
25
+ CaptainHoog::PreGit.run(plugins_list: plugin_list)
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ require 'captain_hoog'
3
+
4
+ plugins_path = File.expand_path(File.join(File.dirname(__FILE__),
5
+ "plugins",
6
+ "test_plugins",
7
+ "passing"))
8
+
9
+ hoog_config = {
10
+ 'exclude' => [],
11
+ 'plugins_dir' => plugins_path,
12
+ 'project_dir' => 'with_git',
13
+ 'pre-commit' => ['simple']
14
+ }
15
+
16
+ plugin_list = CaptainHoog::PluginList.new('pre-commit',
17
+ config: hoog_config)
18
+
19
+ # Adjust settings
20
+ CaptainHoog::PreGit.configure do |config|
21
+ config.plugins_dir = hoog_config['plugins_dir']
22
+ config.project_dir = hoog_config['project_dir']
23
+ end
24
+
25
+ CaptainHoog::PreGit.run(plugins_list: plugin_list)
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe CaptainHoog::Env do
4
+
5
+ it "is inherits from Hash" do
6
+ expect(subject.class).to be_subclass_of(Hash)
7
+ end
8
+
9
+ describe "treats keys as methods" do
10
+
11
+ let(:env){ CaptainHoog::Env.new }
12
+
13
+ before do
14
+ env[:foo] = "bar"
15
+ end
16
+
17
+ it "raises not a NoMethodError" do
18
+ expect { env.foo }.to_not raise_error
19
+ end
20
+
21
+ it "responds to the key as method" do
22
+ expect(env).to respond_to(:foo)
23
+ end
24
+
25
+ it "returns the value for the key" do
26
+ expect(env.foo).to eq "bar"
27
+ end
28
+
29
+ end
30
+
31
+ end