fudge 0.0.5 → 0.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.
- checksums.yaml +15 -0
- data/lib/fudge.rb +5 -0
- data/lib/fudge/build.rb +6 -5
- data/lib/fudge/cli.rb +2 -13
- data/lib/fudge/description.rb +11 -7
- data/lib/fudge/file_finder.rb +35 -0
- data/lib/fudge/generator.rb +42 -0
- data/lib/fudge/output_checker.rb +55 -0
- data/{spec/support → lib/fudge/rspec}/matchers.rb +12 -7
- data/lib/fudge/runner.rb +19 -6
- data/lib/fudge/tasks.rb +2 -0
- data/lib/fudge/tasks/cane.rb +16 -4
- data/lib/fudge/tasks/clean_bundler_env.rb +3 -10
- data/lib/fudge/tasks/composite_task.rb +20 -8
- data/lib/fudge/tasks/each_directory.rb +13 -15
- data/lib/fudge/tasks/flay.rb +64 -0
- data/lib/fudge/tasks/flog.rb +89 -0
- data/lib/fudge/tasks/in_directory.rb +2 -13
- data/lib/fudge/tasks/rspec.rb +5 -5
- data/lib/fudge/tasks/shell.rb +8 -35
- data/lib/fudge/version.rb +1 -1
- data/lib/fudge/with_directory.rb +27 -0
- data/spec/lib/fudge/build_spec.rb +28 -0
- data/spec/lib/fudge/output_checker_spec.rb +31 -0
- data/spec/lib/fudge/runner_spec.rb +19 -0
- data/spec/lib/fudge/tasks/bundler_spec.rb +8 -0
- data/spec/lib/fudge/tasks/composite_task_spec.rb +15 -0
- data/spec/lib/fudge/tasks/flay_spec.rb +48 -0
- data/spec/lib/fudge/tasks/flog_spec.rb +89 -0
- data/spec/lib/fudge/tasks/shell_spec.rb +21 -0
- data/spec/lib/fudge/with_directory_spec.rb +17 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/dummy_task.rb +2 -1
- metadata +96 -80
@@ -12,16 +12,10 @@ module Fudge
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def run(options={})
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
redir.select { |path| File.directory? path }.each do |dir|
|
20
|
-
next if exclude && exclude.include?(dir)
|
21
|
-
|
22
|
-
Dir.chdir dir do
|
23
|
-
output_dir(dir)
|
24
|
-
|
15
|
+
output = options[:output] || $stdout
|
16
|
+
directories.each do |dir|
|
17
|
+
next if skip_directory?(dir)
|
18
|
+
WithDirectory.new(dir, output).inside do
|
25
19
|
return false unless super
|
26
20
|
end
|
27
21
|
end
|
@@ -29,13 +23,17 @@ module Fudge
|
|
29
23
|
|
30
24
|
private
|
31
25
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
def directories
|
27
|
+
# Allow either a string (usually "*") or an array of strings
|
28
|
+
# with directories
|
29
|
+
files = @pattern.kind_of?(String) ? Dir[@pattern] : Dir[*@pattern]
|
30
|
+
files.select { |path| File.directory? path }
|
31
|
+
end
|
36
32
|
|
37
|
-
|
33
|
+
def skip_directory?(dir)
|
34
|
+
exclude && exclude.include?(dir)
|
38
35
|
end
|
36
|
+
|
39
37
|
end
|
40
38
|
|
41
39
|
register EachDirectory
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Fudge
|
2
|
+
module Tasks
|
3
|
+
# Allow use of Flay complexity analyser
|
4
|
+
#
|
5
|
+
# task :flay
|
6
|
+
#
|
7
|
+
# runs flay with max score of 0
|
8
|
+
#
|
9
|
+
# task :flay, :exclude => 'subpath/'
|
10
|
+
#
|
11
|
+
# excludes files matching :exclude from run
|
12
|
+
#
|
13
|
+
# task :flay, :max => 20
|
14
|
+
#
|
15
|
+
# sets max score to 20
|
16
|
+
#
|
17
|
+
# Any and all options can be defined
|
18
|
+
#
|
19
|
+
class Flay < Shell
|
20
|
+
include Helpers::BundleAware
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def cmd(options={})
|
25
|
+
bundle_cmd(flay_ruby_files, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def flay_ruby_files
|
29
|
+
finder = FileFinder.new(options)
|
30
|
+
finder.generate_command("flay", tty_options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_for
|
34
|
+
[check_regex, method(:flay_checker)]
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_regex
|
38
|
+
/Total score \(lower is better\) = (?<score>\d+)/
|
39
|
+
end
|
40
|
+
|
41
|
+
def flay_checker(matches)
|
42
|
+
score = matches[:score].to_i
|
43
|
+
if score > max_score
|
44
|
+
"Duplication Score Higher Than #{max_score}"
|
45
|
+
else
|
46
|
+
true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def max_score
|
51
|
+
options.fetch(:max, 0)
|
52
|
+
end
|
53
|
+
|
54
|
+
def tty_options
|
55
|
+
opts = []
|
56
|
+
opts << "--diff"
|
57
|
+
opts
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
register Flay
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Fudge
|
2
|
+
module Tasks
|
3
|
+
# Allow use of Flog complexity analyser
|
4
|
+
#
|
5
|
+
# task :flog
|
6
|
+
#
|
7
|
+
# runs flog with max score of 10.0 for a method, 5.0 average
|
8
|
+
#
|
9
|
+
# task :flog, :exclude => 'subpath/'
|
10
|
+
#
|
11
|
+
# excludes files matching :exclude from run
|
12
|
+
#
|
13
|
+
# task :flog, :max => 20.0
|
14
|
+
#
|
15
|
+
# sets max score for a method to 20.0
|
16
|
+
#
|
17
|
+
# task :flog, :average => 10.0
|
18
|
+
#
|
19
|
+
# sets required average to 10.0
|
20
|
+
#
|
21
|
+
# task :flog, :methods => true
|
22
|
+
#
|
23
|
+
# runs only scoring methods, not classes macros etc
|
24
|
+
#
|
25
|
+
# Any and all options can be defined
|
26
|
+
#
|
27
|
+
class Flog < Shell
|
28
|
+
include Helpers::BundleAware
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def cmd(options={})
|
33
|
+
bundle_cmd(flog_ruby_files, options)
|
34
|
+
end
|
35
|
+
|
36
|
+
def flog_ruby_files
|
37
|
+
finder = FileFinder.new(options)
|
38
|
+
finder.generate_command("flog", tty_options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_for
|
42
|
+
[check_regex, method(:flog_checker)]
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_regex
|
46
|
+
/^\s*(?<score>\d+.\d+): (?<type>.*)$/
|
47
|
+
end
|
48
|
+
|
49
|
+
def flog_checker(matches)
|
50
|
+
average, max = extract_scores(matches)
|
51
|
+
if average > average_required
|
52
|
+
"Average Complexity Higher Than #{average_required}"
|
53
|
+
elsif max > max_score
|
54
|
+
"Maximum Complexity Higher Than #{max_score}"
|
55
|
+
else
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def average_required
|
61
|
+
options.fetch(:average, 5.0).to_f
|
62
|
+
end
|
63
|
+
|
64
|
+
def max_score
|
65
|
+
options.fetch(:max, 10.0).to_f
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_scores(matches)
|
69
|
+
lines = extract_lines(matches)
|
70
|
+
_, average, max, _ = lines
|
71
|
+
[average.first.to_f, max.first.to_f]
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_lines(matches)
|
75
|
+
output = matches.string
|
76
|
+
output.scan(check_regex)
|
77
|
+
end
|
78
|
+
|
79
|
+
def tty_options
|
80
|
+
opts = []
|
81
|
+
opts << "-m" if options[:methods]
|
82
|
+
opts
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
register Flog
|
88
|
+
end
|
89
|
+
end
|
@@ -10,22 +10,11 @@ module Fudge
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def run(options={})
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
output = options[:output] || $stdout
|
14
|
+
WithDirectory.new(@directory, output).inside do
|
16
15
|
super
|
17
16
|
end
|
18
17
|
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def output_dir(dir)
|
23
|
-
message = ""
|
24
|
-
message << "--> In directory".foreground(:cyan)
|
25
|
-
message << " #{dir}:".foreground(:cyan).bright
|
26
|
-
|
27
|
-
puts message
|
28
|
-
end
|
29
18
|
end
|
30
19
|
|
31
20
|
register InDirectory
|
data/lib/fudge/tasks/rspec.rb
CHANGED
@@ -19,6 +19,10 @@ module Fudge
|
|
19
19
|
/((\d+\.\d+)%\) covered)/
|
20
20
|
end
|
21
21
|
|
22
|
+
def check_regex
|
23
|
+
Regexp.union(coverage_regex, pre_conditions_regex)
|
24
|
+
end
|
25
|
+
|
22
26
|
def cmd(options={})
|
23
27
|
self.arguments = 'spec/' if arguments.blank?
|
24
28
|
bundle_cmd("rspec#{tty_options} #{arguments}", options)
|
@@ -30,11 +34,7 @@ module Fudge
|
|
30
34
|
|
31
35
|
def check_for
|
32
36
|
if coverage
|
33
|
-
|
34
|
-
rs2 = pre_conditions_regex.source
|
35
|
-
regex = Regexp.new(rs1 + '|' + rs2)
|
36
|
-
|
37
|
-
[regex, method(:coverage_checker)]
|
37
|
+
[check_regex, method(:coverage_checker)]
|
38
38
|
else
|
39
39
|
super
|
40
40
|
end
|
data/lib/fudge/tasks/shell.rb
CHANGED
@@ -12,58 +12,31 @@ module Fudge
|
|
12
12
|
#
|
13
13
|
# @param [Hash] options Any options to pass to the shell
|
14
14
|
def run(options={})
|
15
|
-
|
15
|
+
output_io = options[:output] || $stdout
|
16
|
+
@output, success = run_command(cmd(options), output_io)
|
16
17
|
|
17
18
|
return false unless success
|
18
|
-
return check_for_output
|
19
|
+
return check_for_output(output_io)
|
19
20
|
end
|
20
21
|
|
21
22
|
private
|
22
23
|
|
23
|
-
def run_command(cmd)
|
24
|
+
def run_command(cmd, output_io)
|
24
25
|
output = ''
|
25
26
|
IO.popen(cmd) do |f|
|
26
27
|
until f.eof?
|
27
28
|
bit = f.getc
|
28
29
|
output << bit
|
29
|
-
putc bit
|
30
|
+
output_io.putc bit
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
34
|
[output, $?.success?]
|
34
35
|
end
|
35
36
|
|
36
|
-
def check_for_output
|
37
|
-
|
38
|
-
|
39
|
-
# Check if we have a callable to parse the regex matches
|
40
|
-
if check_for.is_a? Enumerable
|
41
|
-
regex, pass_if = check_for[0], check_for[1]
|
42
|
-
else
|
43
|
-
regex, pass_if = check_for, nil
|
44
|
-
end
|
45
|
-
|
46
|
-
# Do regex match and fail if no match
|
47
|
-
match = @output.match(regex)
|
48
|
-
unless match
|
49
|
-
puts "Output didn't match #{regex}."
|
50
|
-
return false
|
51
|
-
end
|
52
|
-
|
53
|
-
# If we've got a callable, call it to check on regex matches
|
54
|
-
if pass_if
|
55
|
-
passed = pass_if.call(match)
|
56
|
-
unless passed == true
|
57
|
-
if passed.is_a? String
|
58
|
-
puts passed
|
59
|
-
else
|
60
|
-
puts "Output matched #{check_for} but condition failed."
|
61
|
-
end
|
62
|
-
return false
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
true
|
37
|
+
def check_for_output(output_io)
|
38
|
+
checker = OutputChecker.new(check_for, output_io)
|
39
|
+
checker.check(@output)
|
67
40
|
end
|
68
41
|
|
69
42
|
# Defines the command to run
|
data/lib/fudge/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
#Directory helpers methods
|
2
|
+
class Fudge::WithDirectory
|
3
|
+
attr_reader :dir, :output
|
4
|
+
|
5
|
+
def initialize(dir, output)
|
6
|
+
@dir = dir
|
7
|
+
@output = output
|
8
|
+
end
|
9
|
+
|
10
|
+
# Executes a block inside the directory
|
11
|
+
def inside
|
12
|
+
Dir.chdir(dir) do
|
13
|
+
output_message
|
14
|
+
yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def output_message
|
21
|
+
message = ""
|
22
|
+
message << "--> In directory".foreground(:cyan)
|
23
|
+
message << " #{dir}:".foreground(:cyan).bright
|
24
|
+
|
25
|
+
output.puts message
|
26
|
+
end
|
27
|
+
end
|
@@ -2,4 +2,32 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Fudge::Build do
|
4
4
|
it { should be_a Fudge::Tasks::CompositeTask }
|
5
|
+
|
6
|
+
describe "#run" do
|
7
|
+
|
8
|
+
context "when provided an output" do
|
9
|
+
let(:output) { StringIO.new }
|
10
|
+
|
11
|
+
it "prints messages to the output instead of stdout" do
|
12
|
+
subject.run :output => output
|
13
|
+
|
14
|
+
output.string.should_not be_empty
|
15
|
+
output.string.should include "Skipping callbacks..."
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when there are callback hooks" do
|
19
|
+
let(:hook) { mock(:Hook) }
|
20
|
+
|
21
|
+
before :each do
|
22
|
+
subject.callbacks = true
|
23
|
+
subject.success_hooks << hook
|
24
|
+
end
|
25
|
+
|
26
|
+
it "passes output down to the hook run" do
|
27
|
+
hook.should_receive(:run).with(:output => output).and_return(true)
|
28
|
+
subject.run :output => output
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
5
33
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fudge::OutputChecker do
|
4
|
+
let (:output_io) { StringIO.new }
|
5
|
+
|
6
|
+
describe "#check" do
|
7
|
+
subject { described_class.new /foo/, output_io }
|
8
|
+
|
9
|
+
context "when the output does not match the check" do
|
10
|
+
it 'send a mismatch message to the output io' do
|
11
|
+
subject.check('bar')
|
12
|
+
output_io.string.should include "Output didn't match (?-mix:foo)."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with a block for checking" do
|
17
|
+
let(:callable) do
|
18
|
+
Proc.new do
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
subject { described_class.new [/foo/, callable], output_io }
|
23
|
+
|
24
|
+
it 'sends error mesage to the output io' do
|
25
|
+
subject.check('foo')
|
26
|
+
|
27
|
+
output_io.string.should include "Output matched (?-mix:foo) but condition failed."
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -21,5 +21,24 @@ describe Fudge::Runner do
|
|
21
21
|
|
22
22
|
expect { subject.run_build }.to raise_error Fudge::Exceptions::BuildFailed
|
23
23
|
end
|
24
|
+
|
25
|
+
context "when an output is provided" do
|
26
|
+
let(:output) { StringIO.new }
|
27
|
+
|
28
|
+
before :each do
|
29
|
+
subject.run_build 'default', :output => output
|
30
|
+
end
|
31
|
+
|
32
|
+
it "puts all output to given output instead stdout" do
|
33
|
+
output.string.should_not be_empty
|
34
|
+
output.string.should include "Running build"
|
35
|
+
output.string.should include "default"
|
36
|
+
output.string.should include "Build SUCCEEDED!"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "runs the task passing the output down" do
|
40
|
+
DummyTask.run_options.should == {:output => output}
|
41
|
+
end
|
42
|
+
end
|
24
43
|
end
|
25
44
|
end
|
@@ -11,6 +11,7 @@ class TestBundlerAwareTask
|
|
11
11
|
end
|
12
12
|
|
13
13
|
class TestNonBundlerAwareTask
|
14
|
+
|
14
15
|
def self.name
|
15
16
|
:test_non_bundle_aware
|
16
17
|
end
|
@@ -34,6 +35,13 @@ describe Fudge::Tasks::CleanBundlerEnv do
|
|
34
35
|
|
35
36
|
subject.run.should be_true
|
36
37
|
end
|
38
|
+
|
39
|
+
it "runs each task with a clean bundle env" do
|
40
|
+
Bundler.should_receive(:with_clean_env).and_call_original
|
41
|
+
|
42
|
+
subject.tasks << bundle_aware_task
|
43
|
+
subject.run.should be_true
|
44
|
+
end
|
37
45
|
end
|
38
46
|
end
|
39
47
|
end
|