brakeman-lib 5.0.0 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +46 -0
- data/README.md +10 -1
- data/lib/brakeman.rb +23 -8
- data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
- data/lib/brakeman/checks/check_evaluation.rb +1 -1
- data/lib/brakeman/checks/check_execute.rb +10 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +4 -6
- data/lib/brakeman/checks/check_render.rb +15 -1
- data/lib/brakeman/checks/check_sanitize_methods.rb +2 -1
- data/lib/brakeman/checks/check_sql.rb +58 -8
- data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
- data/lib/brakeman/commandline.rb +1 -1
- data/lib/brakeman/file_parser.rb +45 -15
- data/lib/brakeman/options.rb +7 -2
- data/lib/brakeman/parsers/template_parser.rb +24 -0
- data/lib/brakeman/processors/alias_processor.rb +105 -18
- data/lib/brakeman/processors/base_processor.rb +4 -4
- data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +10 -6
- data/lib/brakeman/processors/lib/rails4_config_processor.rb +2 -1
- data/lib/brakeman/processors/library_processor.rb +9 -0
- data/lib/brakeman/processors/model_processor.rb +31 -0
- data/lib/brakeman/report.rb +4 -1
- data/lib/brakeman/report/ignore/config.rb +4 -4
- data/lib/brakeman/report/ignore/interactive.rb +1 -1
- data/lib/brakeman/report/report_github.rb +31 -0
- data/lib/brakeman/report/report_sarif.rb +21 -2
- data/lib/brakeman/rescanner.rb +1 -1
- data/lib/brakeman/scanner.rb +4 -1
- data/lib/brakeman/tracker.rb +33 -4
- data/lib/brakeman/tracker/collection.rb +57 -7
- data/lib/brakeman/tracker/method_info.rb +70 -0
- data/lib/brakeman/util.rb +34 -18
- data/lib/brakeman/version.rb +1 -1
- data/lib/ruby_parser/bm_sexp.rb +14 -0
- metadata +18 -2
data/lib/brakeman/options.rb
CHANGED
@@ -39,7 +39,7 @@ module Brakeman::Options
|
|
39
39
|
OptionParser.new do |opts|
|
40
40
|
opts.banner = "Usage: brakeman [options] rails/root/path"
|
41
41
|
|
42
|
-
opts.on "-n", "--no-threads", "Run checks sequentially" do
|
42
|
+
opts.on "-n", "--no-threads", "Run checks and file parsing sequentially" do
|
43
43
|
options[:parallel_checks] = false
|
44
44
|
end
|
45
45
|
|
@@ -151,6 +151,11 @@ module Brakeman::Options
|
|
151
151
|
options[:safe_methods].merge methods.map {|e| e.to_sym }
|
152
152
|
end
|
153
153
|
|
154
|
+
opts.on "--sql-safe-methods meth1,meth2,etc", Array, "Do not warn of SQL if the input is wrapped in a safe method" do |methods|
|
155
|
+
options[:sql_safe_methods] ||= Set.new
|
156
|
+
options[:sql_safe_methods].merge methods.map {|e| e.to_sym }
|
157
|
+
end
|
158
|
+
|
154
159
|
opts.on "--url-safe-methods method1,method2,etc", Array, "Do not warn of XSS if the link_to href parameter is wrapped in a safe method" do |methods|
|
155
160
|
options[:url_safe_methods] ||= Set.new
|
156
161
|
options[:url_safe_methods].merge methods.map {|e| e.to_sym }
|
@@ -233,7 +238,7 @@ module Brakeman::Options
|
|
233
238
|
|
234
239
|
opts.on "-f",
|
235
240
|
"--format TYPE",
|
236
|
-
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar],
|
241
|
+
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar, :github],
|
237
242
|
"Specify output formats. Default is text" do |type|
|
238
243
|
|
239
244
|
type = "s" if type == :text
|
@@ -9,6 +9,7 @@ module Brakeman
|
|
9
9
|
def initialize tracker, file_parser
|
10
10
|
@tracker = tracker
|
11
11
|
@file_parser = file_parser
|
12
|
+
@slim_smart = nil # Load slim/smart ?
|
12
13
|
end
|
13
14
|
|
14
15
|
def parse_template path, text
|
@@ -88,6 +89,14 @@ module Brakeman
|
|
88
89
|
|
89
90
|
def parse_slim path, text
|
90
91
|
Brakeman.load_brakeman_dependency 'slim'
|
92
|
+
|
93
|
+
if @slim_smart.nil? and load_slim_smart?
|
94
|
+
@slim_smart = true
|
95
|
+
Brakeman.load_brakeman_dependency 'slim/smart'
|
96
|
+
else
|
97
|
+
@slim_smart = false
|
98
|
+
end
|
99
|
+
|
91
100
|
require_relative 'slim_embedded'
|
92
101
|
|
93
102
|
Slim::Template.new(path,
|
@@ -95,6 +104,21 @@ module Brakeman
|
|
95
104
|
:generator => Temple::Generators::RailsOutputBuffer) { text }.precompiled_template
|
96
105
|
end
|
97
106
|
|
107
|
+
def load_slim_smart?
|
108
|
+
return !@slim_smart unless @slim_smart.nil?
|
109
|
+
|
110
|
+
# Terrible hack to find
|
111
|
+
# gem "slim", "~> 3.0.1", require: ["slim", "slim/smart"]
|
112
|
+
if tracker.app_tree.exists? 'Gemfile'
|
113
|
+
gemfile_contents = tracker.app_tree.file_path('Gemfile').read
|
114
|
+
if gemfile_contents.include? 'slim/smart'
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
false
|
120
|
+
end
|
121
|
+
|
98
122
|
def self.parse_inline_erb tracker, text
|
99
123
|
fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
|
100
124
|
tp = self.new(tracker, fp)
|
@@ -183,6 +183,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
183
183
|
return exp
|
184
184
|
end
|
185
185
|
|
186
|
+
# If x(*[1,2,3]) change to x(1,2,3)
|
187
|
+
# if that's the only argument
|
188
|
+
if splat_array? exp.first_arg and exp.second_arg.nil?
|
189
|
+
exp.arglist = exp.first_arg[1].sexp_body
|
190
|
+
end
|
191
|
+
|
186
192
|
target = exp.target
|
187
193
|
method = exp.method
|
188
194
|
first_arg = exp.first_arg
|
@@ -195,11 +201,20 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
195
201
|
res = process_or_simple_operation(exp)
|
196
202
|
return res if res
|
197
203
|
elsif target == ARRAY_CONST and method == :new
|
198
|
-
return Sexp.new(:array, *exp.args)
|
204
|
+
return Sexp.new(:array, *exp.args).line(exp.line)
|
199
205
|
elsif target == HASH_CONST and method == :new and first_arg.nil? and !node_type?(@exp_context.last, :iter)
|
200
|
-
return Sexp.new(:hash)
|
206
|
+
return Sexp.new(:hash).line(exp.line)
|
201
207
|
elsif exp == RAILS_TEST or exp == RAILS_DEV
|
202
|
-
return Sexp.new(:false)
|
208
|
+
return Sexp.new(:false).line(exp.line)
|
209
|
+
end
|
210
|
+
|
211
|
+
# For the simplest case of `Foo.thing`
|
212
|
+
if node_type? target, :const and first_arg.nil?
|
213
|
+
if tracker and (klass = tracker.find_class(class_name(target.value)))
|
214
|
+
if return_value = klass.get_simple_method_return_value(:class, method)
|
215
|
+
return return_value.deep_clone(exp.line)
|
216
|
+
end
|
217
|
+
end
|
203
218
|
end
|
204
219
|
|
205
220
|
#See if it is possible to simplify some basic cases
|
@@ -214,13 +229,28 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
214
229
|
exp = math_op(:+, target, first_arg, exp)
|
215
230
|
end
|
216
231
|
when :-, :*, :/
|
217
|
-
|
232
|
+
if method == :* and array? target
|
233
|
+
if string? first_arg
|
234
|
+
exp = process_array_join(target, first_arg)
|
235
|
+
end
|
236
|
+
else
|
237
|
+
exp = math_op(method, target, first_arg, exp)
|
238
|
+
end
|
218
239
|
when :[]
|
219
240
|
if array? target
|
220
241
|
exp = process_array_access(target, exp.args, exp)
|
221
242
|
elsif hash? target
|
222
243
|
exp = process_hash_access(target, first_arg, exp)
|
223
244
|
end
|
245
|
+
when :fetch
|
246
|
+
if array? target
|
247
|
+
# Not dealing with default value
|
248
|
+
# so just pass in first argument, but process_array_access expects
|
249
|
+
# an array of arguments.
|
250
|
+
exp = process_array_access(target, [first_arg], exp)
|
251
|
+
elsif hash? target
|
252
|
+
exp = process_hash_access(target, first_arg, exp)
|
253
|
+
end
|
224
254
|
when :merge!, :update
|
225
255
|
if hash? target and hash? first_arg
|
226
256
|
target = process_hash_merge! target, first_arg
|
@@ -237,7 +267,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
237
267
|
env[target_var] = target
|
238
268
|
return target
|
239
269
|
elsif string? target and string_interp? first_arg
|
240
|
-
exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2))
|
270
|
+
exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2)).line(exp.line)
|
241
271
|
env[target_var] = exp
|
242
272
|
elsif string? first_arg and string_interp? target
|
243
273
|
if string? target.last
|
@@ -260,6 +290,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
260
290
|
target = find_push_target(target_var)
|
261
291
|
env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor
|
262
292
|
end
|
293
|
+
when :push
|
294
|
+
if array? target
|
295
|
+
target << first_arg
|
296
|
+
env[target_var] = target
|
297
|
+
return target
|
298
|
+
end
|
263
299
|
when :first
|
264
300
|
if array? target and first_arg.nil? and sexp? target[1]
|
265
301
|
exp = target[1]
|
@@ -273,7 +309,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
273
309
|
exp = target
|
274
310
|
end
|
275
311
|
when :join
|
276
|
-
if array? target and
|
312
|
+
if array? target and (string? first_arg or first_arg.nil?)
|
277
313
|
exp = process_array_join(target, first_arg)
|
278
314
|
end
|
279
315
|
when :!
|
@@ -281,6 +317,15 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
281
317
|
if call? target and target.method == :!
|
282
318
|
exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)
|
283
319
|
end
|
320
|
+
when :values
|
321
|
+
# Hash literal
|
322
|
+
if node_type? target, :hash
|
323
|
+
exp = hash_values(target)
|
324
|
+
end
|
325
|
+
when :values_at
|
326
|
+
if node_type? target, :hash
|
327
|
+
exp = hash_values_at target, exp.args
|
328
|
+
end
|
284
329
|
end
|
285
330
|
|
286
331
|
exp
|
@@ -288,7 +333,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
288
333
|
|
289
334
|
# Painful conversion of Array#join into string interpolation
|
290
335
|
def process_array_join array, join_str
|
291
|
-
|
336
|
+
# Empty array
|
337
|
+
if array.length == 1
|
338
|
+
return s(:str, '').line(array.line)
|
339
|
+
end
|
340
|
+
|
341
|
+
result = s().line(array.line)
|
292
342
|
|
293
343
|
join_value = if string? join_str
|
294
344
|
join_str.value
|
@@ -296,8 +346,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
296
346
|
nil
|
297
347
|
end
|
298
348
|
|
299
|
-
array
|
300
|
-
|
349
|
+
if array.length > 2
|
350
|
+
array[1..-2].each do |e|
|
351
|
+
result << join_item(e, join_value)
|
352
|
+
end
|
301
353
|
end
|
302
354
|
|
303
355
|
result << join_item(array.last, nil)
|
@@ -326,24 +378,32 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
326
378
|
result.unshift combined_first
|
327
379
|
|
328
380
|
# Have to fix up strings that follow interpolation
|
329
|
-
result.reduce(s(:dstr)) do |memo, e|
|
381
|
+
string = result.reduce(s(:dstr).line(array.line)) do |memo, e|
|
330
382
|
if string? e and node_type? memo.last, :evstr
|
331
383
|
e.value = "#{join_value}#{e.value}"
|
332
384
|
elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
|
333
|
-
memo << s(:str, join_value)
|
385
|
+
memo << s(:str, join_value).line(e.line)
|
334
386
|
end
|
335
387
|
|
336
388
|
memo << e
|
337
389
|
end
|
390
|
+
|
391
|
+
# Convert (:dstr, "hello world")
|
392
|
+
# to (:str, "hello world")
|
393
|
+
if string.length == 2 and string.last.is_a? String
|
394
|
+
string[0] = :str
|
395
|
+
end
|
396
|
+
|
397
|
+
string
|
338
398
|
end
|
339
399
|
|
340
400
|
def join_item item, join_value
|
341
401
|
if item.is_a? String
|
342
402
|
"#{item}#{join_value}"
|
343
403
|
elsif string? item or symbol? item or number? item
|
344
|
-
s(:str, "#{item.value}#{join_value}")
|
404
|
+
s(:str, "#{item.value}#{join_value}").line(item.line)
|
345
405
|
else
|
346
|
-
s(:evstr, item)
|
406
|
+
s(:evstr, item).line(item.line)
|
347
407
|
end
|
348
408
|
end
|
349
409
|
|
@@ -359,6 +419,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
359
419
|
s(:call, TEMP_FILE_CLASS, :new).line(line)
|
360
420
|
end
|
361
421
|
|
422
|
+
def splat_array? exp
|
423
|
+
node_type? exp, :splat and
|
424
|
+
node_type? exp[1], :array
|
425
|
+
end
|
426
|
+
|
362
427
|
def process_iter exp
|
363
428
|
@exp_context.push exp
|
364
429
|
exp[1] = process exp.block_call
|
@@ -679,7 +744,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
679
744
|
end
|
680
745
|
end
|
681
746
|
else
|
682
|
-
new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value)
|
747
|
+
new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value).line(exp.line)
|
683
748
|
|
684
749
|
env[match] = new_value
|
685
750
|
end
|
@@ -738,6 +803,18 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
738
803
|
exp
|
739
804
|
end
|
740
805
|
|
806
|
+
def hash_or_array_include_all_literals? exp
|
807
|
+
return unless call? exp and sexp? exp.target
|
808
|
+
target = exp.target
|
809
|
+
|
810
|
+
case target.node_type
|
811
|
+
when :hash
|
812
|
+
hash_include_all_literals? exp
|
813
|
+
else
|
814
|
+
array_include_all_literals? exp
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
741
818
|
# Check if exp is a call to Array#include? on an array literal
|
742
819
|
# that contains all literal values. For example:
|
743
820
|
#
|
@@ -756,6 +833,16 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
756
833
|
(all_literals? exp.target or dir_glob? exp.target)
|
757
834
|
end
|
758
835
|
|
836
|
+
# Check if exp is a call to Hash#include? on a hash literal
|
837
|
+
# that contains all literal values. For example:
|
838
|
+
#
|
839
|
+
# {x: 1}.include? x
|
840
|
+
def hash_include_all_literals? exp
|
841
|
+
call? exp and
|
842
|
+
exp.method == :include? and
|
843
|
+
all_literals? exp.target, :hash
|
844
|
+
end
|
845
|
+
|
759
846
|
#Sets @inside_if = true
|
760
847
|
def process_if exp
|
761
848
|
if @ignore_ifs.nil?
|
@@ -796,7 +883,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
796
883
|
scope do
|
797
884
|
@branch_env = env.current
|
798
885
|
branch_index = 2 + i # s(:if, condition, then_branch, else_branch)
|
799
|
-
if i == 0 and
|
886
|
+
if i == 0 and hash_or_array_include_all_literals? condition
|
800
887
|
# If the condition is ["a", "b"].include? x
|
801
888
|
# set x to "a" inside the true branch
|
802
889
|
var = condition.first_arg
|
@@ -804,7 +891,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
804
891
|
env.current[var] = safe_literal(var.line)
|
805
892
|
exp[branch_index] = process_if_branch branch
|
806
893
|
env.current[var] = previous_value
|
807
|
-
elsif i == 1 and
|
894
|
+
elsif i == 1 and hash_or_array_include_all_literals? condition and early_return? branch
|
808
895
|
var = condition.first_arg
|
809
896
|
env.current[var] = safe_literal(var.line)
|
810
897
|
exp[branch_index] = process_if_branch branch
|
@@ -1002,8 +1089,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
1002
1089
|
method_name = call.method
|
1003
1090
|
|
1004
1091
|
#Look for helper methods and see if we can get a return value
|
1005
|
-
if found_method = find_method(method_name, @current_class)
|
1006
|
-
helper = found_method
|
1092
|
+
if found_method = tracker.find_method(method_name, @current_class)
|
1093
|
+
helper = found_method.src
|
1007
1094
|
|
1008
1095
|
if sexp? helper
|
1009
1096
|
value = process_helper_method helper, call.args
|
@@ -8,7 +8,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
8
8
|
include Brakeman::SafeCallHelper
|
9
9
|
include Brakeman::Util
|
10
10
|
|
11
|
-
IGNORE = Sexp.new
|
11
|
+
IGNORE = Sexp.new(:ignore).line(0)
|
12
12
|
|
13
13
|
#Return a new Processor.
|
14
14
|
def initialize tracker
|
@@ -216,7 +216,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
216
216
|
#
|
217
217
|
#And also :layout for inside templates
|
218
218
|
def find_render_type call, in_view = false
|
219
|
-
rest = Sexp.new(:hash)
|
219
|
+
rest = Sexp.new(:hash).line(call.line)
|
220
220
|
type = nil
|
221
221
|
value = nil
|
222
222
|
first_arg = call.first_arg
|
@@ -236,7 +236,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
236
236
|
end
|
237
237
|
elsif first_arg.is_a? Symbol or first_arg.is_a? String
|
238
238
|
type = :action
|
239
|
-
value = Sexp.new(:lit, first_arg.to_sym)
|
239
|
+
value = Sexp.new(:lit, first_arg.to_sym).line(call.line)
|
240
240
|
elsif first_arg.nil?
|
241
241
|
type = :default
|
242
242
|
elsif not hash? first_arg
|
@@ -293,6 +293,6 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
|
|
293
293
|
@tracker.processor.process_template(template_name, ast, type, nil, @current_file)
|
294
294
|
@tracker.processor.process_template_alias(@tracker.templates[template_name])
|
295
295
|
|
296
|
-
return s(:lit, template_name), options
|
296
|
+
return s(:lit, template_name).line(value.line), options
|
297
297
|
end
|
298
298
|
end
|
@@ -51,7 +51,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
|
|
51
51
|
#Need to process the method like it was in a controller in order
|
52
52
|
#to get the renders set
|
53
53
|
processor = Brakeman::ControllerProcessor.new(@tracker, mixin.file)
|
54
|
-
method = mixin.get_method(name)
|
54
|
+
method = mixin.get_method(name).src.deep_clone
|
55
55
|
|
56
56
|
if node_type? method, :defn
|
57
57
|
method = processor.process_defn method
|
@@ -143,16 +143,16 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
|
|
143
143
|
#Basically, adds any instance variable assignments to the environment.
|
144
144
|
#TODO: method arguments?
|
145
145
|
def process_before_filter name
|
146
|
-
filter = find_method name, @current_class
|
146
|
+
filter = tracker.find_method name, @current_class
|
147
147
|
|
148
148
|
if filter.nil?
|
149
149
|
Brakeman.debug "[Notice] Could not find filter #{name}"
|
150
150
|
return
|
151
151
|
end
|
152
152
|
|
153
|
-
method = filter
|
153
|
+
method = filter.src
|
154
154
|
|
155
|
-
if ivars = @tracker.filter_cache[[filter
|
155
|
+
if ivars = @tracker.filter_cache[[filter.owner, name]]
|
156
156
|
ivars.each do |variable, value|
|
157
157
|
env[variable] = value
|
158
158
|
end
|
@@ -162,7 +162,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
|
|
162
162
|
|
163
163
|
ivars = processor.only_ivars(:include_request_vars).all
|
164
164
|
|
165
|
-
@tracker.filter_cache[[filter
|
165
|
+
@tracker.filter_cache[[filter.owner, name]] = ivars
|
166
166
|
|
167
167
|
ivars.each do |variable, value|
|
168
168
|
env[variable] = value
|
@@ -182,7 +182,7 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
|
|
182
182
|
# method as the line number
|
183
183
|
if line.nil? and controller = @tracker.controllers[@current_class]
|
184
184
|
if meth = controller.get_method(@current_method)
|
185
|
-
if line = meth
|
185
|
+
if line = meth.src && meth.src.last && meth.src.last.line
|
186
186
|
line += 1
|
187
187
|
else
|
188
188
|
line = 1
|
@@ -241,41 +241,4 @@ class Brakeman::ControllerAliasProcessor < Brakeman::AliasProcessor
|
|
241
241
|
[]
|
242
242
|
end
|
243
243
|
end
|
244
|
-
|
245
|
-
#Finds a method in the given class or a parent class
|
246
|
-
#
|
247
|
-
#Returns nil if the method could not be found.
|
248
|
-
#
|
249
|
-
#If found, returns hash table with controller name and method sexp.
|
250
|
-
def find_method method_name, klass
|
251
|
-
return nil if sexp? method_name
|
252
|
-
method_name = method_name.to_sym
|
253
|
-
|
254
|
-
if method = @method_cache[method_name]
|
255
|
-
return method
|
256
|
-
end
|
257
|
-
|
258
|
-
controller = @tracker.controllers[klass]
|
259
|
-
controller ||= @tracker.libs[klass]
|
260
|
-
|
261
|
-
if klass and controller
|
262
|
-
method = controller.get_method method_name
|
263
|
-
|
264
|
-
if method.nil?
|
265
|
-
controller.includes.each do |included|
|
266
|
-
method = find_method method_name, included
|
267
|
-
if method
|
268
|
-
@method_cache[method_name] = method
|
269
|
-
return method
|
270
|
-
end
|
271
|
-
end
|
272
|
-
|
273
|
-
@method_cache[method_name] = find_method method_name, controller.parent
|
274
|
-
else
|
275
|
-
@method_cache[method_name] = { :controller => controller.name, :method => method[:src] }
|
276
|
-
end
|
277
|
-
else
|
278
|
-
nil
|
279
|
-
end
|
280
|
-
end
|
281
244
|
end
|