brakeman-lib 5.0.0 → 5.1.0
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.
- 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
|