hotspots 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
+ v0.2.0
2
+ ------
3
+
4
+ * Use default options when invoked as a library
5
+ * Restructure interfaces for Logger, ExitStrategy & Hotspots. The older classes, modules and methods are still available
6
+
1
7
  v0.1.1
2
- -------
8
+ ------
3
9
 
4
10
  * Sort for an array of array via spaceship operator returns different result on each run on ruby 1.8.7. Store has a string representation breaks intermittently on 1.8.7. Tests fail intermittently on 1.8.7. So support for ruby 1.9.x only
5
11
  * Simplify Rakefile
data/README.md CHANGED
@@ -28,7 +28,7 @@ Specific options:
28
28
  -t, --time [TIME] Time in days to scan the repository for. Defaults to fifteen
29
29
  -r, --repository [PATH] Path to the repository to scan. Defaults to current path
30
30
  -f, --file-filter [REGEX] Regular expression to filtering file names. All files are allowed when not specified
31
- -m, --message-filter [PIPE SEPARATED] Values to filter files names against each commit message separated by pipe.
31
+ -m, --message-filter [PIPE SEPARATED] Pipe separated values to filter files names against each commit message.
32
32
  All files are allowed when not specified
33
33
  -c, --cutoff [CUTOFF] The minimum occurrence to consider for a file to appear in the list. Defaults to zero
34
34
  -v, --verbose Show verbose output
data/TODO.md CHANGED
@@ -1,4 +1,4 @@
1
1
  * Allow better use as a library - add documentation
2
- * Don't barf if not inside root directory of git
3
- * Hotspots#initialize validates presence of mandatory arguments - useful when used as library
2
+ * Reduce conditionals
4
3
  * Optional support for colors
4
+ * Remove compatibility layers in a major release
data/bin/hotspots CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require 'hotspots'
4
4
 
5
- Hotspots::Main.new.execute!
5
+ Hotspots::Main.new.output
data/lib/hotspots.rb CHANGED
@@ -6,54 +6,73 @@ require 'hotspots/repository'
6
6
 
7
7
  module Hotspots
8
8
  class Main
9
- attr_reader :logger, :options, :repository, :verbose,
10
- :exit_strategy, :driver, :parser, :store
9
+ attr_reader :logger, :repository, :verbose,
10
+ :exit_strategy, :driver, :parser, :store,
11
+ :time, :message_filters, :file_filter, :cutoff
11
12
 
13
+ # TODO : change this signature - this method should get resolved options as parameters
14
+ # initialize is not responsible for invoking option parsing
12
15
  def initialize(opts = nil)
13
- @options = opts || Hotspots::OptionsParser.new.parse(*ARGV)
14
- @repository = options[:repository]
15
- @verbose = options[:verbose]
16
- @exit_strategy = options[:exit_strategy]
17
- @logger = Hotspots::Logger.new
16
+ options = opts.nil? ? Hotspots::OptionsParser.new.parse(*ARGV) : Hotspots::OptionsParser.default_options.merge(opts)
17
+
18
+ @logger = Hotspots::Logger.new
19
+ @repository = options[:repository]
20
+ @verbose = options[:verbose]
21
+ @exit_strategy = options[:exit_strategy]
22
+
23
+ @time = options[:time]
24
+ @message_filters = options[:message_filters]
25
+ @file_filter = options[:file_filter]
26
+ @cutoff = options[:cutoff]
18
27
  end
19
28
 
20
- def execute!
21
- validate!
29
+ def output
30
+ validate
22
31
  set
23
32
  run
24
33
  end
25
34
 
26
- def validate!
27
- validate_early_exit!
28
- validate_git_repository!
35
+ # TODO : this method should be private
36
+ def validate #:nodoc:
37
+ exit_if_options_are_for_help
38
+ exit_if_not_git_repository
29
39
  end
30
40
 
31
- def set
32
- set_logger
41
+ # TODO : this method should be private
42
+ def set #:nodoc:
43
+ set_logger_if_verbose
33
44
  set_path
34
45
  assign
35
46
  end
36
47
 
37
- def run
48
+ # TODO : this method should be private
49
+ def run #:nodoc:
38
50
  puts store.to_s
39
51
  end
40
52
 
53
+ # TODO : these methods should be private
54
+ # compatibility begin
55
+ alias_method :execute!, :output
56
+ alias_method :validate!, :validate
57
+ # compatibility end
58
+
41
59
  private
