ruby-lint 0.9.1 → 1.0.0.pre.preview1

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CONTRIBUTING.md +9 -11
  4. data/Gemfile +6 -3
  5. data/MANIFEST +7 -1
  6. data/README.md +15 -24
  7. data/benchmark/bootup.rb +13 -0
  8. data/checksum/ruby-lint-0.9.1.gem.sha512 +1 -0
  9. data/doc/code_analysis.md +20 -0
  10. data/doc/css/common.css +1 -0
  11. data/doc/images/flow.png +0 -0
  12. data/lib/ruby-lint.rb +1 -3
  13. data/lib/ruby-lint/analysis/base.rb +12 -2
  14. data/lib/ruby-lint/analysis/undefined_variables.rb +3 -2
  15. data/lib/ruby-lint/analysis/unused_variables.rb +3 -2
  16. data/lib/ruby-lint/ast/node.rb +1 -1
  17. data/lib/ruby-lint/benchmark/average.rb +115 -0
  18. data/lib/ruby-lint/cli/analyze.rb +19 -1
  19. data/lib/ruby-lint/constant_loader.rb +1 -3
  20. data/lib/ruby-lint/constant_path.rb +112 -0
  21. data/lib/ruby-lint/definition_builder/base.rb +0 -2
  22. data/lib/ruby-lint/definition_builder/ruby_module.rb +1 -1
  23. data/lib/ruby-lint/definitions/core/array.rb +304 -73
  24. data/lib/ruby-lint/definitions/core/fixnum.rb +575 -19
  25. data/lib/ruby-lint/definitions/core/float.rb +2650 -95
  26. data/lib/ruby-lint/definitions/core/hash.rb +926 -85
  27. data/lib/ruby-lint/definitions/core/ruby_copyright.rb +3 -1
  28. data/lib/ruby-lint/definitions/core/ruby_description.rb +3 -1
  29. data/lib/ruby-lint/definitions/core/ruby_engine.rb +3 -1
  30. data/lib/ruby-lint/definitions/core/ruby_patchlevel.rb +3 -1
  31. data/lib/ruby-lint/definitions/core/ruby_platform.rb +3 -1
  32. data/lib/ruby-lint/definitions/core/ruby_release_date.rb +3 -1
  33. data/lib/ruby-lint/definitions/core/ruby_version.rb +3 -1
  34. data/lib/ruby-lint/definitions/core/string.rb +847 -134
  35. data/lib/ruby-lint/definitions/core/string_io.rb +370 -25
  36. data/lib/ruby-lint/definitions/core/struct.rb +611 -146
  37. data/lib/ruby-lint/file_loader.rb +5 -6
  38. data/lib/ruby-lint/file_scanner.rb +44 -11
  39. data/lib/ruby-lint/inspector.rb +12 -2
  40. data/lib/ruby-lint/runner.rb +6 -2
  41. data/lib/ruby-lint/version.rb +1 -1
  42. data/lib/ruby-lint/virtual_machine.rb +19 -5
  43. data/ruby-lint.gemspec +1 -3
  44. data/spec/ruby-lint/analysis/argument_amount_spec.rb +5 -5
  45. data/spec/ruby-lint/analysis/base_spec.rb +4 -0
  46. data/spec/ruby-lint/analysis/shadowing_variables_spec.rb +4 -4
  47. data/spec/ruby-lint/analysis/undefined_methods_spec.rb +6 -6
  48. data/spec/ruby-lint/analysis/undefined_variables_spec.rb +5 -5
  49. data/spec/ruby-lint/analysis/unused_variables_spec.rb +12 -12
  50. data/spec/ruby-lint/cli/analyze_spec.rb +10 -0
  51. data/spec/ruby-lint/constant_path.rb +63 -0
  52. data/spec/ruby-lint/definition/ruby_object_spec.rb +2 -2
  53. data/spec/ruby-lint/report_spec.rb +2 -2
  54. data/spec/ruby-lint/runner_spec.rb +17 -0
  55. data/spec/ruby-lint/virtual_machine/assignments/assignment_arguments_spec.rb +14 -0
  56. data/spec/ruby-lint/virtual_machine/assignments/constants_spec.rb +23 -0
  57. data/spec/ruby-lint/virtual_machine/location_spec.rb +4 -4
  58. data/spec/ruby-lint/virtual_machine/method_call_tracking_spec.rb +4 -4
  59. data/task/build.rake +2 -7
  60. data/task/doc.rake +3 -1
  61. data/task/generate.rake +3 -0
  62. metadata +20 -36
  63. checksums.yaml.gz.asc +0 -17
  64. data.tar.gz.asc +0 -17
  65. data/lib/ruby-lint/helper/constant_paths.rb +0 -50
  66. metadata.gz.asc +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 939921e3e0b0860d74f54f2ddaefe22108be2b0c
