ruby-lint 0.0.1a
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rbenv-version +1 -0
- data/.yardopts +10 -0
- data/Gemfile +3 -0
- data/LICENSE +19 -0
- data/MANIFEST +79 -0
- data/README.md +48 -0
- data/Rakefile +14 -0
- data/bin/rlint +6 -0
- data/doc/.gitkeep +0 -0
- data/doc/build/.gitkeep +0 -0
- data/doc/css/.gitkeep +0 -0
- data/doc/css/common.css +68 -0
- data/lib/rlint/analyze/coding_style.rb +407 -0
- data/lib/rlint/analyze/definitions.rb +244 -0
- data/lib/rlint/analyze/method_validation.rb +104 -0
- data/lib/rlint/analyze/shadowing_variables.rb +37 -0
- data/lib/rlint/analyze/undefined_variables.rb +99 -0
- data/lib/rlint/analyze/unused_variables.rb +103 -0
- data/lib/rlint/callback.rb +67 -0
- data/lib/rlint/cli.rb +167 -0
- data/lib/rlint/constant_importer.rb +102 -0
- data/lib/rlint/definition.rb +230 -0
- data/lib/rlint/formatter/text.rb +54 -0
- data/lib/rlint/helper/definition_resolver.rb +143 -0
- data/lib/rlint/helper/scoping.rb +138 -0
- data/lib/rlint/iterator.rb +193 -0
- data/lib/rlint/options.rb +58 -0
- data/lib/rlint/parser.rb +1252 -0
- data/lib/rlint/parser_error.rb +42 -0
- data/lib/rlint/report.rb +98 -0
- data/lib/rlint/token/assignment_token.rb +46 -0
- data/lib/rlint/token/begin_rescue_token.rb +57 -0
- data/lib/rlint/token/block_token.rb +17 -0
- data/lib/rlint/token/case_token.rb +44 -0
- data/lib/rlint/token/class_token.rb +24 -0
- data/lib/rlint/token/method_definition_token.rb +64 -0
- data/lib/rlint/token/method_token.rb +58 -0
- data/lib/rlint/token/parameters_token.rb +99 -0
- data/lib/rlint/token/regexp_token.rb +15 -0
- data/lib/rlint/token/statement_token.rb +69 -0
- data/lib/rlint/token/token.rb +162 -0
- data/lib/rlint/token/variable_token.rb +18 -0
- data/lib/rlint/version.rb +3 -0
- data/lib/rlint.rb +36 -0
- data/ruby-lint.gemspec +23 -0
- data/spec/benchmarks/memory.rb +52 -0
- data/spec/benchmarks/parse_parser.rb +16 -0
- data/spec/helper.rb +4 -0
- data/spec/rlint/analyze/coding_style.rb +224 -0
- data/spec/rlint/analyze/definitions/classes.rb +114 -0
- data/spec/rlint/analyze/definitions/methods.rb +91 -0
- data/spec/rlint/analyze/definitions/modules.rb +207 -0
- data/spec/rlint/analyze/definitions/variables.rb +103 -0
- data/spec/rlint/analyze/method_validation.rb +177 -0
- data/spec/rlint/analyze/shadowing_variables.rb +30 -0
- data/spec/rlint/analyze/undefined_variables.rb +230 -0
- data/spec/rlint/analyze/unused_variables.rb +225 -0
- data/spec/rlint/callback.rb +28 -0
- data/spec/rlint/constant_importer.rb +27 -0
- data/spec/rlint/definition.rb +96 -0
- data/spec/rlint/formatter/text.rb +21 -0
- data/spec/rlint/iterator.rb +452 -0
- data/spec/rlint/parser/arrays.rb +147 -0
- data/spec/rlint/parser/classes.rb +152 -0
- data/spec/rlint/parser/errors.rb +19 -0
- data/spec/rlint/parser/hashes.rb +136 -0
- data/spec/rlint/parser/methods.rb +249 -0
- data/spec/rlint/parser/modules.rb +49 -0
- data/spec/rlint/parser/objects.rb +39 -0
- data/spec/rlint/parser/operators.rb +75 -0
- data/spec/rlint/parser/procs.rb +113 -0
- data/spec/rlint/parser/ranges.rb +49 -0
- data/spec/rlint/parser/regexp.rb +31 -0
- data/spec/rlint/parser/scalars.rb +93 -0
- data/spec/rlint/parser/statements.rb +550 -0
- data/spec/rlint/parser/variables.rb +181 -0
- data/spec/rlint/report.rb +30 -0
- data/task/test.rake +6 -0
- metadata +188 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rlint
|
2
|
+
module Formatter
|
3
|
+
##
|
4
|
+
# {Rlint::Formatter::Text} is a formatter class that formats a report in a
|
5
|
+
# format similar to the one used by Ruby when validating a Ruby file using
|
6
|
+
# the `ruby` executable. An example of this format is the following:
|
7
|
+
#
|
8
|
+
# b.rb: error: line 1, column 1: test error
|
9
|
+
# b.rb: info: line 3, column 1: test info b.rb
|
10
|
+
#
|
11
|
+
# Basic usage of this formatter is as following:
|
12
|
+
#
|
13
|
+
# report = Rlint::Report.new
|
14
|
+
# formatter = Rlint::Formatter::Text.new
|
15
|
+
#
|
16
|
+
# # Add some data to the report.
|
17
|
+
# # ...
|
18
|
+
#
|
19
|
+
# puts formatter.format(report)
|
20
|
+
#
|
21
|
+
class Text
|
22
|
+
##
|
23
|
+
# A short description of the class.
|
24
|
+
#
|
25
|
+
# @return [String]
|
26
|
+
#
|
27
|
+
DESCRIPTION = 'Formats a report in a human readable format.'
|
28
|
+
|
29
|
+
##
|
30
|
+
# Formats the specified report.
|
31
|
+
#
|
32
|
+
# @param [Rlint::Report] report The report to format.
|
33
|
+
# @return [String]
|
34
|
+
#
|
35
|
+
def format(report)
|
36
|
+
lines = []
|
37
|
+
|
38
|
+
report.messages.sort.each do |level, messages|
|
39
|
+
messages.each do |message|
|
40
|
+
lines << '%s: %s: line %s, column %s: %s' % [
|
41
|
+
report.file,
|
42
|
+
level,
|
43
|
+
message[:line],
|
44
|
+
message[:column],
|
45
|
+
message[:message]
|
46
|
+
]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
return lines.join("\n")
|
51
|
+
end
|
52
|
+
end # Text
|
53
|
+
end # Formatter
|
54
|
+
end # Rlint
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Rlint
|
2
|
+
module Helper
|
3
|
+
##
|
4
|
+
# {Rlint::Helper::DefinitionResolver} is a helper module that can be used
|
5
|
+
# to work with scoping information similar to {Rlint::Helper::Scoping}.
|
6
|
+
#
|
7
|
+
# This module depends on {Rlint::Helper::Scoping} and will include it
|
8
|
+
# automatically.
|
9
|
+
#
|
10
|
+
# ## Methods
|
11
|
+
#
|
12
|
+
# This module defines a set of methods that are called before and after a
|
13
|
+
# method, class or module is defined. These methods take care of retrieving
|
14
|
+
# the scope for each definition.
|
15
|
+
#
|
16
|
+
# These methods will also call two special callback methods that make it
|
17
|
+
# easier to run code whenever the scope changes:
|
18
|
+
#
|
19
|
+
# * `on_new_scope`: called when a new scope has been set.
|
20
|
+
# * `after_new_scope`: called when the code has reached the end of the
|
21
|
+
# current scope.
|
22
|
+
#
|
23
|
+
# Using these methods classes including this module don't have to redefine
|
24
|
+
# methods such as `on_class` (unless explicitly needed of course). Both of
|
25
|
+
# these methods are called *after* the current scope has been updated.
|
26
|
+
#
|
27
|
+
module DefinitionResolver
|
28
|
+
include Scoping
|
29
|
+
|
30
|
+
##
|
31
|
+
# Called before processing all the tokens.
|
32
|
+
#
|
33
|
+
def on_start
|
34
|
+
call_method(:on_new_scope)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Called after all the tokens have been processed.
|
39
|
+
#
|
40
|
+
def on_finish
|
41
|
+
call_method(:after_new_scope)
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Sets the scope for the current method definition.
|
46
|
+
#
|
47
|
+
# @param [Rlint::Token::MethodDefinitionToken] token
|
48
|
+
#
|
49
|
+
def on_method_definition(token)
|
50
|
+
@scopes << scope.lookup(
|
51
|
+
token.receiver ? :method : :instance_method,
|
52
|
+
token.name
|
53
|
+
)
|
54
|
+
|
55
|
+
@call_types << :instance_method
|
56
|
+
|
57
|
+
call_method(:on_new_scope)
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Resets the scope back to the one used before the method definition.
|
62
|
+
#
|
63
|
+
# @see Rlint::Helper::DefinitionResolver#on_method_definition
|
64
|
+
#
|
65
|
+
def after_method_definition(token)
|
66
|
+
@scopes.pop
|
67
|
+
@call_types.pop
|
68
|
+
|
69
|
+
call_method(:after_new_scope)
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Sets the scope for the current class.
|
74
|
+
#
|
75
|
+
# @param [Rlint::Token::ClassToken] token
|
76
|
+
#
|
77
|
+
def on_class(token)
|
78
|
+
name = token.name.join('::')
|
79
|
+
|
80
|
+
@scopes << scope.lookup(:constant, name)
|
81
|
+
@namespace << name
|
82
|
+
@call_types << :method
|
83
|
+
|
84
|
+
call_method(:on_new_scope)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Resets the scope back to the one used before the class definition.
|
89
|
+
#
|
90
|
+
# @see Rlint::Helper::DefinitionResolver#on_class
|
91
|
+
#
|
92
|
+
def after_class(token)
|
93
|
+
@scopes.pop
|
94
|
+
@namespace.pop
|
95
|
+
@call_types.pop
|
96
|
+
|
97
|
+
call_method(:after_new_scope)
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Sets the scope for the current module.
|
102
|
+
#
|
103
|
+
# @param [Rlint::Token::Token] token
|
104
|
+
#
|
105
|
+
def on_module(token)
|
106
|
+
name = token.name.join('::')
|
107
|
+
|
108
|
+
@scopes << scope.lookup(:constant, name)
|
109
|
+
@namespace << name
|
110
|
+
@call_types << :method
|
111
|
+
|
112
|
+
call_method(:on_new_scope)
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Resets the scope back to the one used before the module definition.
|
117
|
+
#
|
118
|
+
# @see Rlint::Helper::DefinitionResolver#on_module
|
119
|
+
#
|
120
|
+
def after_module(token)
|
121
|
+
@scopes.pop
|
122
|
+
@namespace.pop
|
123
|
+
@call_types.pop
|
124
|
+
|
125
|
+
call_method(:after_new_scope)
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
##
|
131
|
+
# Calls the specified method if it exists.
|
132
|
+
#
|
133
|
+
# @param [String|Symbol] method The name of the method to call.
|
134
|
+
# @param [Array] args Array of arguments to pass to the method that is
|
135
|
+
# being called.
|
136
|
+
# @return [Mixed]
|
137
|
+
#
|
138
|
+
def call_method(method, *args)
|
139
|
+
return send(method, *args) if respond_to?(method)
|
140
|
+
end
|
141
|
+
end # DefinitionResolver
|
142
|
+
end # Helper
|
143
|
+
end # Rlint
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Rlint
|
2
|
+
module Helper
|
3
|
+
##
|
4
|
+
# {Rlint::Helper::Scoping} is a helper module that can be used to more
|
5
|
+
# easily access scoping related information in subclasses of
|
6
|
+
# {Rlint::Callback}.
|
7
|
+
#
|
8
|
+
# Note that unlike {Rlint::Helper::DefinitionResolver} this method does not
|
9
|
+
# automatically update the `@scopes` array mentioned below, it merely
|
10
|
+
# creates the required variables and provides a few helper methods.
|
11
|
+
#
|
12
|
+
# ## Methods
|
13
|
+
#
|
14
|
+
# This module provides two methods:
|
15
|
+
#
|
16
|
+
# * scope: a method that can be used to retrieve the current
|
17
|
+
# scope/definition.
|
18
|
+
# * resolve_definition: a method that can be used to retrieve the
|
19
|
+
# scope/definition for the last constant in a constant path.
|
20
|
+
#
|
21
|
+
# ## Variables
|
22
|
+
#
|
23
|
+
# The following instance variables are created upon initializing a class
|
24
|
+
# that includes this module:
|
25
|
+
#
|
26
|
+
# * `@scopes`: an array that should be updated with instance of
|
27
|
+
# {Rlint::Definition} based on the current scope.
|
28
|
+
# * `@namespace`: array containing the constant names for the current
|
29
|
+
# namespace.
|
30
|
+
#
|
31
|
+
# The following keys are set in the `@storage` instance variable:
|
32
|
+
#
|
33
|
+
# * `:scope`: an instance of {Rlint::Definition} that will contain the
|
34
|
+
# definition list of the current block of code that's being analyzed.
|
35
|
+
#
|
36
|
+
module Scoping
|
37
|
+
##
|
38
|
+
# @see Rlint::Callback#initialize
|
39
|
+
#
|
40
|
+
def initialize(*args)
|
41
|
+
super
|
42
|
+
|
43
|
+
@scopes = []
|
44
|
+
@namespace = []
|
45
|
+
@call_types = []
|
46
|
+
|
47
|
+
unless @storage[:scope].is_a?(Definition)
|
48
|
+
@storage[:scope] = Definition.new(nil, :lazy => true, :kernel => true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
##
|
55
|
+
# Returns the scope/definition for the last segment in the specified
|
56
|
+
# constant path.
|
57
|
+
#
|
58
|
+
# @param [Array] path The constant path.
|
59
|
+
# @return [Rlint::Definition]
|
60
|
+
#
|
61
|
+
def resolve_definition(path)
|
62
|
+
current = scope
|
63
|
+
|
64
|
+
path.each do |segment|
|
65
|
+
found = current.lookup(:constant, segment)
|
66
|
+
current = found if found
|
67
|
+
end
|
68
|
+
|
69
|
+
return current
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Checks if the specified token's name is a valid constant path.
|
74
|
+
#
|
75
|
+
# @param [Rlint::Token::VariableToken] token The token to validate.
|
76
|
+
# @return [TrueClass|FalseClass]
|
77
|
+
#
|
78
|
+
def valid_constant_path?(token)
|
79
|
+
current = scope
|
80
|
+
|
81
|
+
token.name.each do |segment|
|
82
|
+
found = current.lookup(:constant, segment)
|
83
|
+
|
84
|
+
if found and token.line > found.token.line
|
85
|
+
current = found
|
86
|
+
else
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
return true
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Checks if the specified type and token result in a valid
|
96
|
+
# {Rlint::Definition} instance.
|
97
|
+
#
|
98
|
+
# @param [#to_sym] type The type of data to look up.
|
99
|
+
# @param [Rlint::Token::VariableToken] token The token containing details
|
100
|
+
# about the variable.
|
101
|
+
# @param [Rlint::Definition] scope The scope to use for looking up the
|
102
|
+
# data.
|
103
|
+
# @return [TrueClass|FalseClass]
|
104
|
+
#
|
105
|
+
def definition_exists?(type, token, scope = scope)
|
106
|
+
found = scope.lookup(type, token.name)
|
107
|
+
has_line = found.respond_to?(:token) \
|
108
|
+
&& !found.token.nil? \
|
109
|
+
&& !found.token.line.nil?
|
110
|
+
|
111
|
+
if !found or (has_line and found.token.line > token.line)
|
112
|
+
return false
|
113
|
+
else
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Returns the call type to use for method calls.
|
120
|
+
#
|
121
|
+
# @return [Symbol]
|
122
|
+
#
|
123
|
+
def call_type
|
124
|
+
return !@call_types.empty? ? @call_types[-1] : :instance_method
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# Returns the current scope. This method is primarily used to make the
|
129
|
+
# code in this class a bit more pleasant to read.
|
130
|
+
#
|
131
|
+
# @return [Rlint::Definition]
|
132
|
+
#
|
133
|
+
def scope
|
134
|
+
return !@scopes.empty? ? @scopes[-1] : @storage[:scope]
|
135
|
+
end
|
136
|
+
end # Scoping
|
137
|
+
end # Helper
|
138
|
+
end # Rlint
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Rlint
|
2
|
+
##
|
3
|
+
# {Rlint::Iterator} is a class that can be used to iterate over the AST
|
4
|
+
# generated by {Rlint::Parser} and execute callback methods for each
|
5
|
+
# encountered node. Basic usage is as following:
|
6
|
+
#
|
7
|
+
# code = <<-CODE
|
8
|
+
# [10, 20].each do |number|
|
9
|
+
# puts number
|
10
|
+
# end
|
11
|
+
# CODE
|
12
|
+
#
|
13
|
+
# parser = Rlint::Parser.new(code)
|
14
|
+
# tokens = parser.parse
|
15
|
+
# iterator = Rlint::Iterator.new
|
16
|
+
#
|
17
|
+
# iterator.run(tokens)
|
18
|
+
#
|
19
|
+
# This particular example doesn't do anything but iterating over the nodes
|
20
|
+
# due to no callback classes being defined. How to add these classes is
|
21
|
+
# discussed below.
|
22
|
+
#
|
23
|
+
# ## Callback Classes
|
24
|
+
#
|
25
|
+
# Without any custom callback classes the iterator class is fairly useless as
|
26
|
+
# it does nothing but iterate over all the nodes. These classes are defined
|
27
|
+
# as any ordinary class and are added to an interator instance using
|
28
|
+
# {Rlint::Iterator#bind}. At the most basic level each callback class should
|
29
|
+
# have the following structure:
|
30
|
+
#
|
31
|
+
# class MyCallback
|
32
|
+
# attr_reader :options
|
33
|
+
#
|
34
|
+
# def initialize(report = nil, storage = {})
|
35
|
+
# @report = report
|
36
|
+
# @storage = storage
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# The constructor method should take two parameters: the first one is used
|
41
|
+
# for storing a instance of {Rlint::Report} (this parameter should be set to
|
42
|
+
# `nil` by default). The second parameter is a Hash containing custom data
|
43
|
+
# that is shared between callback classes bound to the same {Rlint::Iterator}
|
44
|
+
# instance. This Hash can be used to share, for example, definitions defined
|
45
|
+
# in callback class #1 with callback class #2.
|
46
|
+
#
|
47
|
+
# To make this, as well as adding errors and such to a report easier your own
|
48
|
+
# classes can extend {Rlint::Callback}:
|
49
|
+
#
|
50
|
+
# class MyCallback < Rlint::Callback
|
51
|
+
#
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# To add your class to an iterator instance you'd run the following:
|
55
|
+
#
|
56
|
+
# iterator = Rlint::Iterator.new
|
57
|
+
#
|
58
|
+
# iterator.bind(MyCallback)
|
59
|
+
#
|
60
|
+
# ## Callback Methods
|
61
|
+
#
|
62
|
+
# When iterating over an AST the method {Rlint::Iterator#iterator} calls two
|
63
|
+
# callback methods based on the event name stored in the token (in
|
64
|
+
# {Rlint::Token::Token#event}):
|
65
|
+
#
|
66
|
+
# * `on_EVENT_NAME`
|
67
|
+
# * `after_EVENT_NAME`
|
68
|
+
#
|
69
|
+
# Where `EVENT_NAME` is the name of the event. For example, for strings this
|
70
|
+
# would result in the following methods being called:
|
71
|
+
#
|
72
|
+
# * `on_string`
|
73
|
+
# * `after_string`
|
74
|
+
#
|
75
|
+
# Note that the "after" callback is not executed until all child nodes have
|
76
|
+
# been processed.
|
77
|
+
#
|
78
|
+
# Each method should take a single parameter that contains details about the
|
79
|
+
# token that is currently being processed. Each token is an instance of
|
80
|
+
# {Rlint::Token::Token} or one of its child classes.
|
81
|
+
#
|
82
|
+
# If you wanted to display the values of all strings in your console you'd
|
83
|
+
# write the following class:
|
84
|
+
#
|
85
|
+
# class StringPrinter < Rlint::Callback
|
86
|
+
# def on_string(token)
|
87
|
+
# puts token.value
|
88
|
+
# end
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
class Iterator
|
92
|
+
##
|
93
|
+
# Array containing a set of instance specific callback objects.
|
94
|
+
#
|
95
|
+
# @return [Array]
|
96
|
+
#
|
97
|
+
attr_reader :callbacks
|
98
|
+
|
99
|
+
##
|
100
|
+
# Returns the Hash that is used by callback classes to store arbitrary
|
101
|
+
# data.
|
102
|
+
#
|
103
|
+
# @return [Hash]
|
104
|
+
#
|
105
|
+
attr_reader :storage
|
106
|
+
|
107
|
+
##
|
108
|
+
# Creates a new instance of the iterator class.
|
109
|
+
#
|
110
|
+
# @param [Rlint::Report|NilClass] report The report to use, set to `nil` by
|
111
|
+
# default.
|
112
|
+
#
|
113
|
+
def initialize(report = nil)
|
114
|
+
@callbacks = []
|
115
|
+
@report = report
|
116
|
+
@storage = {}
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Processes the entire AST for each callback class in sequence. For each
|
121
|
+
# callback class the method {Rlint::Iterator#iterate} is called to process
|
122
|
+
# an *entire* AST before moving on to the next callback class.
|
123
|
+
#
|
124
|
+
# @param [#each] nodes An array of nodes to process.
|
125
|
+
#
|
126
|
+
def run(nodes)
|
127
|
+
@callbacks.each do |obj|
|
128
|
+
execute_callback(obj, :on_start)
|
129
|
+
|
130
|
+
iterate(obj, nodes)
|
131
|
+
|
132
|
+
execute_callback(obj, :on_finish)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Processes an AST and calls callbacks methods for a specific callback
|
138
|
+
# object.
|
139
|
+
#
|
140
|
+
# @param [Rlint::Callback] callback_obj The callback object on which to
|
141
|
+
# invoke callback method.
|
142
|
+
# @param [#each] nodes An array (or a different object that responds to
|
143
|
+
# `#each()`) that contains a set of tokens to process.
|
144
|
+
#
|
145
|
+
def iterate(callback_obj, nodes)
|
146
|
+
nodes.each do |node|
|
147
|
+
next unless node.is_a?(Rlint::Token::Token)
|
148
|
+
|
149
|
+
event_name = node.event.to_s
|
150
|
+
callback_name = 'on_' + event_name
|
151
|
+
after_callback = 'after_' + event_name
|
152
|
+
|
153
|
+
execute_callback(callback_obj, callback_name, node)
|
154
|
+
|
155
|
+
node.child_nodes.each do |child_nodes|
|
156
|
+
iterate(callback_obj, child_nodes) if child_nodes.respond_to?(:each)
|
157
|
+
end
|
158
|
+
|
159
|
+
execute_callback(callback_obj, after_callback, node)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Adds the specified class to the list of callback classes for this
|
165
|
+
# instance.
|
166
|
+
#
|
167
|
+
# @example
|
168
|
+
# iterator = Rlint::Iterator.new
|
169
|
+
#
|
170
|
+
# iterator.bind(CustomCallbackClass)
|
171
|
+
#
|
172
|
+
# @param [Class] callback_class The class to add.
|
173
|
+
#
|
174
|
+
def bind(callback_class)
|
175
|
+
@callbacks << callback_class.new(@report, @storage)
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
##
|
181
|
+
# Loops through all the bound callback classes and executes the specified
|
182
|
+
# callback method if it exists.
|
183
|
+
#
|
184
|
+
# @param [Rlint::Callback] obj The object on which to invoke the callback
|
185
|
+
# method.
|
186
|
+
# @param [String|Symbol] name The name of the callback method to execute.
|
187
|
+
# @param [Array] args Arguments to pass to the callback method.
|
188
|
+
#
|
189
|
+
def execute_callback(obj, name, *args)
|
190
|
+
obj.send(name, *args) if obj.respond_to?(name)
|
191
|
+
end
|
192
|
+
end # Iterator
|
193
|
+
end # Rlint
|