42
60
 
43
- def validate_early_exit!
61
+ def exit_if_options_are_for_help
44
62
  exit_strategy.perform
45
63
  end
46
64
 
47
- def validate_git_repository!
48
- if not File.directory?(repository) or not File.directory?(File.join(repository, '.git'))
65
+ def exit_if_not_git_repository
66
+ output = `git status 2>&1`
67
+ unless $? == 0
49
68
  puts "'#{repository}' doesn't seem to be a git repository!"
50
69
  exit 10
51
70
  end
52
71
  end
53
72
 
54
- def set_logger
73
+ def set_logger_if_verbose
55
74
  if verbose
56
- logger.set_console
75
+ logger.as_console
57
76
  end
58
77
  end
59
78
 
@@ -62,9 +81,9 @@ module Hotspots
62
81
  end
63
82
 
64
83
  def assign
65
- @driver = Hotspots::Repository::Driver::Git.new logger
66
- @parser = Hotspots::Repository::Parser::Git.new driver, options.clone
67
- @store = Hotspots::Store.new parser.files, options.clone
84
+ @driver = Hotspots::Repository::Driver::Git.new logger
85
+ @parser = Hotspots::Repository::Parser::Git.new driver, :time => time, :message_filters => message_filters
86
+ @store = Hotspots::Store.new parser.files, :cutoff => cutoff, :file_filter => file_filter
68
87
  end
69
88
  end
70
89
  end
@@ -1,41 +1,13 @@
1
+ # compatibility begin
1
2
  module Hotspots
2
- class ExitStrategy
3
- attr_reader :code, :message
4
-
5
- def initialize(options)
6
- @message = options[:message]
7
- @code = options[:code]
8
- end
9
-
10
- def perform
11
- puts @message
12
- exit @code
13
- end
14
-
15
- class Safe
16
- attr_reader :code, :message
17
-
18
- def initialize(options)
19
- @message = options[:message]
20
- @code = 0
21
- end
22
-
23
- def perform
24
- puts @message
25
- exit @code
26
- end
27
- end
28
-
29
- class Null
30
- attr_reader :code, :message
31
-
32
- def initialize(options = {})
33
- @message = ""
34
- @code = nil
35
- end
36
-
37
- def perform
38
- end
39
- end
3
+ module OptionBasedExit #:nodoc: all
4
+ class Error; end
5
+ class Safe; end
6
+ class Noop; end
7
+
8
+ ExitStrategy = Error
9
+ ExitStrategy::Safe = Safe
10
+ ExitStrategy::Null = Noop
40
11
  end
41
12
  end
13
+ # compatibility end
@@ -1,5 +1,5 @@
1
1
  module Hotspots
2
- class Logger
2
+ class Logger #:nodoc: all
3
3
  class Console
4
4
  def self.<<(message)
5
5
  $stdout << message
@@ -16,10 +16,14 @@ module Hotspots
16
16
  @sink = Null
17
17
  end
18
18
 
19
- def set_console
19
+ def as_console
20
20
  @sink = Console
21
21
  end
22
22
 
23
+ # compatibility begin
24
+ alias_method :set_console, :as_console
25
+ # compatibility end
26
+
23
27
  def log(message)
24
28
  @sink << format(message)
25
29
  end
@@ -0,0 +1,47 @@
1
+ module Hotspots
2
+ module OptionBasedExit #:nodoc: all
3
+ class Error
4
+ attr_reader :code, :message
5
+
6
+ def initialize(options)
7
+ @message = options[:message]
8
+ @code = options[:code]
9
+ end
10
+
11
+ def perform
12
+ puts @message
13
+ exit @code
14
+ end
15
+ end
16
+
17
+ class Safe
18
+ attr_reader :code, :message
19
+
20
+ def initialize(options)
21
+ @message = options[:message]
22
+ @code = 0
23
+ end
24
+
25
+ def perform
26
+ puts @message
27
+ exit @code
28
+ end
29
+ end
30
+
31
+ class Noop
32
+ attr_reader :code, :message
33
+
34
+ def initialize(options = {})
35
+ @message = ""
36
+ @code = nil
37
+ end
38
+
39
+ def perform
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # compatibility begin
46
+ require 'hotspots/exit_strategy'
47
+ # compatibility end
@@ -1,20 +1,26 @@
1
1
  require 'optparse'
2
2
 
3
3
  require 'hotspots/version'
