angelic-dont_repeat_yourself 0.3.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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005-2007 The RSpec Development Team
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,13 @@
1
+ dont_repeat_yourself gem
2
+
3
+ Up to date documentation is online on github:
4
+ http://github.com/garnierjm/dont_repeat_yourself/wikis
5
+
6
+ Based on Simian (Similarity Analyser) by Simon Harris from RedHill Consulting, see http://www.redhillconsulting.com.au/products/simian/
7
+ Copyright (c) 2003-08 RedHill Consulting Pty. Ltd. All rights reserved.
8
+
9
+ Report duplicate lines in your code, integrated with Textmate and Netbeans.
10
+
11
+ - License dry-report gem: See ./MIT-LICENSE
12
+ - License Simian: See ./SIMIAN-LICENSE file
13
+
@@ -0,0 +1,117 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+
5
+ #-------- Deployment tasks
6
+ begin
7
+ require "hoe"
8
+ rescue LoadError
9
+ puts "This Rakefile requires the 'hoe' RubyGem."
10
+ puts "Installation: (sudo) gem install hoe -y"
11
+ exit
12
+ end
13
+
14
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
15
+ require 'dont_repeat_yourself/version'
16
+
17
+ # FIXME In ordert to remove hoe from gemspec, DOES NOT WORK!
18
+ class Hoe
19
+ def extra_deps
20
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
21
+ @extra_deps
22
+ end
23
+ end
24
+
25
+ AUTHOR = 'Jean-Michel Garnier' # can also be an array of Authors
26
+ EMAIL = 'jm AT 21croissants dot com'
27
+ DESCRIPTION = 'Generate duplicate lines report'
28
+ GEM_NAME = 'dont_repeat_yourself' # what ppl will type to install your gem
29
+ HOMEPATH = "http://github.com/garnierjm/dont_repeat_yourself"
30
+ VERS = DontRepeatYourself::VERSION::STRING
31
+
32
+ hoe = Hoe.new(GEM_NAME, VERS) do |p|
33
+ p.developer(AUTHOR, EMAIL)
34
+ p.description = DESCRIPTION
35
+ p.summary = DESCRIPTION
36
+ p.url = HOMEPATH
37
+ p.clean_globs |= ['.project', '.gitignore', '**/.*.sw?', '*.gem', '.config', '**/.DS_Store', '**/*.class'] #An array of file patterns to delete on clean.
38
+
39
+ # == Optional
40
+
41
+ # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
42
+ p.extra_deps = [ ['syntax', ">= 1.0.0"] ]
43
+
44
+ end
45
+
46
+ desc "Refresh #{GEM_NAME}.gemspec to include ALL files"
47
+ task :refresh_gemspec => 'refresh_manifest' do
48
+ File.open("#{GEM_NAME}.gemspec", 'w') {|io| io.write(hoe.spec.to_ruby)}
49
+ end
50
+
51
+ desc 'Recreate Manifest.txt to include ALL files'
52
+ task :refresh_manifest do
53
+ manifest_files = %w(SIMIAN-LICENSE MIT-LICENSE README.txt Rakefile) + Dir.glob("{bin,lib}/**/*")
54
+ File.open("Manifest.txt", 'w') {|io| io.write(manifest_files.join("\n"))}
55
+ end
56
+
57
+
58
+ desc 'Install the package as a gem, without generating documentation(ri/rdoc)'
59
+ task :install_gem_no_doc => [:clean, :package] do
60
+ sh "#{'sudo ' unless Hoe::WINDOZE }gem install pkg/*.gem --no-rdoc --no-ri"
61
+ end
62
+
63
+ namespace :manifest do
64
+ desc 'Recreate Manifest.txt to include ALL files'
65
+ task :refresh do
66
+ `rake check_manifest | patch -p0 > Manifest.txt`
67
+ end
68
+ end
69
+
70
+ #-------------- Testing taks
71
+ desc 'Default: run specs.'
72
+ task :default => :spec
73
+
74
+ desc "Run all specs"
75
+ Spec::Rake::SpecTask.new do |t|
76
+ t.spec_files = FileList['spec/**/*_spec.rb']
77
+ t.spec_opts = ['--options', 'spec/spec.opts']
78
+ end
79
+
80
+ desc "Generate documentation for the dont_repeat_yourself plugin and store html output in doc.html"
81
+ Spec::Rake::SpecTask.new('doc') do |t|
82
+ t.spec_files = FileList['spec/**/*_spec.rb']
83
+ t.spec_opts = ['--format html:./doc.html']
84
+ # t.spec_opts = ['--format specdoc:./doc.txt']
85
+ end
86
+
87
+ desc "Run all examples with RCov and generate specs coverage report"
88
+ Spec::Rake::SpecTask.new('coverage') do |t|
89
+ t.spec_files = FileList['spec/**/*_spec.rb']
90
+ t.rcov = true
91
+ t.rcov_opts = ['--exclude', 'spec,boot.rb']
92
+ end
93
+
94
+ require 'cucumber/rake/task'
95
+ desc "Run User Acceptance tests with cucumber"
96
+ Cucumber::Rake::Task.new(:features) do |t|
97
+ t.cucumber_opts = "--format pretty"
98
+ end
99
+
100
+ def egrep(pattern)
101
+ Dir['**/*.rb'].each do |fn|
102
+ count = 0
103
+ open(fn) do |f|
104
+ while line = f.gets
105
+ count += 1
106
+ if line =~ pattern
107
+ puts "#{fn}:#{count}:#{line}"
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ desc "Look for TODO and FIXME tags in the code"
115
+ task :todo do
116
+ egrep /(FIXME|TODO|TBD)/
117
+ end
@@ -0,0 +1,101 @@
1
+ SIMIAN VERSION 2.2.21 SOFTWARE LICENSE AGREEMENT
2
+
3
+ 1. Licenses and Software
4
+
5
+ RedHill Consulting, Pty. Ltd., an Australian Proprietary Limited Company ("REDHILL") hereby grants to the purchaser
6
+ (the "LICENSEE") a limited, revocable, worldwide, non-exclusive, nontransferable, non-sublicensable license to use the
7
+ Simian version 2 (two) software (the "SOFTWARE"), including any minor upgrades thereof during the Term (hereinafter
8
+ defined) up to, but not including the next major version of the Software. The Licensee shall not, or knowingly allow
9
+ others to, reverse engineer, decompile, disassemble, modify, adapt, create derivative works from or otherwise attempt
10
+ to derive source code from the Software provided. And, in accordance with the terms and conditions of this Software
11
+ License Agreement (the "AGREEMENT"), the Software shall be used solely by the Licensee in accordance with the following
12
+ specific conditions:
13
+
14
+ A. Personal/SOHO License
15
+
16
+ A Personal/SOHO License entitles the Licensee to use the Software on one (1) machine only. A Personal/SOHO
17
+ License does not permit the generation of reports for distribution nor use by other than the licensee.
18
+
19
+ B. Project License
20
+
21
+ A Project License entitles the Licensee to use the Software on any number of machines solely for the licensed
22
+ project.
23
+
24
+ C. Enterprise License
25
+
26
+ An Enterprise License entitles the Licensee to use the Software on any number of machines. Reports generated are
27
+ strictly for use by the Licensee only.
28
+
29
+ 2. License Fee
30
+
31
+ In exchange for the License(s), the Licensee shall pay to RedHill a one-time, up front, non-refundable license fee.
32
+ At the sole discretion of RedHill, this fee will be waived for non-commercial/non-government projects and for evaluation
33
+ purposes for a period of fifteen (15) days only. The Licensee is also entitled to minor upgrades up to, but not
34
+ including the next major version of the Software at no charge. Notwithstanding the Licensee's payment of the License
35
+ Fee, RedHill reserves the right to terminate the License if RedHill discovers that the Licensee and/or the Licensee's
36
+ use of the Software is in breach of this Agreement.
37
+
38
+ 3. Proprietary Rights
39
+
40
+ RedHill will retain all right, title and interest in and to the Software, all copies thereof, and RedHill website(s),
41
+ software, and other intellectual property, including, but not limited to, ownership of all copyrights, look and feel,
42
+ trademark rights, design rights, trade secret rights and any and all other intellectual property and other proprietary
43
+ rights therein. The Licensee will not directly or indirectly obtain or attempt to obtain at any time, any right, title
44
+ or interest by registration or otherwise in or to the trademarks, service marks, copyrights, trade names, symbols,
45
+ logos or designations or other intellectual property rights owned or used by RedHill. All technical manuals or other
46
+ information provided by RedHill to the Licensee shall be the sole property of RedHill.
47
+
48
+ 4. Term and Termination
49
+
50
+ Subject to the other provisions hereof, this Agreement shall commence upon the Licensee's opting into this Agreement
51
+ and continue until the Licensee discontinues use of the Software or the Agreement terminates automatically upon the
52
+ Licensee's breach of any term or condition of this Agreement (the "Term"). Upon any such termination, the Licensee will
53
+ delete the Software immediately.
54
+
55
+ 5. Copying & Transfer
56
+
57
+ The Licensee may copy the Software to use the Software and for back-up purposes only. The Licensee may not assign or
58
+ otherwise transfer the Software to any third party unless each of the following conditions is met:
59
+
60
+ 1. Redistributions are made at no charge and in accordance with these License terms.
61
+
62
+ A. Redistributions are made by and for non-commercial/non-government Licensee(s) or for evaluation purposes set
63
+ forth as Article 2.
64
+
65
+ B. Redistributions must faithfully reproduce all accompanying materials, including these License terms, and the
66
+ disclaimer/limitation of liability set forth as Article 6, in the documentation and/or other materials provided
67
+ with the distribution.
68
+
69
+ 6. Specific Disclaimer of Warranty and Limitation of Liability
70
+
71
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
72
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL REDHILL
73
+ CONSULTING OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
74
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
75
+ BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
76
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
77
+ POSSIBILITY OF SUCH DAMAGE.
78
+
79
+ 7. Warranties and Representations
80
+
81
+ Indemnification. The Licensee warrants and represents that the Licensee's actions with regard to the Software will be
82
+ in compliance with all applicable laws; and the Licensee will indemnify, defend, and hold RedHill harmless from and
83
+ against any and all liabilities, damages, losses, claims, costs, and expenses (including legal fees) arising out of or
84
+ resulting from the Licensee's failure to observe the use restrictions set forth herein.
85
+
86
+ 8. Governing Law
87
+
88
+ This Agreement shall be governed by the laws of Victoria, Australia.
89
+
90
+ 9. Independent Contractors
91
+
92
+ Assignment: The parties are independent contractors with respect to each other, and nothing in this Agreement shall be
93
+ construed as creating an employer-employee relationship, a partnership, agency relationship or a joint venture between
94
+ the parties. This Agreement is not assignable or transferable by the Licensee.
95
+
96
+ 10. Entire Agreement
97
+
98
+ This Agreement constitutes the entire agreement between the parties concerning the Licensee's use of the Software. This
99
+ Agreement supersedes any prior verbal understanding between the parties and any Licensee purchase order or other
100
+ ordering document, regardless of whether such document is received by RedHill before or after execution of this
101
+ Agreement. This Agreement may be amended only in writing by RedHill.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.dirname(__FILE__) + '/../lib/') unless $:.include?(File.dirname(__FILE__) + '/../lib/')
3
+ require 'dont_repeat_yourself/cli'
4
+ DontRepeatYourself::CLI.execute
@@ -0,0 +1,127 @@
1
+ #rspec-header {
2
+ background: #65C400; color: #fff;
3
+ }
4
+
5
+ .rspec-report h1 {
6
+ margin: 0px 10px 0px 10px;
7
+ padding: 10px;
8
+ font-family: "Lucida Grande", Helvetica, sans-serif;
9
+ font-size: 1.8em;
10
+ }
11
+
12
+ #summary {
13
+ margin: 0; padding: 5px 10px;
14
+ font-family: "Lucida Grande", Helvetica, sans-serif;
15
+ text-align: right;
16
+ position: absolute;
17
+ top: 0px;
18
+ right: 0px;
19
+ }
20
+
21
+ #summary p {
22
+ margin: 0 0 0 2px;
23
+ }
24
+
25
+ #summary #totals {
26
+ font-size: 1.2em;
27
+ }
28
+
29
+ .behaviour {
30
+ margin: 0 10px 5px;
31
+ background: #fff;
32
+ }
33
+
34
+ dl {
35
+ margin: 0; padding: 0 0 5px;
36
+ font: normal 11px "Lucida Grande", Helvetica, sans-serif;
37
+ }
38
+
39
+ dt {
40
+ padding: 3px;
41
+ background: #65C400;
42
+ color: #fff;
43
+ font-weight: bold;
44
+ }
45
+
46
+ dd {
47
+ margin: 5px 0 5px 5px;
48
+ padding: 3px 3px 3px 18px;
49
+ }
50
+
51
+ dd.spec.passed {
52
+ border-left: 5px solid #65C400;
53
+ border-bottom: 1px solid #65C400;
54
+ background: #DBFFB4; color: #3D7700;
55
+ }
56
+
57
+ dd.spec.failed {
58
+ border-left: 5px solid #C20000;
59
+ border-bottom: 1px solid #C20000;
60
+ color: #C20000; background: #FFFBD3;
61
+ }
62
+
63
+ dd.spec.not_implemented {
64
+ border-left: 5px solid #FAF834;
65
+ border-bottom: 1px solid #FAF834;
66
+ background: #FCFB98; color: #131313;
67
+ }
68
+
69
+ dd.spec.pending_fixed {
70
+ border-left: 5px solid #0000C2;
71
+ border-bottom: 1px solid #0000C2;
72
+ color: #0000C2; background: #D3FBFF;
73
+ }
74
+
75
+ .backtrace {
76
+ color: #000;
77
+ font-size: 12px;
78
+ }
79
+
80
+ a {
81
+ color: #BE5C00;
82
+ }
83
+
84
+ div.rspec-report div.dyn-source {
85
+ background:#FFFFEE none repeat scroll 0%;
86
+ border:1px dotted black;
87
+ color:#000000;
88
+ display:none;
89
+ margin:0.5em 2em;
90
+ padding:0.5em;
91
+ }
92
+
93
+ /* Ruby code, style similar to vibrant ink */
94
+ .ruby {
95
+ font-size: 12px;
96
+ font-family: monospace;
97
+ color: white;
98
+ background-color: black;
99
+ padding: 0.1em 0 0.2em 0;
100
+ }
101
+
102
+ .ruby .keyword { color: #FF6600; }
103
+ .ruby .constant { color: #339999; }
104
+ .ruby .attribute { color: white; }
105
+ .ruby .global { color: white; }
106
+ .ruby .module { color: white; }
107
+ .ruby .class { color: white; }
108
+ .ruby .string { color: #66FF00; }
109
+ .ruby .ident { color: white; }
110
+ .ruby .method { color: #FFCC00; }
111
+ .ruby .number { color: white; }
112
+ .ruby .char { color: white; }
113
+ .ruby .comment { color: #9933CC; }
114
+ .ruby .symbol { color: white; }
115
+ .ruby .regex { color: #44B4CC; }
116
+ .ruby .punct { color: white; }
117
+ .ruby .escape { color: white; }
118
+ .ruby .interp { color: white; }
119
+ .ruby .expr { color: white; }
120
+
121
+ .ruby .offending { background-color: gray; }
122
+ .ruby .linenum {
123
+ width: 75px;
124
+ padding: 0.1em 1em 0.2em 0;
125
+ color: #000000;
126
+ background-color: #FFFBD3;
127
+ }
@@ -0,0 +1,29 @@
1
+ // Lifted from Ruby RDoc
2
+ function toggleSource( id ) {
3
+ var elem
4
+ var link
5
+
6
+ if( document.getElementById )
7
+ {
8
+ elem = document.getElementById( id )
9
+ link = document.getElementById( "l_" + id )
10
+ }
11
+ else if ( document.all )
12
+ {
13
+ elem = eval( "document.all." + id )
14
+ link = eval( "document.all.l_" + id )
15
+ }
16
+ else
17
+ return false;
18
+
19
+ if( elem.style.display == "block" )
20
+ {
21
+ elem.style.display = "none"
22
+ link.innerHTML = "Show duplicate lines source code"
23
+ }
24
+ else
25
+ {
26
+ elem.style.display = "block"
27
+ link.innerHTML = "Hide duplicate lines source code"
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+
3
+ require 'dont_repeat_yourself/cli'
4
+ require 'dont_repeat_yourself/unit_testing_helpers'
5
+
6
+ module DontRepeatYourself
7
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/reporter'
2
+ require 'optparse'
3
+
4
+ module DontRepeatYourself
5
+ class CLI
6
+
7
+ class << self
8
+
9
+ def execute
10
+ parse(ARGV).execute!
11
+ end
12
+
13
+ def parse(args)
14
+ cli = new
15
+ cli.parse_options!(args)
16
+ cli
17
+ end
18
+ end
19
+
20
+ attr_reader :options
21
+ FORMATS = DontRepeatYourself::REPORT_TYPES.map{|format| format.downcase}
22
+ REPORTERS = DontRepeatYourself::REPORTER_TYPES.map{|reporter| reporter.downcase}
23
+
24
+ def initialize
25
+
26
+ end
27
+
28
+ def parse_options!(args)
29
+ args.extend(OptionParser::Arguable)
30
+
31
+ @options = { :basedir => './', :format => 'default', :reporter => 'ruby' }
32
+ args.options do |opts|
33
+ opts.banner = "Usage: dry-report [options] "
34
+
35
+ opts.on("-d BASEDIR", "--basedir BASEDIR", "set up the base directory of your ruby project, current directory by default") do |b|
36
+ @options[:basedir] = b
37
+ end
38
+
39
+ opts.on("-f FORMAT", "--format FORMAT", "report format (default is plain text)",
40
+ "Available formats: #{FORMATS.join(", ")}") do |v|
41
+ unless FORMATS.index(v)
42
+ STDERR.puts "Invalid format: #{v}\n"
43
+ STDERR.puts opts.help
44
+ exit 1
45
+ end
46
+ @options[:format] = v
47
+ end
48
+
49
+ opts.on("-t THRESHOLD", "--threshold THRESHOLD", "threshold of duplicate lines") do |t|
50
+ @options[:threshold] = t.to_i
51
+ end
52
+
53
+ opts.on("-r REPORTER", "--reporter REPORTER", "reporter to use (default is ruby): ruby, rails, or plugin",
54
+ "Available reporters: #{REPORTERS.join(", ")}") do |v|
55
+ unless REPORTERS.index(v)
56
+ STDERR.puts "Invalid reporter: #{v}\n"
57
+ STDERR.puts opts.help
58
+ exit 1
59
+ end
60
+ @options[:reporter] = v
61
+ end
62
+
63
+ opts.on_tail("--help", "You're looking at it. Any question? http://21croissants.blogspot.com/2008/10/dry.html") do
64
+ puts opts.help
65
+ exit
66
+ end
67
+ end.parse!
68
+
69
+ end
70
+
71
+ def execute!
72
+ ruby_project_reporter = DontRepeatYourself::ProjectReporterBase.build_reporter(@options[:reporter], @options[:basedir])
73
+ ruby_project_reporter.with_threshold_of_duplicate_lines(@options[:threshold]) if @options.has_key?(:threshold)
74
+ ruby_project_reporter.send("with_#{@options[:format]}_reporting") if @options.has_key?(:format)
75
+ STDOUT.print(ruby_project_reporter.report)
76
+ end
77
+ end # class
78
+
79
+ end
@@ -0,0 +1,139 @@
1
+ require File.dirname(__FILE__) + '/simian_results'
2
+ require File.dirname(__FILE__) + '/snippet_extractor'
3
+
4
+ module DontRepeatYourself
5
+
6
+ class FormatterFactory
7
+
8
+ # TODO Use a kind of Dependency Injection here, the plugin should not know about this class
9
+ @@snippet_extractor = DontRepeatYourself::SnippetExtractor.new
10
+
11
+ def self.create_report(report_type, simian_results)
12
+ formatter_class = DontRepeatYourself.const_get("#{report_type}Formatter")
13
+ return formatter_class.new(@@snippet_extractor, simian_results).report
14
+ end
15
+ end
16
+
17
+ class DefaultFormatter
18
+
19
+ attr_reader :simian_results
20
+
21
+ # Inject dependency thtough
22
+ def initialize(snippet_extractor, simian_results)
23
+ @snippet_extractor = snippet_extractor
24
+ @simian_results = simian_results
25
+ end
26
+
27
+ def report
28
+ report_body.gsub(/TWO_SPACE_CHARS/, " ")
29
+ end
30
+
31
+ # Protected methods to be used by formatters
32
+ # protected
33
+
34
+ def report_body
35
+ body = ""
36
+ body << @simian_results.sentence_processed_a_total_of_x_significant_lines_in_y_files << "\n"
37
+ body << @simian_results.sentence_found_x_duplicate_lines_in_y_blocks_in_z_files << "\n\n"
38
+ @simian_results.sets.each{|set|
39
+ body << set.sentence_found_x_duplicate_lines_in_the_following_files << "\n"
40
+ set.blocks.each{ |block|
41
+ body << "TWO_SPACE_CHARS" << format_sentence_between_lines_x_and_y_in_filepath(block) << "\n"
42
+ }
43
+ body << "TWO_SPACE_CHARS" << format_duplicate_lines_snippet(set.blocks.last) << "\n"
44
+ }
45
+ body
46
+ end
47
+
48
+ # Default, return the sentence
49
+ def format_sentence_between_lines_x_and_y_in_filepath(block)
50
+ block.sentence_between_lines_x_and_y_in_filepath
51
+ end
52
+
53
+ def format_duplicate_lines_snippet(block)
54
+ snippet = "Duplicate lines:\n"
55
+ snippet << @snippet_extractor.plain_source_code(block.line_number_of_first_duplicate_line, block.line_number_of_last_duplicate_line, block.file_path)
56
+ end
57
+
58
+ end
59
+
60
+ class NetbeansFormatter < DefaultFormatter
61
+ def format_sentence_between_lines_x_and_y_in_filepath(block)
62
+ block.sentence_between_lines_x_and_y_in_filepath << ":#{block.line_number_of_first_duplicate_line}:"
63
+ end
64
+ end
65
+
66
+ class HTMLFormatter < DefaultFormatter
67
+
68
+ def report
69
+ report = report_header
70
+ report << report_body.gsub(/TWO_SPACE_CHARS/, "&nbsp;&nbsp;").gsub(/\n/, "</br>\n")
71
+ report << report_footer
72
+ end
73
+
74
+ def format_duplicate_lines_snippet(block)
75
+ starts = block.line_number_of_first_duplicate_line
76
+ ends = block.line_number_of_last_duplicate_line
77
+ file_path = block.file_path
78
+ html_source_code = @snippet_extractor.snippet(starts, ends, file_path)
79
+
80
+ source_id = "#{File.basename(file_path)}_#{starts}_#{ends}"
81
+ source_code_div = " <div>&nbsp;&nbsp;[<a id=\"l_#{source_id}\" href=\"javascript:toggleSource('#{source_id}')\">Show duplicate lines source code</a>]</div>"
82
+ source_code_div << " <div id=\"#{source_id}\" class=\"dyn-source\"><pre class=\"ruby\"><code>#{html_source_code}</code></pre></div>"
83
+ end
84
+
85
+ # TODO use erb to generate the report?
86
+ def get_asset(asset)
87
+ IO.read(File.dirname(__FILE__) + '/../assets/' + asset)
88
+ end
89
+
90
+ def report_header
91
+ global_scripts = get_asset('dry.js')
92
+ global_styles = get_asset('/dry.css')
93
+ # TODO use erb.html ;-)
94
+ <<-EOF
95
+ <html>
96
+ <head>
97
+ <script type="text/javascript">
98
+ // <![CDATA[
99
+ #{global_scripts}
100
+ // ]]>
101
+ </script>
102
+ <style type="text/css">
103
+ #{global_styles}
104
+ </style>
105
+ </head>
106
+
107
+ <body>
108
+ <div class="rspec-report">
109
+
110
+ <div id="rspec-header">
111
+ <h1>Don't Repeat Yourself report Result</h1>
112
+ </div>
113
+
114
+ <div class="results">
115
+ EOF
116
+ end
117
+
118
+ def report_footer
119
+ <<-EOF
120
+ </div>
121
+ </div>
122
+ </body>
123
+ </html>
124
+ EOF
125
+ end
126
+ end
127
+
128
+ class TextMateFormatter < HTMLFormatter
129
+
130
+ def format_sentence_between_lines_x_and_y_in_filepath(block)
131
+ starts = block.line_number_of_first_duplicate_line
132
+ file_path = block.file_path
133
+ sentence = block.sentence_between_lines_x_and_y_in_filepath
134
+ "<a href='txmt://open?url=file://#{file_path}&line=#{starts}'>#{sentence}</a>"
135
+ end
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,144 @@
1
+ require File.dirname(__FILE__) + '/simian_runner'
2
+ require File.dirname(__FILE__) + '/simian_results'
3
+ require File.dirname(__FILE__) + '/formatter'
4
+ require 'pathname'
5
+
6
+ module DontRepeatYourself
7
+
8
+ # TODO Refactor: Use an enum gem / plugin here?
9
+ DEFAULT_REPORT, HTML_REPORT, NETBEANS_REPORT, TEXTMATE_REPORT =
10
+ "Default", "HTML", "Netbeans", "TextMate" unless defined?(DontRepeatYourself::DEFAULT_REPORT)
11
+
12
+ DEFAULT_REPORT_DESC = "display the default plain report"
13
+ NETBEANS_REPORT_DESC = "display the report in the Output window of the Netbeans IDE (Ctrl+4)"
14
+ HTML_REPORT_DESC = "generate an DRY_report.html file in the project root folder"
15
+ TEXTMATE_REPORT_DESC = "to generate an html report with links which open files in the Textmate editor"
16
+
17
+ REPORT_TYPES = [DEFAULT_REPORT, NETBEANS_REPORT, HTML_REPORT, TEXTMATE_REPORT] unless defined?(DontRepeatYourself::REPORT_TYPES)
18
+
19
+ RUBY, RAILS, PLUGIN = "Ruby", "Rails", "Plugin" unless defined?(DontRepeatYourself::RUBY)
20
+ REPORTER_TYPES = [RUBY, RAILS, PLUGIN] unless defined?(DontRepeatYourself::REPORTER_TYPES)
21
+
22
+ class ProjectReporterBase
23
+ attr_reader :name, :maximum_number_of_duplicate_lines_i_want_in_my_project, :report_type
24
+
25
+ def initialize(name)
26
+ @name = name
27
+ @simian_runner = DontRepeatYourself::SimianRunner.new
28
+
29
+ # Default values
30
+ @maximum_number_of_duplicate_lines_i_want_in_my_project = 0
31
+ @report_type = DontRepeatYourself::DEFAULT_REPORT
32
+ end
33
+
34
+ def patterns_of_directories_to_search_for_duplicate_lines
35
+ @simian_runner.patterns_of_directories_to_search_for_duplicate_lines
36
+ end
37
+
38
+ # Fluent interface methods
39
+ def with_threshold_of_duplicate_lines(threshold)
40
+ @simian_runner.threshold = threshold
41
+ return self
42
+ end
43
+
44
+ # with_<REPORT_TYPE>_reporting fluent methods
45
+ REPORT_TYPES.each do |report_type|
46
+ define_method("with_#{report_type.downcase}_reporting") do
47
+ @report_type = eval("DontRepeatYourself::#{report_type.upcase}_REPORT")
48
+ self
49
+ end
50
+ end
51
+
52
+ def ignoring_the_file(path)
53
+ @simian_runner.ignore_file(path)
54
+ self
55
+ end
56
+
57
+ def ignoring_the_directory(path)
58
+ @simian_runner.ignore_directory(path)
59
+ self
60
+ end
61
+
62
+ def basedir
63
+ @simian_runner.basedir
64
+ end
65
+
66
+ def report
67
+ DontRepeatYourself::FormatterFactory.create_report(@report_type, run_simian)
68
+ end
69
+
70
+ # TODO Not very readable: you have to read the code of run_simian to understand
71
+ def is_dry?
72
+ run_simian.duplicate_line_count <= @maximum_number_of_duplicate_lines_i_want_in_my_project
73
+ end
74
+
75
+ def description
76
+ "DRY\n" << " - with a threshold of #{@simian_runner.threshold} duplicate lines"
77
+ end
78
+
79
+ def failure_message
80
+ "expected #{@name} to have less or equal #{@maximum_number_of_duplicate_lines_i_want_in_my_project} duplicate lines :\n
81
+ DRY Report:\n#{report}\n"
82
+ end
83
+
84
+ def self.build_reporter(reporter, basedir)
85
+ case reporter
86
+ when 'ruby' then DontRepeatYourself::RubyProjectReporter.new(basedir)
87
+ when 'rails' then DontRepeatYourself::RailsProjectReporter.new(basedir)
88
+ when 'plugin' then DontRepeatYourself::RailsPluginProjectReporter.new(basedir)
89
+ end
90
+ end
91
+ protected
92
+
93
+ def run_simian
94
+ return @simian_results if @simian_results # so we don't run simian if we already have results
95
+ self.configure_simian
96
+ results_in_yaml_format = @simian_runner.run
97
+ @simian_results = DontRepeatYourself::SimianResults.new(results_in_yaml_format)
98
+ end
99
+
100
+ def basename_of_directory(directory)
101
+ Pathname.new(directory).basename.to_s
102
+ end
103
+
104
+ def folder_exists?(folder)
105
+ File.directory?(@simian_runner.basedir + "/" + folder)
106
+ end
107
+ end
108
+
109
+ # TODO Rename to RubyGemProjectReporter. we might add a "java" project reporter at some point 1 day ;-)
110
+ class RubyProjectReporter < ProjectReporterBase
111
+ def initialize(project_path)
112
+ super(basename_of_directory(project_path))
113
+ @simian_runner.basedir = project_path
114
+ end
115
+
116
+ def configure_simian
117
+ # Only add the "lib" directoy if exists
118
+ @simian_runner.add_ruby_directory_to_search_for_duplicate_lines("lib")
119
+ @simian_runner.add_ruby_directory_to_search_for_duplicate_lines("test") if folder_exists?("test")
120
+ @simian_runner.add_ruby_directory_to_search_for_duplicate_lines("spec") if folder_exists?("spec")
121
+ end
122
+ end
123
+
124
+ class RailsProjectReporter < RubyProjectReporter
125
+ def configure_simian
126
+ @simian_runner.add_ruby_directory_to_search_for_duplicate_lines("app")
127
+ @simian_runner.add_ruby_directory_to_search_for_duplicate_lines("lib")
128
+ @simian_runner.add_html_directory_to_search_for_duplicate_lines("app/views")
129
+ end
130
+ end
131
+
132
+ class RailsPluginProjectReporter < ProjectReporterBase
133
+ def initialize(plugin_name)
134
+ super(plugin_name )
135
+ end
136
+
137
+ def configure_simian
138
+ @simian_runner.basedir = "#{RAILS_ROOT}/vendor/plugins/#{@name}"
139
+ @simian_runner.add_ruby_directory_to_search_for_duplicate_lines("lib")
140
+ # @project_name = project_basename + " plugin"
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,92 @@
1
+ module DontRepeatYourself
2
+ class SimianResults
3
+ attr_reader :duplicate_file_count,
4
+ :duplicate_line_count,
5
+ :duplicate_block_count,
6
+ :total_significant_line_count,
7
+ :total_raw_line_count,
8
+ :total_file_count,
9
+ :sets
10
+
11
+ def initialize(simian_log_yaml)
12
+ @simian_log_yaml = simian_log_yaml
13
+
14
+ # TODO Use some code to generate this boring code!
15
+ @duplicate_file_count = summary['duplicateFileCount']
16
+ @duplicate_line_count = summary['duplicateLineCount']
17
+ @duplicate_block_count = summary['duplicateBlockCount']
18
+
19
+ @total_significant_line_count = summary['totalSignificantLineCount']
20
+ @total_raw_line_count = summary['totalRawLineCount']
21
+ @total_file_count = summary['totalFileCount']
22
+
23
+ sets = @simian_log_yaml["simian"]["checks"][0]["sets"]
24
+ if sets.nil?
25
+ @sets = []
26
+ else
27
+ @sets = @simian_log_yaml["simian"]["checks"][0]["sets"].collect{ |original_set|
28
+ DontRepeatYourself::SimianResults::DuplicateLinesSet.new(original_set)
29
+ }
30
+ end
31
+ end
32
+
33
+ # ignoreCurlyBraces false boolean Curly braces are ignored.
34
+ #ignoreIdentifiers false boolean Completely ignores all identfiers.
35
+ #ignoreIdentifierCase true boolean Matches identifiers irrespective of case. Eg. MyVariableName and myvariablename would both match.
36
+ #ignoreStrings false boolean MyVariable and myvariablewould both match.
37
+ #ignoreStringCase J true boolean "Hello, World" and "HELLO, WORLD" would both match.
38
+ #ignoreNumbers false boolean int x = 1; and int x = 576; would both match.
39
+ #ignoreCharacters false boolean 'A' and 'Z'would both match.
40
+ #ignoreCharacterCase true boolean 'A' and 'a'would both match.
41
+ #ignoreLiterals false boolean 'A', "one" and 27.8would all match.
42
+ #balanceParentheses false boolean Ensures that expressions inside parenthesis that are split across multiple physical lines are considered as one.
43
+ #balanceCurlyBraces false boolean Ensures that expressions inside curly braces that are split across multiple physical lines are considered as one.
44
+ #balanceSquareBrackets false boolean Ensures that expressions inside square brackets that are split across multiple physical lines are considered as one. Defaults to false.
45
+
46
+ def sentence_found_x_duplicate_lines_in_y_blocks_in_z_files
47
+ "Found #{self.duplicate_line_count} duplicate lines in #{self.duplicate_block_count} blocks in #{self.duplicate_file_count} files"
48
+ end
49
+
50
+ def sentence_processed_a_total_of_x_significant_lines_in_y_files
51
+ "Processed a total of #{self.total_significant_line_count} significant (#{self.total_raw_line_count} raw) lines in #{self.total_file_count} files"
52
+ end
53
+
54
+ class DuplicateLinesSet
55
+ attr_reader :number_of_duplicate_lines, :blocks
56
+ def initialize(original_set)
57
+ @number_of_duplicate_lines = original_set["lineCount"]
58
+ @blocks = original_set["blocks"].collect{ |original_block|
59
+ DontRepeatYourself::SimianResults::DuplicateLinesBlock.new(original_block)
60
+ }
61
+ end
62
+
63
+ def sentence_found_x_duplicate_lines_in_the_following_files
64
+ "Found #{@number_of_duplicate_lines} duplicate lines in the following files:"
65
+ end
66
+
67
+ end
68
+
69
+ class DuplicateLinesBlock
70
+ attr_reader :line_number_of_first_duplicate_line,
71
+ :line_number_of_last_duplicate_line,
72
+ :file_path
73
+
74
+ def initialize(original_block)
75
+ @line_number_of_first_duplicate_line = original_block["startLineNumber"]
76
+ @line_number_of_last_duplicate_line = original_block["endLineNumber"]
77
+ @file_path = original_block["sourceFile"]
78
+ end
79
+
80
+ def sentence_between_lines_x_and_y_in_filepath
81
+ "Between lines #{self.line_number_of_first_duplicate_line} and #{self.line_number_of_last_duplicate_line} in #{self.file_path}"
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def summary
88
+ @simian_log_yaml["simian"]["checks"][0]["summary"]
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,135 @@
1
+ require 'yaml'
2
+
3
+ module DontRepeatYourself
4
+
5
+ # See Simian doc in http://www.redhillconsulting.com.au/products/simian/installation.html#cli
6
+ class SimianRunner
7
+
8
+ attr_reader :basedir,
9
+ :patterns_of_directories_to_search_for_duplicate_lines,
10
+ :formatter_option,
11
+ :simian_jar_path,
12
+ :executable,
13
+ :simian_log_file,
14
+ :threshold
15
+
16
+ DEFAULT_THRESHOLD = 3
17
+ SIMIAN_LOGFILE_NAME = "simian_log.yaml"
18
+
19
+ def initialize()
20
+ @threshold = DEFAULT_THRESHOLD
21
+ @patterns_of_directories_to_search_for_duplicate_lines, @patterns_of_directories_to_exclude_for_duplicate_lines = [], []
22
+ @formatter_option = "-formatter=yaml"
23
+
24
+ # extension is .txt because the selenium_on_rails project had a problem with jar files that could be
25
+ # downloaded from the rubygems repository
26
+ # In order to prevent this kind of problem, I decided to use another suffix as they did ...
27
+ @simian_jar_path = File.join(File.dirname(__FILE__), '..', 'jars', 'simian-2.2.24.jar.xyz')
28
+
29
+ @executable = "java -jar #{@simian_jar_path}".freeze
30
+
31
+ end
32
+
33
+ def threshold=(threshold)
34
+ raise ArgumentError.new("Error: Threshold can't be less that 2") if threshold < 2
35
+ @threshold = threshold
36
+ end
37
+
38
+ def basedir=(basedir)
39
+ # TODO Check if Validatable has some generic code for this. I keep copy-pasting here !!!
40
+ raise ArgumentError.new(basedir << " does not exist") if !File.directory?(basedir)
41
+ @basedir = basedir
42
+ @simian_log_file = @basedir + "/" + DontRepeatYourself::SimianRunner::SIMIAN_LOGFILE_NAME
43
+ end
44
+
45
+ def add_ruby_directory_to_search_for_duplicate_lines(path)
46
+ valid_directory_path(path)
47
+ # TODO if the next line usefull ?
48
+ @patterns_of_directories_to_search_for_duplicate_lines << (path + "/*.rb")
49
+ @patterns_of_directories_to_search_for_duplicate_lines << (path + "/**/*.rb")
50
+ end
51
+
52
+ def add_html_directory_to_search_for_duplicate_lines(path)
53
+ valid_directory_path(path)
54
+ @patterns_of_directories_to_search_for_duplicate_lines << (path + "/**/*.*html")
55
+ end
56
+
57
+ def ignore_file(path)
58
+ valid_file_path(path)
59
+ @patterns_of_directories_to_exclude_for_duplicate_lines << "/" + path
60
+ end
61
+
62
+ def ignore_directory(path)
63
+ valid_directory_path(path)
64
+ @patterns_of_directories_to_exclude_for_duplicate_lines << add_ruby_pattern(path)
65
+ end
66
+
67
+ def run
68
+ run_java
69
+ results_yaml = YAML.load(simian_output_with_header_removed)
70
+ delete_simian_log_file
71
+ results_yaml
72
+ end
73
+
74
+ private
75
+
76
+ def add_ruby_pattern(path)
77
+ "#{path}/**/*.rb"
78
+ end
79
+
80
+ def parameter_threshold
81
+ "-threshold=#{@threshold}"
82
+ end
83
+
84
+ def parameter_includes
85
+ generate_commandline_parameter("includes", @patterns_of_directories_to_search_for_duplicate_lines)
86
+ end
87
+
88
+ def parameter_excludes
89
+ generate_commandline_parameter("excludes", @patterns_of_directories_to_exclude_for_duplicate_lines)
90
+ end
91
+
92
+ def generate_commandline_parameter(paramater_name, parameter_list)
93
+ parameter_list.map { |pattern|
94
+ "-#{paramater_name}=#{File.join(@basedir, pattern)}"
95
+ } * ' '
96
+ end
97
+
98
+ def command_line
99
+ "#{@executable} #{parameter_threshold} #{@formatter_option} #{parameter_includes} #{parameter_excludes} > #{@simian_log_file}"
100
+ end
101
+
102
+ # TODO Add return code processing
103
+ def run_java
104
+ system(command_line)
105
+ end
106
+
107
+ def simian_output_with_header_removed
108
+ # Remove the Simian text header
109
+ log = IO.read(@simian_log_file)
110
+ log[/simian:.*/m] # m <=> Multiline mode
111
+ end
112
+
113
+ def delete_simian_log_file
114
+ File.delete @simian_log_file if File.file?(@simian_log_file)
115
+ end
116
+
117
+ def valid_directory_path(path)
118
+ absolute_path = File.join(@basedir, "/" + path)
119
+ if !File.directory?(absolute_path)
120
+ raise ArgumentError.new(absolute_path << " does not exist, path should be relative to #{@basedir} and not start neither end with '/' ")
121
+ end
122
+ absolute_path
123
+ end
124
+
125
+ def valid_file_path(path)
126
+ absolute_path = File.join(@basedir, "/" + path)
127
+ if !File.file?(absolute_path)
128
+ raise ArgumentError.new(absolute_path << " file does not exist, path should be relative to #{@basedir} and not start neither end with '/' ")
129
+ end
130
+ absolute_path
131
+ end
132
+
133
+ end
134
+
135
+ end
@@ -0,0 +1,31 @@
1
+ module DontRepeatYourself
2
+
3
+ class SnippetExtractor #:nodoc:
4
+ class NullConverter; def convert(code, pre); code; end; end #:nodoc:
5
+ begin
6
+ require 'rubygems'
7
+ require 'syntax/convertors/html'
8
+ @@converter = Syntax::Convertors::HTML.for_syntax "ruby"
9
+ rescue LoadError
10
+ @@converter = NullConverter.new
11
+ end
12
+
13
+ def plain_source_code(starts, ends, file_path)
14
+ if File.file?(file_path)
15
+ lines = File.open(file_path).read.split("\n")
16
+ lines[starts-1..ends-1].join("\n")
17
+ else
18
+ "# Couldn't get snippet for #{file_path}"
19
+ end
20
+ end
21
+
22
+ def snippet(starts, ends, file_path)
23
+ raw_code = plain_source_code(starts, ends, file_path)
24
+ highlighted = @@converter.convert(raw_code, false)
25
+ highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
26
+ highlighted
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__) + '/reporter'
2
+ require 'spec'
3
+
4
+ # TODO separate the file!
5
+ require 'test/unit/assertions'
6
+
7
+ module DontRepeatYourself
8
+
9
+ module UnitTestingHelpers
10
+
11
+ module RubyProjectHelpers
12
+ def ruby_project(project_path)
13
+ DontRepeatYourself::RubyProjectReporter.new(project_path)
14
+ end
15
+ def ruby_code_in_rails_plugin(plugin_name)
16
+ DontRepeatYourself::RailsPluginProjectReporter.new(plugin_name)
17
+ end
18
+
19
+ def rails_application(rails_root = RAILS_ROOT)
20
+ DontRepeatYourself::RailsProjectReporter.new(rails_root)
21
+ end
22
+ end
23
+
24
+ module TestUnitExtension
25
+ include DontRepeatYourself::UnitTestingHelpers::RubyProjectHelpers
26
+
27
+ def assert_dry(project)
28
+ assert(project.is_dry?, project.failure_message)
29
+ end
30
+ end
31
+
32
+ module RSpecMatchers
33
+
34
+ include DontRepeatYourself::UnitTestingHelpers::RubyProjectHelpers
35
+
36
+ class BeDRYMatcher
37
+
38
+ def matches?(project)
39
+ @project = project
40
+ @project.is_dry?
41
+ end
42
+
43
+ # TODO Do we really need this? It does not make a lot of sense
44
+ def negative_failure_message
45
+ "expected #{@project.name} to have more than #{@project.maximum_number_of_duplicate_lines_i_want_in_my_project} duplicate lines :\n but found the following:\n "
46
+ end
47
+
48
+ def description
49
+ "follow the 'Don't Repeat Yourself' principle " << @project.description
50
+ end
51
+
52
+ def failure_message
53
+ @project.failure_message
54
+ end
55
+
56
+ end
57
+
58
+ # Custom expectation matcher
59
+ def follow_the_dry_principle
60
+ DontRepeatYourself::UnitTestingHelpers::RSpecMatchers::BeDRYMatcher.new
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+
67
+ # Automatically includes assert_dry
68
+ module Test
69
+ module Unit
70
+ class TestCase #:nodoc:
71
+ include DontRepeatYourself::UnitTestingHelpers::RubyProjectHelpers
72
+ include DontRepeatYourself::UnitTestingHelpers::TestUnitExtension
73
+ end
74
+ end
75
+ end
76
+
@@ -0,0 +1,9 @@
1
+ module DontRepeatYourself #:nodoc:
2
+ class VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 3
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: angelic-dont_repeat_yourself
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Jean-Michel Garnier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-19 00:00:00 -08:00
13
+ default_executable: dry-report
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: syntax
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.2
34
+ version:
35
+ description: Generate duplicate lines report
36
+ email:
37
+ - jm AT 21croissants dot com
38
+ executables:
39
+ - dry-report
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README.txt
44
+ files:
45
+ - SIMIAN-LICENSE
46
+ - MIT-LICENSE
47
+ - README.txt
48
+ - Rakefile
49
+ - bin/dry-report
50
+ - lib/assets
51
+ - lib/assets/dry.css
52
+ - lib/assets/dry.js
53
+ - lib/dont_repeat_yourself
54
+ - lib/dont_repeat_yourself/formatter.rb
55
+ - lib/dont_repeat_yourself/reporter.rb
56
+ - lib/dont_repeat_yourself/simian_results.rb
57
+ - lib/dont_repeat_yourself/simian_runner.rb
58
+ - lib/dont_repeat_yourself/snippet_extractor.rb
59
+ - lib/dont_repeat_yourself/unit_testing_helpers.rb
60
+ - lib/dont_repeat_yourself/cli.rb
61
+ - lib/dont_repeat_yourself/version.rb
62
+ - lib/jars
63
+ - lib/jars/simian-2.2.24.jar.xyz
64
+ - lib/dont_repeat_yourself.rb
65
+ has_rdoc: true
66
+ homepage: http://github.com/garnierjm/dont_repeat_yourself
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --main
70
+ - README.txt
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ requirements: []
86
+
87
+ rubyforge_project: dont_repeat_yourself
88
+ rubygems_version: 1.2.0
89
+ signing_key:
90
+ specification_version: 2
91
+ summary: Generate duplicate lines report
92
+ test_files: []
93
+