4
- data.tar.gz: 940ca31c4345114f3a7cd864acda3fd4d64a8b34
3
+ metadata.gz: 24fc661993350c12969fcf32d98a88a3539a1f71
4
+ data.tar.gz: 4ae7f0b1589d953aef60c3d027aa38e6de6dc17b
5
5
  SHA512:
6
- metadata.gz: 8d4341b6d719cc515b69fa16eb2e76d5515a40655e63fbf88f60a6bd9bdcdfec59184ac62a151ce6e42920ff039206fc359a47e131ac2ab76de2a8a83f69fd2b
7
- data.tar.gz: 3e3dfa77c8a7a0cad2c7ab54839f870c124cc1685c661de5881b6980678a498cc5a339772bb4800c93d6da85ebef7f71f57a522cb987b1e02c876cc6976aa1a7
6
+ metadata.gz: 053bc9734416821c16a410eef0b8b49bfcf16b84b29fb08f82a3d1ac73dfc41b5b144bc0695e681cc556a127e2af010abb501d4e45dfd5cbf3baecfcc725999d
7
+ data.tar.gz: 61a61089a5f673979e0fc696e2e36e0595dfaea73c29b7aaba677ce9134ac0c6f77375d9ea3c67bfe5074eb21a7e2b5843fc957d507ba3834daa532fc01afbdf
@@ -4,7 +4,7 @@ script: rake travis
4
4
  rvm:
5
5
  - 1.9.3
6
6
  - 2.0.0
7
- - rbx-19mode
7
+ - rbx-2.2.1
8
8
  - jruby-19mode
9
9
 
10
10
  notifications:
@@ -4,22 +4,20 @@ Those that wish to contribute to ruby-lint are more than welcome. However, to
4
4
  make the lifes of both me and others easier there are a few things one should
5
5
  keep in mind.
6
6
 
7
- The basic set of guidelines is described at
8
- <http://yorickpeterse.com/articles/contributing-to-my-code/>. The article looks
9
- a bit daunting but most of what this article describes is already common
10
- practise in Ruby land. In short:
7
+ The gist of this is as following:
11
8
 
