bugwatch 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bugwatch.gemspec +74 -0
- data/lib/bugwatch.rb +50 -0
- data/lib/bugwatch/adapters.rb +50 -0
- data/lib/bugwatch/analysis.rb +58 -0
- data/lib/bugwatch/attrs.rb +64 -0
- data/lib/bugwatch/churn.rb +60 -0
- data/lib/bugwatch/commit.rb +82 -0
- data/lib/bugwatch/complexity_score.rb +59 -0
- data/lib/bugwatch/diff.rb +73 -0
- data/lib/bugwatch/diff_parser.rb +68 -0
- data/lib/bugwatch/exception_data.rb +57 -0
- data/lib/bugwatch/exception_tracker.rb +72 -0
- data/lib/bugwatch/file_adapters/ruby_file_adapter.rb +51 -0
- data/lib/bugwatch/flog_score.rb +88 -0
- data/lib/bugwatch/import.rb +57 -0
- data/lib/bugwatch/method_parser.rb +105 -0
- data/lib/bugwatch/repo.rb +134 -0
- data/lib/bugwatch/ruby_complexity.rb +85 -0
- data/lib/bugwatch/tag.rb +59 -0
- data/lib/bugwatch/tree.rb +65 -0
- data/vendor/flog/lib/flog.rb +803 -0
- metadata +113 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright (c) 2013, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions
|
6
|
+
# are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer in the
|
13
|
+
# documentation and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of GROUPON nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without
|
17
|
+
# specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
module Bugwatch
|
32
|
+
|
33
|
+
class Import
|
34
|
+
|
35
|
+
def initialize(repo, caching_strategy)
|
36
|
+
@repo = repo
|
37
|
+
@caching_strategy = caching_strategy
|
38
|
+
end
|
39
|
+
|
40
|
+
def import(begin_sha=nil)
|
41
|
+
unimported_commits(begin_sha).each do |commit_sha|
|
42
|
+
commit = @repo.commit(commit_sha)
|
43
|
+
@caching_strategy.store(commit) if commit
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def unimported_commits(begin_sha)
|
50
|
+
repo_shas = @repo.commit_shas(begin_sha)
|
51
|
+
imported_shas = @caching_strategy.imported
|
52
|
+
repo_shas - imported_shas
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# Copyright (c) 2013, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions
|
6
|
+
# are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer in the
|
13
|
+
# documentation and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of GROUPON nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without
|
17
|
+
# specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
require 'ruby_parser'
|
32
|
+
require 'sexp_processor'
|
33
|
+
|
34
|
+
module Bugwatch
|
35
|
+
|
36
|
+
class MethodParser < SexpProcessor
|
37
|
+
|
38
|
+
attr_reader :parser, :klasses
|
39
|
+
|
40
|
+
def initialize(line_range)
|
41
|
+
super()
|
42
|
+
@line_range = line_range
|
43
|
+
@klasses = Hash.new {|h, k| h[k] = []}
|
44
|
+
@current_class = []
|
45
|
+
self.auto_shift_type = true
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.find(code, line_range)
|
49
|
+
method_parser = new(line_range)
|
50
|
+
ast = RubyParser.new.process(code)
|
51
|
+
method_parser.process ast
|
52
|
+
method_parser.klasses
|
53
|
+
end
|
54
|
+
|
55
|
+
def process_class(exp)
|
56
|
+
begin_line = exp.line
|
57
|
+
line_range = begin_line..exp.last.line # TODO figure out last line
|
58
|
+
class_name = get_class_name(exp.shift)
|
59
|
+
within_class(class_name, line_range) do
|
60
|
+
process(exp.shift) until exp.empty?
|
61
|
+
end
|
62
|
+
s()
|
63
|
+
end
|
64
|
+
|
65
|
+
alias_method :process_module, :process_class
|
66
|
+
|
67
|
+
def process_defn(exp)
|
68
|
+
first_line_of_method = exp.line
|
69
|
+
name = exp.shift
|
70
|
+
@klasses[current_class].push name.to_s if within_target?(first_line_of_method..exp.last.line)
|
71
|
+
process(exp.shift) until exp.empty?
|
72
|
+
s()
|
73
|
+
end
|
74
|
+
|
75
|
+
def within_class(class_name, line_range, &block)
|
76
|
+
@current_class.push class_name
|
77
|
+
methods_before_process = @klasses[current_class].count
|
78
|
+
block.call
|
79
|
+
if (methods_before_process == @klasses[current_class].count) && within_target?(line_range)
|
80
|
+
@klasses[current_class].push nil
|
81
|
+
end
|
82
|
+
@current_class.shift
|
83
|
+
end
|
84
|
+
|
85
|
+
def within_target?(range)
|
86
|
+
@line_range.any? {|num| range.cover? num}
|
87
|
+
end
|
88
|
+
|
89
|
+
def current_class
|
90
|
+
@current_class.join('::')
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_class_name(exp, namespace=[])
|
94
|
+
if exp.is_a?(Sexp) && exp.first.is_a?(Sexp)
|
95
|
+
get_class_name(exp.first, namespace + [exp.last])
|
96
|
+
elsif exp.is_a?(Sexp) && exp[1].is_a?(Sexp)
|
97
|
+
get_class_name(exp[1], namespace + [exp.last])
|
98
|
+
else
|
99
|
+
([Array(exp).last] + namespace.reverse).join('::')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# Copyright (c) 2013, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions
|
6
|
+
# are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer in the
|
13
|
+
# documentation and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of GROUPON nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without
|
17
|
+
# specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
module Bugwatch
|
32
|
+
|
33
|
+
class Repo
|
34
|
+
|
35
|
+
REPO_PATH = 'repos'
|
36
|
+
|
37
|
+
attr_reader :name, :url
|
38
|
+
|
39
|
+
def self.discover(name, url)
|
40
|
+
new(name, url).tap {|repo| repo.discover! }
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(name, url)
|
44
|
+
@name = name
|
45
|
+
@url = url
|
46
|
+
end
|
47
|
+
|
48
|
+
def discover!
|
49
|
+
if exists?
|
50
|
+
update!
|
51
|
+
else
|
52
|
+
clone!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def clone!
|
57
|
+
Kernel.system("cd #{REPO_PATH}; git clone #{url} #{name}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def update!
|
61
|
+
Kernel.system("cd #{path_to_repo}; git fetch origin master; git reset --hard origin/master; git fetch --tags")
|
62
|
+
end
|
63
|
+
|
64
|
+
def exists?
|
65
|
+
File.exists?(path_to_repo)
|
66
|
+
end
|
67
|
+
|
68
|
+
def analyze!(caching_strategy, analyzers, begin_sha=nil)
|
69
|
+
import(caching_strategy, begin_sha)
|
70
|
+
analyze(caching_strategy, *analyzers)
|
71
|
+
end
|
72
|
+
|
73
|
+
def import(caching_strategy, begin_sha=nil)
|
74
|
+
Import.new(self, caching_strategy).import(sha_or_head(begin_sha))
|
75
|
+
end
|
76
|
+
|
77
|
+
def analyze(caching_strategy, *analyzers)
|
78
|
+
Analysis.new(self, caching_strategy).analyze(*analyzers)
|
79
|
+
end
|
80
|
+
|
81
|
+
def head
|
82
|
+
rugged_repo.head.target
|
83
|
+
end
|
84
|
+
|
85
|
+
def commits(begin_sha=nil)
|
86
|
+
rugged_repo.walk(sha_or_head(begin_sha)).map do |rugged_commit|
|
87
|
+
self.commit(rugged_commit.oid)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def commit(sha)
|
92
|
+
grit_commit = grit.commit(sha)
|
93
|
+
Commit.from_grit(grit_commit) if grit_commit
|
94
|
+
end
|
95
|
+
|
96
|
+
def commit_shas(begin_sha=nil)
|
97
|
+
rugged_repo.walk(sha_or_head(begin_sha)).map(&:oid)
|
98
|
+
end
|
99
|
+
|
100
|
+
def tree
|
101
|
+
Tree.new(grit.tree)
|
102
|
+
end
|
103
|
+
|
104
|
+
def grit
|
105
|
+
@grit ||= Grit::Repo.new(path_to_repo)
|
106
|
+
end
|
107
|
+
|
108
|
+
def tags
|
109
|
+
grit_tags.map do |tag|
|
110
|
+
Tag.new(tag)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def sha_or_head(sha)
|
117
|
+
sha || head
|
118
|
+
end
|
119
|
+
|
120
|
+
def grit_tags
|
121
|
+
@grit_tags ||= grit.tags
|
122
|
+
end
|
123
|
+
|
124
|
+
def path_to_repo
|
125
|
+
"#{REPO_PATH}/#{name}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def rugged_repo
|
129
|
+
@rugged_repo ||= Rugged::Repository.new(path_to_repo)
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Copyright (c) 2013, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions
|
6
|
+
# are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer in the
|
13
|
+
# documentation and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of GROUPON nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without
|
17
|
+
# specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
module Bugwatch
|
32
|
+
|
33
|
+
class RubyComplexity
|
34
|
+
|
35
|
+
include RubyFileAdapter
|
36
|
+
|
37
|
+
def initialize(diffs)
|
38
|
+
@diffs = diffs
|
39
|
+
end
|
40
|
+
|
41
|
+
def score
|
42
|
+
ruby_flog.total_score
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_score
|
46
|
+
test_flog.total_score
|
47
|
+
end
|
48
|
+
|
49
|
+
def scores
|
50
|
+
complexity_scores(ruby_flog.scores)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_scores
|
54
|
+
complexity_scores(test_flog.scores)
|
55
|
+
end
|
56
|
+
|
57
|
+
def cyclomatic
|
58
|
+
[]
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def ruby_flog
|
64
|
+
FlogScore.new(ruby_diffs)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_flog
|
68
|
+
FlogScore.new(test_diffs)
|
69
|
+
end
|
70
|
+
|
71
|
+
def ruby_diffs
|
72
|
+
@diffs.select {|diff| analyzable_file? diff.path }
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_diffs
|
76
|
+
@diffs.select {|diff| test_file? diff.path }
|
77
|
+
end
|
78
|
+
|
79
|
+
def complexity_scores(scores)
|
80
|
+
scores.map {|(file, before_score, after_score)| ComplexityScore.new(file, before_score, after_score) }
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/lib/bugwatch/tag.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Copyright (c) 2013, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions
|
6
|
+
# are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer in the
|
13
|
+
# documentation and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of GROUPON nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without
|
17
|
+
# specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
module Bugwatch
|
32
|
+
|
33
|
+
class Tag
|
34
|
+
|
35
|
+
attr_reader :grit
|
36
|
+
|
37
|
+
def initialize(grit_tag)
|
38
|
+
@grit = grit_tag
|
39
|
+
end
|
40
|
+
|
41
|
+
def name
|
42
|
+
grit.name
|
43
|
+
end
|
44
|
+
|
45
|
+
def committed_date
|
46
|
+
grit.commit.committed_date
|
47
|
+
end
|
48
|
+
|
49
|
+
def authored_date
|
50
|
+
grit.commit.authored_date
|
51
|
+
end
|
52
|
+
|
53
|
+
def tag_date
|
54
|
+
grit.tag_date
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# Copyright (c) 2013, Groupon, Inc.
|
2
|
+
# All rights reserved.
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
5
|
+
# modification, are permitted provided that the following conditions
|
6
|
+
# are met:
|
7
|
+
#
|
8
|
+
# Redistributions of source code must retain the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
#
|
11
|
+
# Redistributions in binary form must reproduce the above copyright
|
12
|
+
# notice, this list of conditions and the following disclaimer in the
|
13
|
+
# documentation and/or other materials provided with the distribution.
|
14
|
+
#
|
15
|
+
# Neither the name of GROUPON nor the names of its contributors may be
|
16
|
+
# used to endorse or promote products derived from this software without
|
17
|
+
# specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
20
|
+
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
21
|
+
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
22
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
25
|
+
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
26
|
+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
27
|
+
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
28
|
+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
29
|
+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
module Bugwatch
|
32
|
+
|
33
|
+
class Tree
|
34
|
+
|
35
|
+
Content = Struct.new(:file, :size)
|
36
|
+
|
37
|
+
def initialize(grit=nil)
|
38
|
+
@grit = grit
|
39
|
+
end
|
40
|
+
|
41
|
+
def /(filepath)
|
42
|
+
@grit / filepath if @grit
|
43
|
+
end
|
44
|
+
|
45
|
+
def ruby_files
|
46
|
+
@files ||= get_ruby_files(@grit.contents)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def get_ruby_files(tree_contents)
|
52
|
+
tree_contents.reduce([]) do |count, type|
|
53
|
+
if type.is_a?(Grit::Blob) && RubyFileAdapter.ruby_file?(type.name)
|
54
|
+
count + [Content.new(type.name, type.size)]
|
55
|
+
elsif type.is_a?(Grit::Tree)
|
56
|
+
count + get_ruby_files(type.contents)
|
57
|
+
else
|
58
|
+
count
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|