4
- require 'hotspots/exit_strategy'
4
+ require 'hotspots/option_based_exit'
5
5
 
6
6
  module Hotspots
7
- class OptionsParser
7
+ class OptionsParser #:nodoc: all
8
+ class << self
9
+ def default_options
10
+ {
11
+ :time => 15,
12
+ :repository => ".",
13
+ :file_filter => "",
14
+ :message_filters => [""],
15
+ :cutoff => 0,
16
+ :verbose => false,
17
+ :exit_strategy => OptionBasedExit::Noop.new
18
+ }
19
+ end
20
+ end
21
+
8
22
  def initialize
9
- @options = {
10
- :time => 15,
11
- :repository => ".",
12
- :file_filter => "",
13
- :message_filters => [""],
14
- :cutoff => 0,
15
- :verbose => false,
16
- :exit_strategy => ExitStrategy::Null.new
17
- }
23
+ @options = self.class.default_options
18
24
  end
19
25
 
20
26
  def parse(*args)
@@ -22,7 +28,7 @@ module Hotspots
22
28
  begin
23
29
  parser.parse args
24
30
  rescue ::OptionParser::InvalidOption, ::OptionParser::InvalidArgument => ex
25
- @options[:exit_strategy] = ExitStrategy.new(:code => 1, :message => (ex.to_s << "\nUse -h for help\n"))
31
+ @options[:exit_strategy] = OptionBasedExit::Error.new(:code => 1, :message => (ex.to_s << "\nUse -h for help\n"))
26
32
  end
27
33
  @options
28
34
  end
@@ -83,7 +89,7 @@ module Hotspots
83
89
 
84
90
  def handle_message_filter_on(opts)
85
91
  opts.on("-m", "--message-filter [PIPE SEPARATED]", String,
86
- "Pipe separated values to filter files names against each commit message separated by pipe.",
92
+ "Pipe separated values to filter files names against each commit message.",
87
93
  "All files are allowed when not specified") do |o|
88
94
  @options[:message_filters] = o.to_s.split("|")
89
95
  end
@@ -106,7 +112,7 @@ module Hotspots
106
112
  def handle_help_on(opts)
107
113
  opts.on_tail("-h", "--help",
108
114
  "Show this message") do
109
- @options[:exit_strategy] = ExitStrategy::Safe.new(:message => opts.to_s)
115
+ @options[:exit_strategy] = OptionBasedExit::Safe.new(:message => opts.to_s)
110
116
  end
111
117
  end
112
118
  end
@@ -1,2 +1,3 @@
1
+ require 'hotspots/repository/command'
1
2
  require 'hotspots/repository/driver'
2
3
  require 'hotspots/repository/parser'
@@ -0,0 +1 @@
1
+ require 'hotspots/repository/command/git'
@@ -0,0 +1,40 @@
1
+ module Hotspots
2
+ module Repository #:nodoc: all
3
+ module Command
4
+ module Git
5
+ class Log
6
+ attr_reader :since_days, :message_filter
7
+
8
+ def initialize(options)
9
+ @since_days = options[:since_days]
10
+ @message_filter = options[:message_filter].to_s
11
+ end
12
+
13
+ def build
14
+ "git log --pretty=\"%H\" #{since_clause}#{grep_clause}"
15
+ end
16
+
17
+ def since_clause
18
+ "--since=\"#{since_days} days ago\""
19
+ end
20
+
21
+ def grep_clause
22
+ message_filter.empty? ? "" : " --grep \"#{message_filter}\""
23
+ end
24
+ end
25
+
26
+ class Show
27
+ attr_reader :commit_hash
28
+
29
+ def initialize(options)
30
+ @commit_hash = options[:commit_hash]
31
+ end
32
+
33
+ def build
34
+ "git show --oneline --name-only #{commit_hash}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,5 @@
1
1
  module Hotspots
2
- module Repository
2
+ module Repository #:nodoc: all
3
3
  module Driver
4
4
  class Git
5
5
  attr_reader :logger
@@ -8,19 +8,20 @@ module Hotspots
8
8
  @logger = logger
9
9
  end
10
10
 
11
+ # Input and output should be optionally coloured
11
12
  def pretty_log(options)
