fudge 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,16 +12,10 @@ module Fudge
12
12
  end
13
13
 
14
14
  def run(options={})
15
- # Allow either a string (usually "*") or an array of strings
16
- # with directories
17
- redir = @pattern.kind_of?(String) ? Dir[@pattern] : Dir[*@pattern]
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 output_dir(dir)
33
- message = ""
34
- message << "--> In directory".foreground(:cyan)
35
- message << " #{dir}:".foreground(:cyan).bright
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
- puts message
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
- Dir.chdir @directory do
14
- output_dir(@directory)
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
@@ -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
- rs1 = coverage_regex.source
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
@@ -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
- @output, success = run_command(cmd(options))
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
- return true unless check_for # We're ok if no output defined
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
@@ -1,4 +1,4 @@
1
1
  module Fudge
2
2
  # Define gem version
3
- VERSION = "0.0.5"
3
+ VERSION = "0.1.0"
4
4
  end
@@ -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