doppelganger 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +31 -0
- data/LICENSE +22 -0
- data/Manifest.txt +32 -0
- data/README.rdoc +67 -0
- data/Rakefile +32 -0
- data/bin/doppelganger +189 -0
- data/doppelganger.gemspec +53 -0
- data/lib/doppelganger.rb +63 -0
- data/lib/doppelganger/extractor.rb +105 -0
- data/lib/doppelganger/exts/array.rb +23 -0
- data/lib/doppelganger/exts/sexp.rb +89 -0
- data/lib/doppelganger/node_analysis.rb +121 -0
- data/lib/doppelganger/unified_ruby.rb +358 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +192 -0
- data/tasks/git.rake +41 -0
- data/tasks/manifest.rake +48 -0
- data/tasks/post_load.rake +38 -0
- data/tasks/rdoc.rake +48 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +239 -0
- data/tasks/test.rake +34 -0
- data/test/array_test.rb +16 -0
- data/test/doppelganger_test.rb +112 -0
- data/test/sample_files/duplicate_test_data/first_file.rb +7 -0
- data/test/sample_files/duplicate_test_data/second_file.rb +7 -0
- data/test/sample_files/larger_diff/first_file.rb +7 -0
- data/test/sample_files/larger_diff/second_file.rb +7 -0
- data/test/sample_files/repeats_removal_sample_file.rb +94 -0
- data/test/sample_files/sexp_test_file.rb +7 -0
- data/test/sexp_ext_test.rb +53 -0
- data/test/test_helper.rb +79 -0
- metadata +158 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
== 0.8.0 / 2008-11-18
|
2
|
+
|
3
|
+
* Colorize CLI output
|
4
|
+
* Add progress bar in CLI
|
5
|
+
* Improve performance where possible
|
6
|
+
* Change to using Ruby2Ruby in CLI output when verbose
|
7
|
+
* Use custom UnifiedRuby instead of the one in ParseTree
|
8
|
+
* Separate analysis into two independent classes
|
9
|
+
* +Extractor+ extracts block-like nodes from code
|
10
|
+
* +NodeAnalysis+ analyzes for duplicates/similar nodes
|
11
|
+
|
12
|
+
== 0.7.0 / 2008-11-09
|
13
|
+
|
14
|
+
* Exclude literals from comparison (from Flay)
|
15
|
+
* Compare blocks as well as methods
|
16
|
+
* Move RubyParser from ParseTree
|
17
|
+
* Cleanup formating of CLI output
|
18
|
+
* Remove Unique node reporting
|
19
|
+
* Remove unneeded view/Ruby2Ruby functionality
|
20
|
+
* Improve and Simplify tests
|
21
|
+
* Change to using Test::Unit
|
22
|
+
* Improve Documentation
|
23
|
+
|
24
|
+
== 0.2.0 / 2008-11-06
|
25
|
+
|
26
|
+
* Add percent different functionality
|
27
|
+
* Name change to Doppelganger
|
28
|
+
|
29
|
+
== 0.1.0 / 2008-10-01
|
30
|
+
|
31
|
+
* Initial conversion to a gem
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2008 Brian Landau
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
CHANGELOG
|
2
|
+
LICENSE
|
3
|
+
Manifest.txt
|
4
|
+
README.rdoc
|
5
|
+
Rakefile
|
6
|
+
bin/doppelganger
|
7
|
+
doppelganger.gemspec
|
8
|
+
lib/doppelganger.rb
|
9
|
+
lib/doppelganger/extractor.rb
|
10
|
+
lib/doppelganger/exts/array.rb
|
11
|
+
lib/doppelganger/exts/sexp.rb
|
12
|
+
lib/doppelganger/node_analysis.rb
|
13
|
+
lib/doppelganger/unified_ruby.rb
|
14
|
+
tasks/bones.rake
|
15
|
+
tasks/gem.rake
|
16
|
+
tasks/git.rake
|
17
|
+
tasks/manifest.rake
|
18
|
+
tasks/post_load.rake
|
19
|
+
tasks/rdoc.rake
|
20
|
+
tasks/rubyforge.rake
|
21
|
+
tasks/setup.rb
|
22
|
+
tasks/test.rake
|
23
|
+
test/array_test.rb
|
24
|
+
test/doppelganger_test.rb
|
25
|
+
test/sample_files/duplicate_test_data/first_file.rb
|
26
|
+
test/sample_files/duplicate_test_data/second_file.rb
|
27
|
+
test/sample_files/larger_diff/first_file.rb
|
28
|
+
test/sample_files/larger_diff/second_file.rb
|
29
|
+
test/sample_files/repeats_removal_sample_file.rb
|
30
|
+
test/sample_files/sexp_test_file.rb
|
31
|
+
test/sexp_ext_test.rb
|
32
|
+
test/test_helper.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
= Doppelganger
|
2
|
+
|
3
|
+
by Brian Landau of Viget Labs <brian.landau@viget.com>
|
4
|
+
|
5
|
+
[<b>GitHub Project</b>] http://github.com/brianjlandau/doppelganger
|
6
|
+
[<b>RubyForge Project</b>] http://rubyforge.org/projects/doppelganger/
|
7
|
+
[<b>RDoc</b>] http://doppelganger.rubyforge.org/
|
8
|
+
|
9
|
+
|
10
|
+
== DESCRIPTION:
|
11
|
+
|
12
|
+
Doppelganger helps you to find areas of your code that are good places to refactor.
|
13
|
+
|
14
|
+
It does this by finding methods and blocks that are duplicates or have less then
|
15
|
+
a set threshold level of difference or be less then a specified percent different.
|
16
|
+
This library can either be used with in another larger code metric/heuristic library, or
|
17
|
+
called from the command line.
|
18
|
+
|
19
|
+
|
20
|
+
== FEATURES:
|
21
|
+
|
22
|
+
* Find duplicate methods and blocks.
|
23
|
+
* Find methods and blocks similar by a threshold level of difference.
|
24
|
+
* Find methods and blocks similar by threshold percentage.
|
25
|
+
* Uses the Diff::LCS library for finding differences.
|
26
|
+
* Uses RubyParser to compare methods.
|
27
|
+
* Outputs the set of similar/duplicate nodes together and what files they are in and their line numbers.
|
28
|
+
|
29
|
+
== CAVEATS:
|
30
|
+
|
31
|
+
Doing the Diff::LCS on all the flattened Sexps is a very time consuming and processor
|
32
|
+
intense operation. This means doing the "diff" or "percent_diff" comparisons on large
|
33
|
+
libraries can take a very long time.
|
34
|
+
|
35
|
+
|
36
|
+
== REQUIREMENTS:
|
37
|
+
|
38
|
+
* ruby_parser
|
39
|
+
* sexp_processor
|
40
|
+
* Diff::LCS
|
41
|
+
* Ruby2Ruby
|
42
|
+
* HighLine
|
43
|
+
* Facets
|
44
|
+
|
45
|
+
|
46
|
+
== USAGE:
|
47
|
+
|
48
|
+
Find duplicates and methods have 5 or less differences.
|
49
|
+
|
50
|
+
$ doppelganger -n 5 project_dir/lib
|
51
|
+
|
52
|
+
Find duplicates and methods that are 10 percent different or less.
|
53
|
+
|
54
|
+
$ doppelganger -p 10 project_dir/lib
|
55
|
+
|
56
|
+
|
57
|
+
== INSTALL:
|
58
|
+
|
59
|
+
sudo gem install doppelganger
|
60
|
+
|
61
|
+
|
62
|
+
=== CREDITS:
|
63
|
+
|
64
|
+
Inspired and based off of Giles Bowkett's Towelie.
|
65
|
+
|
66
|
+
Parts also influenced or extracted by Ryan Davis' Flay.
|
67
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
load 'tasks/setup.rb'
|
6
|
+
|
7
|
+
ensure_in_path 'lib'
|
8
|
+
require 'doppelganger'
|
9
|
+
|
10
|
+
task :default => 'test:run'
|
11
|
+
|
12
|
+
PROJ.name = 'doppelganger'
|
13
|
+
PROJ.authors = 'Brian Landau'
|
14
|
+
PROJ.email = 'brian.landau@viget.com'
|
15
|
+
PROJ.url = 'http://doppelganger.rubyforge.org/'
|
16
|
+
PROJ.version = Doppelganger::VERSION
|
17
|
+
PROJ.rubyforge.name = 'doppelganger'
|
18
|
+
|
19
|
+
PROJ.rcov.opts = ['--no-html', '-T', '--sort coverage',
|
20
|
+
'-x "\/Library\/Ruby\/"',
|
21
|
+
'-x "\/opt\/local\/lib/ruby"',
|
22
|
+
'-x "\/System\/Library\/"']
|
23
|
+
|
24
|
+
|
25
|
+
PROJ.gem.development_dependencies = [['thoughtbot-shoulda', '~> 2.0']]
|
26
|
+
depend_on 'sexp_processor', '~> 3.0.0'
|
27
|
+
depend_on 'ruby_parser', '~> 2.0.0'
|
28
|
+
depend_on 'ruby2ruby', '~> 1.2.0'
|
29
|
+
depend_on 'diff-lcs', '~> 1.1'
|
30
|
+
depend_on 'highline', '~> 1.4'
|
31
|
+
depend_on 'facets', '~> 2.4'
|
32
|
+
|
data/bin/doppelganger
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'facets/progressbar'
|
6
|
+
require File.expand_path( File.join(File.dirname(__FILE__), %w[.. lib doppelganger]) )
|
7
|
+
|
8
|
+
|
9
|
+
class Doppelganger::CLI
|
10
|
+
def self.parse(args)
|
11
|
+
options = OpenStruct.new
|
12
|
+
options.verbose = false
|
13
|
+
options.color = true
|
14
|
+
options.progress_bar = true
|
15
|
+
|
16
|
+
opts = OptionParser.new do |opts|
|
17
|
+
opts.program_name = 'doppelganger'
|
18
|
+
opts.version = Doppelganger.version
|
19
|
+
opts.banner = "Usage: #{opts.program_name} [options] DIRECTORY"
|
20
|
+
|
21
|
+
opts.separator " "
|
22
|
+
opts.separator "Options:"
|
23
|
+
|
24
|
+
opts.on('-t', "--threshold [N]", Integer, "The number of node differences to print blocks for.") do |n|
|
25
|
+
options.threshold = n
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('-p', "--percentage [N]", Integer, "The percent difference to look for in two blocks.") do |n|
|
29
|
+
options.percentage = n
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-v', "--[no-]verbose", "Display compared nodes when matched.") do |v|
|
33
|
+
options.verbose = v
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('--[no-]color', "Don't use color output.") do |c|
|
37
|
+
options.color = c
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on('--[no-]progress-bar', "Don't show the progress bar on Diffs.") do |pb|
|
41
|
+
options.progress_bar = pb
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.separator " "
|
45
|
+
|
46
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
47
|
+
puts opts.help
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on_tail("--version", "Show version") do
|
52
|
+
puts opts.ver
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.parse!(args)
|
58
|
+
if ARGV.length < 1
|
59
|
+
puts "ERROR: Missing expected arguments. ou must specify a directory parameter."
|
60
|
+
puts
|
61
|
+
puts opts.help
|
62
|
+
exit
|
63
|
+
elsif ARGV.length > 1
|
64
|
+
puts "ERROR: Too many arguments. Only a directory is needed."
|
65
|
+
puts
|
66
|
+
puts opts.help
|
67
|
+
exit
|
68
|
+
else
|
69
|
+
options.dir = ARGV.shift
|
70
|
+
end
|
71
|
+
|
72
|
+
@options = options
|
73
|
+
@root_path = File.expand_path(@options.dir)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.run(args)
|
77
|
+
parse(args)
|
78
|
+
|
79
|
+
if @options.color
|
80
|
+
gem 'highline', '~> 1.4'
|
81
|
+
require 'highline'
|
82
|
+
HighLine.color_scheme = HighLine::ColorScheme.new do |cs|
|
83
|
+
cs[:headline] = [:bold, :magenta]
|
84
|
+
cs[:set_start] = [:cyan]
|
85
|
+
cs[:horizontal_line] = [:white]
|
86
|
+
cs[:block_item] = [:red]
|
87
|
+
cs[:code] = [:green]
|
88
|
+
end
|
89
|
+
@hl = HighLine.new
|
90
|
+
end
|
91
|
+
|
92
|
+
@doppelganger = Doppelganger::Extractor.new
|
93
|
+
@sexp_blocks = @doppelganger.extract_blocks(@options.dir)
|
94
|
+
@analysis = Doppelganger::NodeAnalysis.new(@sexp_blocks)
|
95
|
+
@max_filename_length = longest_filename(@sexp_blocks)
|
96
|
+
|
97
|
+
if @options.verbose
|
98
|
+
gem 'ruby2ruby', '~> 1.2.0'
|
99
|
+
require 'ruby2ruby'
|
100
|
+
@ruby2ruby = Ruby2Ruby.new
|
101
|
+
end
|
102
|
+
|
103
|
+
duplicates = @analysis.duplicates
|
104
|
+
display_block_sets duplicates, "=== DUPLICATES: ===\n", ' -- [DUPLICATED BLOCK SET]:'
|
105
|
+
|
106
|
+
if @options.threshold
|
107
|
+
compare_nodes(:diff, @options.threshold, "MAX DIFF of #{@options.threshold} NODES")
|
108
|
+
end
|
109
|
+
|
110
|
+
if @options.percentage
|
111
|
+
compare_nodes(:percent_diff, @options.percentage, "PERCENT DIFF of #{@options.percentage}%")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.compare_nodes(method, diff_size, title)
|
116
|
+
print_text('-' * 80, :horizontal_line)
|
117
|
+
puts
|
118
|
+
if @options.progress_bar
|
119
|
+
progress_bar = ProgressBar.new("DIFF Progress", @sexp_blocks.size**2)
|
120
|
+
progress_bar.bar_mark = '='
|
121
|
+
similar_methods = @analysis.send(method, diff_size, progress_bar)
|
122
|
+
progress_bar.finish
|
123
|
+
puts
|
124
|
+
else
|
125
|
+
similar_methods = @analysis.send(method, diff_size)
|
126
|
+
end
|
127
|
+
display_block_sets similar_methods, "=== #{title}: ===\n", ' -- [SIMILAR BLOCK PAIR]:'
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.display_block_sets(set, title, set_title)
|
131
|
+
unless set.empty?
|
132
|
+
print_text(title, :headline)
|
133
|
+
puts unless @options.color
|
134
|
+
set.each do |block_set|
|
135
|
+
print_text(set_title, :set_start)
|
136
|
+
block_set.each do |block_node|
|
137
|
+
if block_node.respond_to?(:name)
|
138
|
+
display_filename_line_and_method_name(block_node)
|
139
|
+
else
|
140
|
+
display_filename_and_line(block_node)
|
141
|
+
end
|
142
|
+
if @options.verbose
|
143
|
+
print_text(@ruby2ruby.process(block_node.node.deep_clone).gsub(/\n/, "\n"+(' '*6)).insert(0, ' '*6), :code)
|
144
|
+
puts
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
puts
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.display_filename_line_and_method_name(method_defn)
|
153
|
+
print_text(sprintf(' -> %1$*4$s:#%2$*5$d => `%3$s`', format_filename(method_defn.filename),
|
154
|
+
method_defn.line.to_i, method_defn.name,
|
155
|
+
@max_filename_length.to_i, -5), :block_item)
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.display_filename_and_line(block_node)
|
159
|
+
print_text(sprintf(' -> %1$*3$s:#%2$*4$d', format_filename(block_node.filename),
|
160
|
+
block_node.line.to_i, @max_filename_length.to_i, -5), :block_item)
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.format_filename(filename)
|
164
|
+
relative_filename = filename.gsub(@root_path, '.')
|
165
|
+
if relative_filename.size > @max_filename_length
|
166
|
+
output_filename = relative_filename[-@max_filename_length, @max_filename_length]
|
167
|
+
output_filename[0..2] = '...'
|
168
|
+
else
|
169
|
+
output_filename = relative_filename
|
170
|
+
end
|
171
|
+
output_filename
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.longest_filename(nodes)
|
175
|
+
max_filename_length = nodes.map{|n| n.filename.gsub(@root_path, '.').size}.max
|
176
|
+
max_filename_length > 50 ? 50 : max_filename_length
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.print_text(text, color)
|
180
|
+
if @options.color
|
181
|
+
@hl.say(@hl.color(text, color))
|
182
|
+
else
|
183
|
+
puts text
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
Doppelganger::CLI.run(ARGV)
|
189
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{doppelganger}
|
3
|
+
s.version = "0.8.0"
|
4
|
+
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.authors = ["Brian Landau"]
|
7
|
+
s.date = %q{2008-11-18}
|
8
|
+
s.default_executable = %q{doppelganger}
|
9
|
+
s.description = %q{Doppelganger helps you to find areas of your code that are good places to refactor. It does this by finding methods and blocks that are duplicates or have less then a set threshold level of difference or be less then a specified percent different. This library can either be used with in another larger code metric/heuristic library, or called from the command line.}
|
10
|
+
s.email = %q{brian.landau@viget.com}
|
11
|
+
s.executables = ["doppelganger"]
|
12
|
+
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.rdoc"]
|
13
|
+
s.files = ["CHANGELOG", "LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "bin/doppelganger", "doppelganger.gemspec", "lib/doppelganger.rb", "lib/doppelganger/extractor.rb", "lib/doppelganger/exts/array.rb", "lib/doppelganger/exts/sexp.rb", "lib/doppelganger/node_analysis.rb", "lib/doppelganger/unified_ruby.rb", "tasks/bones.rake", "tasks/gem.rake", "tasks/git.rake", "tasks/manifest.rake", "tasks/post_load.rake", "tasks/rdoc.rake", "tasks/rubyforge.rake", "tasks/setup.rb", "tasks/test.rake", "test/array_test.rb", "test/doppelganger_test.rb", "test/sample_files/duplicate_test_data/first_file.rb", "test/sample_files/duplicate_test_data/second_file.rb", "test/sample_files/larger_diff/first_file.rb", "test/sample_files/larger_diff/second_file.rb", "test/sample_files/repeats_removal_sample_file.rb", "test/sample_files/sexp_test_file.rb", "test/sexp_ext_test.rb", "test/test_helper.rb"]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://doppelganger.rubyforge.org/}
|
16
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{doppelganger}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{Doppelganger helps you to find areas of your code that are good places to refactor}
|
21
|
+
s.test_files = ["test/array_test.rb", "test/doppelganger_test.rb", "test/sexp_ext_test.rb"]
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 2
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_runtime_dependency(%q<sexp_processor>, ["~> 3.0.0"])
|
29
|
+
s.add_runtime_dependency(%q<ruby_parser>, ["~> 2.0.0"])
|
30
|
+
s.add_runtime_dependency(%q<ruby2ruby>, ["~> 1.2.0"])
|
31
|
+
s.add_runtime_dependency(%q<diff-lcs>, ["~> 1.1"])
|
32
|
+
s.add_runtime_dependency(%q<highline>, ["~> 1.4"])
|
33
|
+
s.add_runtime_dependency(%q<facets>, ["~> 2.4"])
|
34
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, ["~> 2.0"])
|
35
|
+
else
|
36
|
+
s.add_dependency(%q<sexp_processor>, ["~> 3.0.0"])
|
37
|
+
s.add_dependency(%q<ruby_parser>, ["~> 2.0.0"])
|
38
|
+
s.add_dependency(%q<ruby2ruby>, ["~> 1.2.0"])
|
39
|
+
s.add_dependency(%q<diff-lcs>, ["~> 1.1"])
|
40
|
+
s.add_dependency(%q<highline>, ["~> 1.4"])
|
41
|
+
s.add_dependency(%q<facets>, ["~> 2.4"])
|
42
|
+
s.add_dependency(%q<thoughtbot-shoulda>, ["~> 2.0"])
|
43
|
+
end
|
44
|
+
else
|
45
|
+
s.add_dependency(%q<sexp_processor>, ["~> 3.0.0"])
|
46
|
+
s.add_dependency(%q<ruby_parser>, ["~> 2.0.0"])
|
47
|
+
s.add_dependency(%q<ruby2ruby>, ["~> 1.2.0"])
|
48
|
+
s.add_dependency(%q<diff-lcs>, ["~> 1.1"])
|
49
|
+
s.add_dependency(%q<highline>, ["~> 1.4"])
|
50
|
+
s.add_dependency(%q<facets>, ["~> 2.4"])
|
51
|
+
s.add_dependency(%q<thoughtbot-shoulda>, ["~> 2.0"])
|
52
|
+
end
|
53
|
+
end
|
data/lib/doppelganger.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'find'
|
2
|
+
require 'rubygems'
|
3
|
+
gem 'sexp_processor', '~> 3.0.0'
|
4
|
+
require 'sexp_processor'
|
5
|
+
require 'composite_sexp_processor'
|
6
|
+
gem 'ruby_parser', '~> 2.0.0'
|
7
|
+
require 'ruby_parser'
|
8
|
+
gem 'diff-lcs', '~> 1.1'
|
9
|
+
require 'diff/lcs'
|
10
|
+
|
11
|
+
# Equivalent to a header guard in C/C++
|
12
|
+
# Used to prevent the class/module from being loaded more than once
|
13
|
+
unless defined? Doppelganger
|
14
|
+
|
15
|
+
module Doppelganger
|
16
|
+
|
17
|
+
# :stopdoc:
|
18
|
+
VERSION = '0.8.0'.freeze
|
19
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
20
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
21
|
+
|
22
|
+
# Returns the version string for the library.
|
23
|
+
#
|
24
|
+
def self.version
|
25
|
+
VERSION
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the library path for the module. If any arguments are given,
|
29
|
+
# they will be joined to the end of the libray path using
|
30
|
+
# <tt>File.join</tt>.
|
31
|
+
#
|
32
|
+
def self.libpath( *args )
|
33
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the lpath for the module. If any arguments are given,
|
37
|
+
# they will be joined to the end of the path using
|
38
|
+
# <tt>File.join</tt>.
|
39
|
+
#
|
40
|
+
def self.path( *args )
|
41
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Utility method used to rquire all files ending in .rb that lie in the
|
45
|
+
# directory below this file that has the same name as the filename passed
|
46
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
47
|
+
# the _filename_ does not have to be equivalent to the directory.
|
48
|
+
#
|
49
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
50
|
+
dir ||= ::File.basename(fname, '.*')
|
51
|
+
search_me = ::File.expand_path(
|
52
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
53
|
+
|
54
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
55
|
+
end
|
56
|
+
# :startdoc:
|
57
|
+
|
58
|
+
end # module Doppelganger
|
59
|
+
|
60
|
+
Doppelganger.require_all_libs_relative_to __FILE__
|
61
|
+
|
62
|
+
end # unless defined?
|
63
|
+
|