12
- grep_clause = options[:message_filter].empty? ? "" : " --grep \"#{options[:message_filter]}\""
13
- command = %Q(git log --pretty="%H" --since="#{options[:since_days]} days ago" #{grep_clause}).
14
- tap {|raw| logger.log "<Input>\n#{raw}\n</Input>"}
15
- %x(#{command}).
16
- tap {|raw| logger.log "<Output>\n#{raw}\n</Output>"}
13
+ command = log_with_tag("Input") { Command::Git::Log.new(:since_days => options[:since_days], :message_filter => options[:message_filter]).build }
14
+ log_with_tag("Output") { %x(#{command}) }
17
15
  end
18
16
 
17
+ # Input and output should be optionally coloured
19
18
  def show_one_line_names(options)
20
- command = %Q(git show --oneline --name-only #{options[:commit_hash]}).
21
- tap {|raw| logger.log "<Input>\n#{raw}\n</Input>"}
22
- %x(#{command}).
23
- tap {|raw| logger.log "<Output>\n#{raw}\n</Output>"}
19
+ command = log_with_tag("Input") { Command::Git::Show.new(:commit_hash => options[:commit_hash]).build }
20
+ log_with_tag("Output") { %x(#{command}) }
21
+ end
22
+
23
+ def log_with_tag(tag, &block)
24
+ yield.tap { |raw| logger.log "<#{tag}>\n#{raw}<#{tag}/>" }
24
25
  end
25
26
  end
26
27
  end
@@ -1,25 +1,29 @@
1
1
  module Hotspots
2
- module Repository
2
+ module Repository #:nodoc: all
3
3
  module Parser
4
4
  class Git
5
+ attr_reader :driver, :time, :message_filters
6
+
5
7
  def initialize(driver, options)
6
8
  @driver = driver
7
9
  @time = options[:time]
8
10
  @message_filters = options[:message_filters]
9
11
  end
10
12
 
13
+ # TODO : replace with each_line
11
14
  def files
12
15
  filtered_commit_hashes.map do |commit_hash|
13
- @driver.show_one_line_names(:commit_hash => commit_hash).
16
+ driver.show_one_line_names(:commit_hash => commit_hash).
14
17
  gsub("\r\n", "\n").
15
18
  gsub("\r", "\n").
16
19
  split("\n")[1..-1]
17
20
  end.flatten
18
21
  end
19
22
 
23
+ # TODO : replace with each_line
20
24
  def filtered_commit_hashes
21
- @message_filters.map do |filter|
22
- @driver.pretty_log(:since_days => @time, :message_filter => filter).
25
+ message_filters.map do |filter|
26
+ driver.pretty_log(:since_days => time, :message_filter => filter).
23
27
  gsub("\r\n", "\n").
24
28
  gsub("\r", "\n").
25
29
  split("\n")
@@ -1,5 +1,5 @@
1
1
  module Hotspots
2
- class Store
2
+ class Store #:nodoc: all
3
3
  def initialize(lines, options = {})
4
4
  @lines = lines
5
5
  @store = Hash.new(0)
@@ -1,3 +1,3 @@
1
1
  module Hotspots
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,6 +1,3 @@
1
- require File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', 'lib', 'hotspots', 'exit_strategy')
2
- require File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', 'lib', 'hotspots', 'options_parser')
3
-
4
1
  require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'minitest_helper')
5
2
 
6
3
  module Hotspots
@@ -123,7 +120,7 @@ module Hotspots
123
120
  end
124
121
  end
125
122
 
126
- describe "on a invalid option" do
123
+ describe "on an invalid option" do
127
124
  before do
128
125
  @options = @parser.parse("--invalid-option")
129
126
  end
@@ -137,7 +134,7 @@ module Hotspots
137
134
  end
138
135
  end
139
136
 
140
- describe "on a invalid argument" do
137
+ describe "on an invalid argument" do
141
138
  before do
142
139
  @options = @parser.parse("--repo", "")
143
140
  end
@@ -0,0 +1,39 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', 'minitest_helper')
2
+
3
+ module Hotspots::Repository
4
+ describe "Command::Git" do
5
+ describe "Log" do
6
+ describe "#build" do
7
+ describe "when message filter exists" do
8
+ it "includes a grep clause" do
9
+ log_command = Command::Git::Log.new :since_days => 20, :message_filter => "Foo|Bar"
10
+ log_command.build.must_equal 'git log --pretty="%H" --since="20 days ago" --grep "Foo|Bar"'
11
+ end
12
+ end
13
+
14
+ describe "when message filter doesn't exist" do
15
+ it "doesn't have a grep clause" do
16
+ log_command = Command::Git::Log.new :since_days => 20
17
+ log_command.build.must_equal 'git log --pretty="%H" --since="20 days ago"'
18
+ end
19
+ end
20
+
21
+ describe "when message filter is set to an empty string" do
22
+ it "grep clause is ignored" do
23
+ log_command = Command::Git::Log.new :since_days => 20, :message_filter => ""
24
+ log_command.build.must_equal 'git log --pretty="%H" --since="20 days ago"'
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "Show" do
31
+ describe "#build" do
32
+ it "constructs a git show with one-line format" do
33
+ show_command = Command::Git::Show.new :commit_hash => "abc123"
34
+ show_command.build.must_equal 'git show --oneline --name-only abc123'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,3 @@
1
- require File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', '..', 'lib', 'hotspots', 'repository', 'parser', 'git')
2
-
3
1
  require File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', 'minitest_helper')
4
2
 
5
3
  module Hotspots::Repository
@@ -1,5 +1,3 @@
1
- require File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', 'lib', 'hotspots', 'store')
2
-
3
1
  require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'minitest_helper')
4
2
 
5
3
  module Hotspots
@@ -1,10 +1,15 @@
1
- begin
2
- require 'simplecov'
3
- SimpleCov.start do
4
- add_filter "/test/"
1
+ lib = File.expand_path('../../lib/', __FILE__)
2
+ $:.unshift lib unless $:.include?(lib)
3
+
4
+ if ENV["coverage"] == "true"
5
+ begin
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ add_filter "/test/"
9
+ end
10
+ rescue LoadError
11
+ puts "\nPlease install simplecov to generate coverage report!\n\n"
5
12
  end
6
- rescue LoadError
7
- puts "\nPlease install simplecov to generate coverage report!\n\n"
8
13
  end
9
14
 
10
15
  # eager load all files
@@ -12,6 +17,7 @@ Dir["lib/**/*.rb"].each do |file|
12
17
  require File.expand_path(file)
13
18
  end
14
19
 
20
+ require_relative "../lib/hotspots"
15
21
 
16
22
  require 'minitest/autorun'
17
23
  require 'minitest/spec'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotspots
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-22 00:00:00.000000000 Z
12
+ date: 2012-12-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -77,7 +77,10 @@ extra_rdoc_files: []
77
77
  files:
78
78
  - lib/hotspots/exit_strategy.rb
79
79
  - lib/hotspots/logger.rb
80
+ - lib/hotspots/option_based_exit.rb
80
81
  - lib/hotspots/options_parser.rb
82
+ - lib/hotspots/repository/command/git.rb
83
+ - lib/hotspots/repository/command.rb
81
84
  - lib/hotspots/repository/driver/git.rb
82
85
  - lib/hotspots/repository/driver.rb
83
86
  - lib/hotspots/repository/parser/git.rb
@@ -88,6 +91,7 @@ files:
88
91
  - lib/hotspots.rb
89
92
  - bin/hotspots
90
93
  - test/hotspots/options_parser_test.rb
94
+ - test/hotspots/repository/command/git_test.rb
91
95
  - test/hotspots/repository/parser/git_test.rb
92
96
  - test/hotspots/store_test.rb
93
97
  - test/minitest_helper.rb
@@ -107,12 +111,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
107
111
  - - ! '>='
108
112
  - !ruby/object:Gem::Version
109
113
  version: '0'
114
+ segments:
115
+ - 0
116
+ hash: 1394833690291643919
110
117
  required_rubygems_version: !ruby/object:Gem::Requirement
111
118
  none: false
112
119
  requirements:
113
120
  - - ! '>='
114
121
  - !ruby/object:Gem::Version
115
122
  version: '0'
123
+ segments:
124
+ - 0
125
+ hash: 1394833690291643919
116
126
  requirements: []
117
127
  rubyforge_project: hotspots
118
128
  rubygems_version: 1.8.24
@@ -121,6 +131,7 @@ specification_version: 3
121
131
  summary: Find all files that changed over the past in a git repository based on conditions
122
132
  test_files:
123
133
  - test/hotspots/options_parser_test.rb
134
+ - test/hotspots/repository/command/git_test.rb
124
135
  - test/hotspots/repository/parser/git_test.rb
125
136
  - test/hotspots/store_test.rb
126
137
  - test/minitest_helper.rb