ruby-lint 0.0.1a
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.
- 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
|