captain_hoog 1.0

Sign up to get free protection for your applications and to get access to all the features.
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