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.
@@ -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