12
- * write tests
13
- * write documentation using YARD (at the very least arguments and return values
9
+ * Write tests
10
+ * Write documentation using YARD (at the very least arguments and return values
14
11
  should be documented)
15
- * wrap lines at 79 characters per line
12
+ * Wrap lines at 79 characters per line
16
13
  * Git commits should have a <= 50 character summary, optionally followed by a
17
14
  blank line and a more in depth description of 79 characters per line.
18
15
 
19
- These are the most important bits, the rest is all described in the above
20
- article. If you have any questions or whatsoever don't hesitate to ask and
21
- don't worry about making mistakes (e.g. missing some documentation), I'll help
22
- wherever I can and won't slap you with a large trout.
16
+ More information can be found here:
17
+ <http://yorickpeterse.com/articles/contributing-to-my-code/>. If you have any
18
+ questions or whatsoever don't hesitate to ask and don't worry about making
19
+ mistakes (e.g. missing some documentation), I'll help wherever I can and won't
20
+ slap you with a large trout.
23
21
 
24
22
  ## Legal
25
23
 
data/Gemfile CHANGED
@@ -2,6 +2,9 @@ source 'https://rubygems.org/'
2
2
 
3
3
  gemspec
4
4
 
5
- gem 'racc', :platforms => :rbx
6
-
7
- gem 'ruby-prof', :platforms => :mri
5
+ group :testing do
6
+ gem 'rubysl', :platform => :rbx
7
+ gem 'rubinius-developer_tools', :platform => :rbx
8
+ gem 'racc', :platform => :rbx
9
+ gem 'ruby-prof', :platform => :mri
10
+ end
data/MANIFEST CHANGED
@@ -8,6 +8,7 @@ LICENSE
8
8
  MANIFEST
9
9
  README.md
10
10
  Rakefile
11
+ benchmark/bootup.rb
11
12
  benchmark/virtual_machine.rb
12
13
  bin/ruby-lint
13
14
  checksum/.gitkeep
@@ -15,6 +16,7 @@ checksum/ruby-lint-0.0.3.gem.sha512
15
16
  checksum/ruby-lint-0.0.4.gem.sha512
16
17
  checksum/ruby-lint-0.0.5.gem.sha512
17
18
  checksum/ruby-lint-0.9.0.gem.sha512
19
+ checksum/ruby-lint-0.9.1.gem.sha512
18
20
  doc/.gitkeep
19
21
  doc/DCO.md
20
22
  doc/architecture.md
@@ -36,6 +38,7 @@ lib/ruby-lint/analysis/undefined_variables.rb
36
38
  lib/ruby-lint/analysis/unused_variables.rb
37
39
  lib/ruby-lint/ast/builder.rb
38
40
  lib/ruby-lint/ast/node.rb
41
+ lib/ruby-lint/benchmark/average.rb
39
42
  lib/ruby-lint/cache.rb
40
43
  lib/ruby-lint/cache_entry.rb
41
44
  lib/ruby-lint/cli.rb
@@ -45,6 +48,7 @@ lib/ruby-lint/cli/base.rb
45
48
  lib/ruby-lint/cli/plot.rb
46
49
  lib/ruby-lint/configuration.rb
47
50
  lib/ruby-lint/constant_loader.rb
51
+ lib/ruby-lint/constant_path.rb
48
52
  lib/ruby-lint/default_names.rb
49
53
  lib/ruby-lint/definition/constant_proxy.rb
50
54
  lib/ruby-lint/definition/ruby_method.rb
@@ -218,7 +222,6 @@ lib/ruby-lint/file_loader.rb
218
222
  lib/ruby-lint/file_scanner.rb
219
223
  lib/ruby-lint/generated_constant.rb
220
224
  lib/ruby-lint/global_scope.rb
221
- lib/ruby-lint/helper/constant_paths.rb
222
225
  lib/ruby-lint/inspector.rb
223
226
  lib/ruby-lint/iterator.rb
224
227
  lib/ruby-lint/method_call/alias.rb
@@ -278,6 +281,7 @@ spec/ruby-lint/cache_spec.rb
278
281
  spec/ruby-lint/cli/analyze_spec.rb
279
282
  spec/ruby-lint/cli/ast_spec.rb
280
283
  spec/ruby-lint/configuration_spec.rb
284
+ spec/ruby-lint/constant_path.rb
281
285
  spec/ruby-lint/definition/constant_proxy_spec.rb
282
286
  spec/ruby-lint/definition/dsl_spec.rb
283
287
  spec/ruby-lint/definition/ruby_method_spec.rb
@@ -305,6 +309,8 @@ spec/ruby-lint/report_spec.rb
305
309
  spec/ruby-lint/runner_spec.rb
306
310
  spec/ruby-lint/virtual_machine/alias_spec.rb
307
311
  spec/ruby-lint/virtual_machine/assignments/arrays_spec.rb
312
+ spec/ruby-lint/virtual_machine/assignments/assignment_arguments_spec.rb
313
+ spec/ruby-lint/virtual_machine/assignments/constants_spec.rb
308
314
  spec/ruby-lint/virtual_machine/assignments/hashes_spec.rb
309
315
  spec/ruby-lint/virtual_machine/assignments/optional_spec.rb
310
316
  spec/ruby-lint/virtual_machine/assignments/return_values_spec.rb
data/README.md CHANGED
@@ -16,9 +16,9 @@ instead of focusing on semantics (e.g. the amount of characters per line).
16
16
 
17
17
  The following Ruby implementations/versions are officially supported:
18
18
 
19
- * Ruby 1.9.3 or 2.0 and newer
20
- * Rubinius head running in 1.9 mode
21
- * Jruby head running in 1.9 mode
19
+ * MRI 1.9.3 and newer
20
+ * Rubinius 2.0 and newer
21
+ * Jruby 1.7 and newer
22
22
 
23
23
  Ruby implementations running a 1.8 based version of Ruby are not supported.
24
24
 
@@ -40,25 +40,18 @@ This builds a new version of the Gem and saves it in the pkg/ directory.
40
40
 
41
41
  ## Security
42
42
 
43
- To ensure that people can't tamper with the ruby-lint Gem once it's being
44
- distributed as a `.gem` file the Gem is signed using GNUPG (using the
45
- [rubygems-openpgp][rubygems-openpgp] Gem). If you have this Gem installed it's
46
- recommended that you install ruby-lint as following:
43
+ As a basic form of security ruby-lint provides a set of SHA512 checksums for
44
+ every Gem release. These checksums can be found in the `checksum/` directory.
45
+ Although these checksums do not prevent malicious users from tampering with a
46
+ built Gem they can be used for basic integrity verification purposes.
47
47
 
48
- gem install ruby-lint --verify --trust
48
+ The checksum of a file can be checked using the `sha512sum` command. For
49
+ example:
49
50
 
50
- Unless you have my GPG public key and have marked it as trusted this process
51
- will fail. For signing Gems I use the public key **3649F444** registered to
52
- "Yorick Peterse" using Email address <yorickpeterse@gmail.com>.
51
+ $ sha512sum pkg/ruby-lint-0.9.1.gem
52
+ 10a51f27c455e5743fff7fefe29512cff20116b805bec148e09d4bade1727e3beab7f7f9ee97b020d290773edcb7bd1685858ccad0bbd1a35cc0282c00c760c6 pkg/ruby-lint-0.9.1.gem
53
53
 
54
- You can add this key by running the following command:
55
-
56
- gpg --recv-keys 3649F444
57
-
58
- In case you don't use GPG but still want some form of verification you can use
59
- the checksums that are located in the "checksum" directory. These checksums are
60
- SHA512 checksums of entire Gem files and can be verified using the `sha512sum`
61
- command.
54
+ In the past Gems were also signed using PGP, this is no longer the case.
62
55
 
63
56
  ## Usage
64
57
 
@@ -91,9 +84,9 @@ Given the following code:
91
84
  Analysing this file using ruby-lint (with the default settings) would result in
92
85
  the following output:
93
86
 
94
- test.rb: error: line 7, column 21: undefined instance variable @name
95
- test.rb: warning: line 12, column 0: unused local variable greeting
96
- test.rb: error: line 14, column 0: wrong number of arguments (expected 0 but got 1)
87
+ test.rb: error: line 7, column 22: undefined instance variable @name
88
+ test.rb: warning: line 12, column 1: unused local variable greeting
89
+ test.rb: error: line 14, column 1: wrong number of arguments (expected 0 but got 1)
97
90
 
98
91
  ## Documentation
99
92
 
@@ -107,5 +100,3 @@ the following output:
107
100
  All source code in this repository is licensed under the MIT license unless
108
101
  specified otherwise. A copy of this license can be found in the file "LICENSE"
109
102
  in the root directory of this repository.
110
-
111
- [rubygems-openpgp]: https://github.com/grant-olson/rubygems-openpgp
@@ -0,0 +1,13 @@
1
+ # This benchmark script measures the amount of time it takes to boot up the CLI
2
+ # and analyze the VM with caching disabled.
3
+
4
+ require_relative '../lib/ruby-lint/benchmark/average'
5
+
6
+ script = File.expand_path('../../bin/ruby-lint', __FILE__)
7
+ command = "#{script} analyze --disable-cache lib/ruby-lint/virtual_machine.rb"
8
+
9
+ RubyLint::Benchmark::Average.measure do
10
+ output = `#{command}`
11
+
12
+ raise "Command failed: #{output}" unless $?.success?
13
+ end
@@ -0,0 +1 @@
1
+ 10a51f27c455e5743fff7fefe29512cff20116b805bec148e09d4bade1727e3beab7f7f9ee97b020d290773edcb7bd1685858ccad0bbd1a35cc0282c00c760c6
@@ -8,6 +8,8 @@ example, the callback method `on_string` is used before a `(string)` node is
8
8
  processed. For more low level details see the API documentation of
9
9
  {RubyLint::Iterator} and {RubyLint::Analysis::Base} (which extends the former).
10
10
 
11
+ ## Example
12
+
11
13
  For this guide we'll be creating an analysis class that checks for local
12
14
  variables written in camelCase. Whenever it finds these variables a warning
13
15
  will be added informing the developer that he/she should use snake\_case
@@ -88,3 +90,21 @@ The full code of this exercise looks like the following:
88
90
  iterator.iterate(ast)
89
91
 
90
92
  puts presenter.present(report)
93
+
94
+ ## Conditional Analysis
95
+
96
+ In some cases you want to use a certain analysis class but only enable it if a
97
+ certain condition is met. In order to do so a analysis class should define a
98
+ class method called `analyze?` that returns a boolean that indicates if the
99
+ class should be used or not. The basic signature of this method can be seen at
100
+ {RubyLint::Analysis::Base.analyze?}.
101
+
102
+ For example, if you only want to analyze RSpec files:
103
+
104
+ class RSpecExample < RubyLint::Analysis::Base
105
+ def self.analyze?(ast, vm)
106
+ return ast.file =~ /_spec\.rb$/
107
+ end
108
+ end
109
+
110
+ By default all analysis classes are enabled.
@@ -17,6 +17,7 @@ pre.code
17
17
  {
18
18
  font-size: 13px;
19
19
  line-height: 1.4;
20
+ overflow: auto;
20
21
  }
21
22
 
22
23
  /**
Binary file
@@ -1,7 +1,5 @@
1
- gem 'racc', '>= 1.4.10'
2
1
  gem 'parser', '>= 2.0.0'
3
2
 
4
- require 'racc'
5
3
  require 'parser'
6
4
 
7
5
  # Try to load the latest parser and fall back to 2.0. This should only occur on
@@ -37,7 +35,7 @@ require_relative 'ruby-lint/node_hash'
37
35
  require_relative 'ruby-lint/cache'
38
36
  require_relative 'ruby-lint/cache_entry'
39
37
 
40
- require_relative 'ruby-lint/helper/constant_paths'
38
+ require_relative 'ruby-lint/constant_path'
41
39
 
42
40
  require_relative 'ruby-lint/definition_builder/base'
43
41
  require_relative 'ruby-lint/definition_builder/ruby_module'
@@ -11,8 +11,6 @@ module RubyLint
11
11
  # @return [RubyLint::VirtualMachine]
12
12
  #
13
13
  class Base < Iterator
14
- include Helper::ConstantPaths
15
-
16
14
  attr_reader :report, :vm
17
15
 
18
16
  ##
@@ -23,6 +21,18 @@ module RubyLint
23
21
  #
24
22
  SCOPES = [:root, :block, :class, :def, :module, :sclass]
25
23
 
24
+ ##
25
+ # Returns a boolean that indicates if the analysis class should be used
26
+ # or not.
27
+ #
28
+ # @param [RubyLint::AST::Node] ast
29
+ # @param [RubyLint::VirtualMachine] vm
30
+ # @return [TrueClass|FalseClass]
31
+ #
32
+ def self.analyze?(ast, vm)
33
+ return true
34
+ end
35
+
26
36
  ##
27
37
  # Called after a new instance of this class is created.
28
38
  #
@@ -36,8 +36,9 @@ module RubyLint
36
36
  # @param [RubyLint::AST::Node] node
37
37
  #
38
38
  def on_const(node)
39
- variable = resolve_constant_path(node)
40
- name = constant_segments(node).join('::')
39
+ path = ConstantPath.new(node)
40
+ variable = path.resolve(current_scope)
41
+ name = path.to_s
41
42
 
42
43
  error("undefined constant #{name}", node) unless variable
43
44
  end
@@ -49,8 +49,9 @@ module RubyLint
49
49
  # @param [RubyLint::AST::Node] node
50
50
  #
51
51
  def on_casgn(node)
52
- variable = resolve_constant_path(node)
53
- name = constant_segments(node).join('::')
52
+ path = ConstantPath.new(node)
53
+ variable = path.resolve(current_scope)
54
+ name = path.to_s
54
55
 
55
56
  if variable and !variable.used?
56
57
  warning("unused constant #{name}", node)
@@ -18,7 +18,7 @@ module RubyLint
18
18
  # @return [Numeric]
19
19
  #
20
20
  def column
21
- return location.expression.column if location
21
+ return location.expression.column + 1 if location
22
22
  end
23
23
 
24
24
  ##
@@ -0,0 +1,115 @@
1
+ require 'benchmark'
2
+
3
+ module RubyLint
4
+ module Benchmark
5
+ ##
6
+ # Benchmark class for measuring min/max/avg timings of a block of Ruby
7
+ # code.
8
+ #
9
+ # @!attribute [r] iterations
10
+ # @return [Numeric]
11
+ #
12
+ # @!attribute [r] precision
13
+ # @return [Numeric]
14
+ #
15
+ class Average
16
+ attr_reader :iterations, :precision
17
+
18
+ ##
19
+ # @return [Array]
20
+ #
21
+ AVERAGE_COLUMNS = %w(Iterations Minimum Maximum Average)
22
+
23
+ ##
24
+ # @return [String]
25
+ #
26
+ SEPARATOR = ' | '
27
+
28
+ ##
29
+ # Shorthand method for easily measuring a block.
30
+ #
31
+ # @see #measure
32
+ #
33
+ def self.measure(*args, &block)
34
+ new(*args).measure(&block)
35
+ end
36
+
37
+ ##
38
+ # @param [Numeric] iterations The amount of times to call the block.
39
+ # @param [Numeric] precision The amount of decimals to display.
40
+ #
41
+ def initialize(iterations = 100, precision = 6)
42
+ @iterations = iterations
43
+ @precision = precision
44
+ end
45
+
46
+ ##
47
+ # Runs the given block N times and displays the minimum, maximum and average
48
+ # execution times in secnds.
49
+ #
50
+ def measure
51
+ timings = []
52
+
53
+ iterations.times do
54
+ timings << ::Benchmark.measure { yield }.real
55
+ end
56
+
57
+ avg = timings.inject(:+) / iterations
58
+
59
+ show(timings.min, timings.max, avg)
60
+ end
61
+
62
+ ##
63
+ # @param [Numeric] min
64
+ # @param [Numeric] max
65
+ # @param [Numeric] avg
66
+ #
67
+ def show(min, max, avg)
68
+ values = [iterations.to_s] + format_values([min, max, avg])
69
+ padding = padding_length(AVERAGE_COLUMNS + values)
70
+
71
+ show_header(padding)
72
+
73
+ puts values.map { |val| val.ljust(padding) }.join(SEPARATOR)
74
+ end
75
+
76
+ private
77
+
78
+ ##
79
+ # Display the header.
80
+ #
81
+ # @param [Numeric] padding
82
+ #
83
+ def show_header(padding)
84
+ header = format_header(padding)
85
+
86
+ puts header
87
+ puts header.gsub('|', '+').gsub(/[^+]/, '-')
88
+ end
89
+
90
+ ##
91
+ # @param [Array] values
92
+ # @return [Array<String>]
93
+ #
94
+ def format_values(values)
95
+ return values.map { |val| "#{val.round(precision)} sec" }
96
+ end
97
+
98
+ ##
99
+ # @param [Numeric] padding
100
+ # @return [String]
101
+ #
102
+ def format_header(padding)
103
+ return AVERAGE_COLUMNS.map { |val| val.ljust(padding) }.join(SEPARATOR)
104
+ end
105
+
106
+ ##
107
+ # @param [Array] values
108
+ # @return [Numeric]
109
+ #
110
+ def padding_length(values)
111
+ return values.sort_by { |value| value.length }.last.length
112
+ end
113
+ end # Average
114
+ end # Benchmark
115
+ end # RubyLint
@@ -49,6 +49,10 @@ Examples:
49
49
  You can also specify multiple files:
50
50
 
51
51
  $ ruby-lint analyze first_file.rb second_file.rb
52
+
53
+ Run analysis on an entire directory:
54
+
55
+ $ ruby-lint analyze lib/
52
56
  EOF
53
57
 
54
58
  separator RubyLint::CLI::OPTIONS_HEADER
@@ -78,14 +82,28 @@ Examples:
78
82
 
79
83
  if File.file?(file)
80
84
  existing << file
85
+
86
+ elsif File.directory?(file)
87
+ existing = existing | glob_files(file)
88
+
81
89
  else
82
- abort "The file #{file} does not exist"
90
+ abort "The file/directory #{file} does not exist"
83
91
  end
84
92
  end
85
93
 
86
94
  return existing
87
95
  end
88
96
 
97
+ ##
98
+ # Returns a list of Ruby files in the given directory. This list includes
99
+ # deeply nested files.
100
+ #
101
+ # @return [Array]
102
+ #
103
+ def glob_files(directory)
104
+ return Dir.glob(File.join(directory, '**/*.rb'))
105
+ end
106
+
89
107
  ##
90
108
  # @return [Hash]
91
109
  #