angelic-dont_repeat_yourself 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+