fasterer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.fasterer.yml +17 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +93 -0
- data/Rakefile +7 -0
- data/bin/fasterer +5 -0
- data/fasterer.gemspec +28 -0
- data/lib/fasterer.rb +7 -0
- data/lib/fasterer/analyzer.rb +99 -0
- data/lib/fasterer/binary_call.rb +4 -0
- data/lib/fasterer/cli.rb +10 -0
- data/lib/fasterer/file_traverser.rb +81 -0
- data/lib/fasterer/method_call.rb +138 -0
- data/lib/fasterer/method_definition.rb +51 -0
- data/lib/fasterer/offense.rb +69 -0
- data/lib/fasterer/offense_collector.rb +17 -0
- data/lib/fasterer/parse_error.rb +10 -0
- data/lib/fasterer/parser.rb +11 -0
- data/lib/fasterer/rescue_call.rb +36 -0
- data/lib/fasterer/scanners/method_call_scanner.rb +134 -0
- data/lib/fasterer/scanners/method_definition_scanner.rb +52 -0
- data/lib/fasterer/scanners/offensive.rb +25 -0
- data/lib/fasterer/scanners/rescue_call_scanner.rb +28 -0
- data/lib/fasterer/token.rb +27 -0
- data/lib/fasterer/version.rb +3 -0
- data/spec/lib/fasterer/analyzer/01_parallel_assignment_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/02_rescue_vs_respond_to_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/03_module_eval_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/04_find_vs_bsearch_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/06_shuffle_first_vs_sample_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/08_for_loop_vs_each_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/09_each_with_index_vs_while_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/10_map_flatten_vs_flat_map_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/11_reverse_each_vs_reverse_each_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/12_select_first_vs_detect_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/13_sort_vs_sort_by_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/14_fetch_with_argument_vs_block_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/15_keys_each_vs_each_key_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/16_hash_merge_bang_vs_hash_brackets_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/18_block_vs_symbol_to_proc_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/19_proc_call_vs_yield_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/24_gsub_vs_tr_spec.rb +12 -0
- data/spec/lib/fasterer/analyzer/98_misc_spec.rb +11 -0
- data/spec/lib/fasterer/analyzer/99_exceptional_files_spec.rb +11 -0
- data/spec/lib/fasterer/method_call_spec.rb +483 -0
- data/spec/lib/fasterer/method_definition_spec.rb +93 -0
- data/spec/lib/fasterer/rescue_call_spec.rb +76 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/support/analyzer/01_parallel_assignment.rb +10 -0
- data/spec/support/analyzer/02_rescue_vs_respond_to.rb +36 -0
- data/spec/support/analyzer/03_module_eval.rb +11 -0
- data/spec/support/analyzer/04_find_vs_bsearch.rb +5 -0
- data/spec/support/analyzer/06_shuffle_first_vs_sample.rb +5 -0
- data/spec/support/analyzer/08_for_loop_vs_each.rb +8 -0
- data/spec/support/analyzer/09_each_with_index_vs_while.rb +3 -0
- data/spec/support/analyzer/10_map_flatten_vs_flat_map.rb +15 -0
- data/spec/support/analyzer/11_reverse_each_vs_reverse_each.rb +9 -0
- data/spec/support/analyzer/12_select_first_vs_detect.rb +7 -0
- data/spec/support/analyzer/13_sort_vs_sort_by.rb +9 -0
- data/spec/support/analyzer/14_fetch_with_argument_vs_block.rb +11 -0
- data/spec/support/analyzer/15_keys_each_vs_each_key.rb +15 -0
- data/spec/support/analyzer/16_hash_merge_bang_vs_hash_brackets.rb +21 -0
- data/spec/support/analyzer/18_block_vs_symbol_to_proc.rb +30 -0
- data/spec/support/analyzer/19_proc_call_vs_yield.rb +24 -0
- data/spec/support/analyzer/24_gsub_vs_tr.rb +14 -0
- data/spec/support/analyzer/98_misc.rb +15 -0
- data/spec/support/analyzer/99_exceptional_files.rb +7 -0
- data/spec/support/binary_call/simple_comparison.rb +0 -0
- data/spec/support/method_call/method_call_on_constant.rb +1 -0
- data/spec/support/method_call/method_call_on_integer.rb +1 -0
- data/spec/support/method_call/method_call_on_method_call.rb +1 -0
- data/spec/support/method_call/method_call_on_string.rb +1 -0
- data/spec/support/method_call/method_call_on_variable.rb +2 -0
- data/spec/support/method_call/method_call_with_a_block.rb +5 -0
- data/spec/support/method_call/method_call_with_a_integer_argument.rb +1 -0
- data/spec/support/method_call/method_call_with_a_regex_argument.rb +1 -0
- data/spec/support/method_call/method_call_with_an_argument_and_a_block.rb +2 -0
- data/spec/support/method_call/method_call_with_an_implicit_receiver.rb +1 -0
- data/spec/support/method_call/method_call_with_an_implicit_receiver_and_no_brackets.rb +1 -0
- data/spec/support/method_call/method_call_with_an_implicit_receiver_and_no_brackets_and_do_end.rb +3 -0
- data/spec/support/method_call/method_call_with_equals.rb +1 -0
- data/spec/support/method_call/method_call_with_one_argument.rb +1 -0
- data/spec/support/method_call/method_call_with_two_arguments.rb +2 -0
- data/spec/support/method_call/method_call_without_brackets.rb +1 -0
- data/spec/support/method_definition/method_with_argument_and_block.rb +3 -0
- data/spec/support/method_definition/method_with_block.rb +3 -0
- data/spec/support/method_definition/method_with_default_argument.rb +3 -0
- data/spec/support/method_definition/method_with_splat_and_block.rb +3 -0
- data/spec/support/method_definition/simple_method.rb +2 -0
- data/spec/support/method_definition/simple_method_omitted_parenthesis.rb +2 -0
- data/spec/support/method_definition/simple_method_with_argument.rb +3 -0
- data/spec/support/rescue_call/plain_rescue.rb +7 -0
- data/spec/support/rescue_call/rescue_with_class.rb +5 -0
- data/spec/support/rescue_call/rescue_with_class_and_variable.rb +5 -0
- data/spec/support/rescue_call/rescue_with_multiple_classes.rb +4 -0
- data/spec/support/rescue_call/rescue_with_multiple_classes_and_variable.rb +5 -0
- data/spec/support/rescue_call/rescue_with_variable.rb +6 -0
- metadata +303 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
module Fasterer
|
2
|
+
class MethodCall
|
3
|
+
attr_reader :element
|
4
|
+
attr_reader :receiver
|
5
|
+
attr_reader :method_name
|
6
|
+
attr_reader :arguments
|
7
|
+
attr_reader :block_body
|
8
|
+
attr_reader :block_argument_names
|
9
|
+
|
10
|
+
alias_method :name, :method_name
|
11
|
+
|
12
|
+
def initialize(element)
|
13
|
+
@element = element
|
14
|
+
set_call_element
|
15
|
+
set_receiver
|
16
|
+
set_method_name
|
17
|
+
set_arguments
|
18
|
+
set_block_presence
|
19
|
+
set_block_body
|
20
|
+
set_block_argument_names
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_block?
|
24
|
+
@block_present || false
|
25
|
+
end
|
26
|
+
|
27
|
+
def receiver_element
|
28
|
+
call_element[1]
|
29
|
+
end
|
30
|
+
|
31
|
+
def arguments_element
|
32
|
+
call_element[3..-1] || []
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
attr_reader :call_element
|
38
|
+
# TODO: explanation
|
39
|
+
def set_call_element
|
40
|
+
@call_element = case element.sexp_type
|
41
|
+
when :call
|
42
|
+
@element
|
43
|
+
when :iter
|
44
|
+
@element[1]
|
45
|
+
else
|
46
|
+
fail '!!!!!!!'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_receiver
|
51
|
+
@receiver = ReceiverFactory.new(receiver_element)
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_method_name
|
55
|
+
@method_name = call_element[2]
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_arguments
|
59
|
+
@arguments = arguments_element.map { |argument| Argument.new(argument) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_block_presence
|
63
|
+
if element.sexp_type == :iter
|
64
|
+
@block_present = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_block_body
|
69
|
+
if has_block?
|
70
|
+
@block_body = element[3]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# TODO: write specs for lambdas and procs
|
75
|
+
def set_block_argument_names
|
76
|
+
@block_argument_names = if has_block? and element[2].is_a?(Sexp) # hack for lambdas
|
77
|
+
element[2].drop(1).map { |argument| argument }
|
78
|
+
end || []
|
79
|
+
end
|
80
|
+
|
81
|
+
def token
|
82
|
+
element[0]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# For now, used for determening if
|
87
|
+
# the receiver is a reference or not.
|
88
|
+
class ReceiverFactory
|
89
|
+
attr_reader :name
|
90
|
+
attr_reader :method
|
91
|
+
|
92
|
+
def self.new(receiver_info)
|
93
|
+
return if receiver_info.nil?
|
94
|
+
token = receiver_info.first
|
95
|
+
|
96
|
+
case token
|
97
|
+
when :lvar
|
98
|
+
return VariableReference.new(receiver_info)
|
99
|
+
when :method_add_arg, :method_add_block
|
100
|
+
case receiver_info[1][0]
|
101
|
+
when :call, :fcall
|
102
|
+
return MethodCall.new(receiver_info[1])
|
103
|
+
else
|
104
|
+
# binding.pry watch out for :method_add_arg
|
105
|
+
# raise 'nije ni metoda'
|
106
|
+
end
|
107
|
+
when :call, :iter
|
108
|
+
return MethodCall.new(receiver_info)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class VariableReference
|
114
|
+
attr_reader :name
|
115
|
+
|
116
|
+
def initialize(reference_info)
|
117
|
+
@reference_info = reference_info
|
118
|
+
@name = reference_info[1]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class Argument
|
123
|
+
|
124
|
+
attr_reader :element
|
125
|
+
|
126
|
+
def initialize(element)
|
127
|
+
@element = element
|
128
|
+
end
|
129
|
+
|
130
|
+
def type
|
131
|
+
@type ||= @element[0]
|
132
|
+
end
|
133
|
+
|
134
|
+
def value
|
135
|
+
@value ||= @element[1]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Fasterer
|
2
|
+
class MethodDefinition
|
3
|
+
attr_reader :element # for testing purposes
|
4
|
+
attr_reader :method_name
|
5
|
+
attr_reader :block_argument_name
|
6
|
+
attr_reader :body
|
7
|
+
|
8
|
+
def initialize(element)
|
9
|
+
@element = element # Ripper element
|
10
|
+
set_method_name
|
11
|
+
set_body
|
12
|
+
|
13
|
+
if arguments_element.any?
|
14
|
+
set_argument_names
|
15
|
+
set_block_argument_name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_block?
|
20
|
+
!!@block_argument_name
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def arguments_element
|
26
|
+
element[2].drop(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_method_name
|
30
|
+
@method_name = @element[1]
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_argument_names
|
34
|
+
# TODO
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_body
|
38
|
+
@body = @element[3..-1]
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_block_argument_name
|
42
|
+
if last_argument.to_s.start_with?('&')
|
43
|
+
@block_argument_name = last_argument.to_s[1..-1].to_sym
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def last_argument
|
48
|
+
arguments_element.last
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Fasterer
|
2
|
+
class Offense
|
3
|
+
attr_reader :offense_name, :line_number
|
4
|
+
|
5
|
+
alias_method :name, :offense_name
|
6
|
+
alias_method :line, :line_number
|
7
|
+
|
8
|
+
def initialize(offense_name, line_number)
|
9
|
+
@offense_name = offense_name
|
10
|
+
@line_number = line_number
|
11
|
+
explanation # Set explanation right away.
|
12
|
+
end
|
13
|
+
|
14
|
+
def explanation
|
15
|
+
@explanation ||= EXPLANATIONS.fetch(offense_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
EXPLANATIONS = {
|
19
|
+
parallel_assignment:
|
20
|
+
'Parallel assignment is slower than sequential assignment',
|
21
|
+
|
22
|
+
rescue_vs_respond_to:
|
23
|
+
'Don\'t rescue NoMethodError, rather check with respond_to?',
|
24
|
+
|
25
|
+
module_eval:
|
26
|
+
'Using module_eval is slower than define_method',
|
27
|
+
|
28
|
+
shuffle_first_vs_sample:
|
29
|
+
'Array#shuffle.first is slower than Array#sample',
|
30
|
+
|
31
|
+
for_loop_vs_each:
|
32
|
+
'For loop is slower than using each',
|
33
|
+
|
34
|
+
each_with_index_vs_while:
|
35
|
+
'Using each_with_index is slower than while loop',
|
36
|
+
|
37
|
+
map_flatten_vs_flat_map:
|
38
|
+
'Array#map.flatten(1) is slower than Array#flat_map',
|
39
|
+
|
40
|
+
reverse_each_vs_reverse_each:
|
41
|
+
'Array#reverese.each is slower than Array#reverse_each',
|
42
|
+
|
43
|
+
select_first_vs_detect:
|
44
|
+
'Array#select.first is slower than Array#detect',
|
45
|
+
|
46
|
+
sort_vs_sort_by:
|
47
|
+
'Enumerable#sort is slower than Enumerable#sort_by',
|
48
|
+
|
49
|
+
fetch_with_argument_vs_block:
|
50
|
+
'Hash#fetch with second argument is slower than Hash#fetch with block',
|
51
|
+
|
52
|
+
keys_each_vs_each_key:
|
53
|
+
'Hash#keys.each is slower than Hash#each_key',
|
54
|
+
|
55
|
+
hash_merge_bang_vs_hash_brackets:
|
56
|
+
'Hash#merge! with one argument is slower than Hash#[]',
|
57
|
+
|
58
|
+
block_vs_symbol_to_proc:
|
59
|
+
'Calling argumentless methods within blocks is slower than using symbol to proc',
|
60
|
+
|
61
|
+
proc_call_vs_yield:
|
62
|
+
'Calling blocks with call is slower than yielding',
|
63
|
+
|
64
|
+
gsub_vs_tr:
|
65
|
+
'Use tr instead of gsub when grepping plain strings'
|
66
|
+
}
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Fasterer
|
4
|
+
class OffenseCollector
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@offenses = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](offense_name)
|
12
|
+
@offenses.select { |offense| offense.name == offense_name }
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@offenses, :push, :any?, :each, :group_by
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fasterer
|
2
|
+
class RescueCall
|
3
|
+
attr_reader :element
|
4
|
+
attr_reader :rescue_classes
|
5
|
+
|
6
|
+
def initialize(element)
|
7
|
+
@element = element
|
8
|
+
@rescue_classes = []
|
9
|
+
set_rescue_classes
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def set_rescue_classes
|
15
|
+
return if element[1].sexp_type != :array
|
16
|
+
|
17
|
+
@rescue_classes = element[1].drop(1).map do |rescue_reference|
|
18
|
+
if rescue_reference.sexp_type == :const
|
19
|
+
rescue_reference[1]
|
20
|
+
end
|
21
|
+
end.compact
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_multiple_rescue_classes
|
25
|
+
@rescue_classes = element[1].drop(1).map do |rescue_reference|
|
26
|
+
rescue_reference.flatten[2]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_single_rescue_class
|
31
|
+
if element[1][0][0] == :var_ref
|
32
|
+
@rescue_classes = Array(element[1][0][1][1])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'fasterer/method_call'
|
2
|
+
require 'fasterer/offense'
|
3
|
+
require 'fasterer/scanners/offensive'
|
4
|
+
|
5
|
+
module Fasterer
|
6
|
+
class MethodCallScanner
|
7
|
+
include Fasterer::Offensive
|
8
|
+
|
9
|
+
attr_reader :element
|
10
|
+
|
11
|
+
def initialize(element)
|
12
|
+
@element = element
|
13
|
+
check_offense
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_call
|
17
|
+
@method_call ||= MethodCall.new(element)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def check_offense
|
23
|
+
case method_call.method_name
|
24
|
+
when :module_eval
|
25
|
+
check_module_eval_offense
|
26
|
+
when :gsub
|
27
|
+
check_gsub_offense
|
28
|
+
when :sort
|
29
|
+
check_sort_offense
|
30
|
+
when :each_with_index
|
31
|
+
check_each_with_index_offense
|
32
|
+
when :first
|
33
|
+
check_first_offense
|
34
|
+
when :each
|
35
|
+
check_each_offense
|
36
|
+
when :flatten
|
37
|
+
check_flatten_offense
|
38
|
+
when :fetch
|
39
|
+
check_fetch_offense
|
40
|
+
when :merge!
|
41
|
+
check_merge_bang_offense
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def check_module_eval_offense
|
46
|
+
add_offense(:module_eval)
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_gsub_offense
|
50
|
+
unless method_call.arguments.first.value.is_a? Regexp
|
51
|
+
add_offense(:gsub_vs_tr)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_sort_offense
|
56
|
+
if method_call.arguments.count > 0 || method_call.has_block?
|
57
|
+
add_offense(:sort_vs_sort_by)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def check_each_with_index_offense
|
62
|
+
add_offense(:each_with_index_vs_while)
|
63
|
+
end
|
64
|
+
|
65
|
+
def check_first_offense
|
66
|
+
return method_call unless method_call.receiver.is_a?(MethodCall)
|
67
|
+
|
68
|
+
case method_call.receiver.name
|
69
|
+
when :shuffle
|
70
|
+
add_offense(:shuffle_first_vs_sample)
|
71
|
+
when :select
|
72
|
+
add_offense(:select_first_vs_detect)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def check_each_offense
|
77
|
+
return method_call unless method_call.receiver.is_a?(MethodCall)
|
78
|
+
|
79
|
+
case method_call.receiver.name
|
80
|
+
when :reverse
|
81
|
+
add_offense(:reverse_each_vs_reverse_each)
|
82
|
+
when :keys
|
83
|
+
add_offense(:keys_each_vs_each_key)
|
84
|
+
end
|
85
|
+
|
86
|
+
check_symbol_to_proc
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_flatten_offense
|
90
|
+
return method_call unless method_call.receiver.is_a?(MethodCall)
|
91
|
+
|
92
|
+
if method_call.receiver.name == :map &&
|
93
|
+
method_call.arguments.count == 1 &&
|
94
|
+
method_call.arguments.first.value == 1
|
95
|
+
|
96
|
+
add_offense(:map_flatten_vs_flat_map)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_fetch_offense
|
101
|
+
if method_call.arguments.count == 2 && !method_call.has_block?
|
102
|
+
add_offense(:fetch_with_argument_vs_block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Need to refactor, fukken complicated conditions.
|
107
|
+
def check_symbol_to_proc
|
108
|
+
return unless method_call.block_argument_names.count == 1
|
109
|
+
return if method_call.block_body.nil?
|
110
|
+
return unless method_call.block_body.sexp_type == :call
|
111
|
+
return if method_call.arguments.count > 0
|
112
|
+
|
113
|
+
body_method_call = MethodCall.new(method_call.block_body)
|
114
|
+
|
115
|
+
return unless body_method_call.arguments.count.zero?
|
116
|
+
return if body_method_call.has_block?
|
117
|
+
return unless body_method_call.receiver.name == method_call.block_argument_names.first
|
118
|
+
|
119
|
+
add_offense(:block_vs_symbol_to_proc)
|
120
|
+
end
|
121
|
+
|
122
|
+
def check_merge_bang_offense
|
123
|
+
return unless method_call.arguments.count == 1
|
124
|
+
|
125
|
+
first_argument = method_call.arguments.first
|
126
|
+
return unless first_argument.type == :hash
|
127
|
+
|
128
|
+
if first_argument.element.drop(1).count == 2 # each key and value is an item by itself.
|
129
|
+
add_offense(:hash_merge_bang_vs_hash_brackets)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|