Module: RuboCop::Cop::Isucon::Correctors::NPlusOneQueryCorrector::ReplaceMethods

Included in:
RuboCop::Cop::Isucon::Correctors::NPlusOneQueryCorrector
Defined in:
lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb

Overview

replace ast

Instance Method Summary collapse

Instance Method Details

#generate_each_with_objectString

Examples:

response example

each_with_object({}) { |v, hash| hash[v[:id]] = v }

Returns:

  • (String)


53
54
55
56
57
58
59
60
61
62
63
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 53

def generate_each_with_object
  hash_key =
    case xquery_arg.node_parts[2].type
    when :sym
      ":#{where_column_without_quote}"
    when :str
      %("#{where_column_without_quote}")
    end

  "each_with_object({}) { |v, hash| hash[v[#{hash_key}]] = v }"
end

#generate_second_lineString

Examples:

response example

teacher = @users_by_id[course[:teacher_id]]

Returns:

  • (String)


117
118
119
120
121
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 117

def generate_second_line
  object_source = xquery_arg.node_parts[0].source
  symbol_source = xquery_arg.node_parts[2].source
  "#{xquery_lvar.node_parts[0]} = #{instance_var_name}[#{object_source}[#{symbol_source}]]\n"
end

#indent_level(node) ⇒ Integer

Parameters:

  • node (RuboCop::AST::Node)

Returns:

  • (Integer)


106
107
108
109
110
111
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 106

def indent_level(node)
  node.loc.expression.source_line =~ /^(\s+)/
  return 0 unless Regexp.last_match(1)

  Regexp.last_match(1).length
end

#instance_var_nameString

Returns:

  • (String)


91
92
93
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 91

def instance_var_name
  "@#{gda.table_names[0]}_by_#{where_column_without_quote}"
end

#replaceObject



10
11
12
13
14
15
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 10

def replace
  replace_where_condition_in_sql
  replace_xquery_2nd_arg
  replace_chained_method_to_each_with_object
  replace_to_2_lines
end

#replace_chained_method_to_each_with_objectObject

Replace .first -> .each_with_object({}) { |v, hash| hash[v[:id]] = v }



39
40
41
42
43
44
45
46
47
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 39

def replace_chained_method_to_each_with_object
  xquery_chained_method_begin_pos = current_node.loc.end.end_pos + 1
  xquery_chained_method_range =
    Parser::Source::Range.new(current_node.loc.expression.source_buffer,
                              xquery_chained_method_begin_pos,
                              xquery_chained_method_begin_pos + xquery_chained_method.length)

  corrector.replace(xquery_chained_method_range, generate_each_with_object)
end

#replace_to_2_linesObject

Split line

Examples:

Before

teacher = db.xquery("SELECT * FROM `users` WHERE `id` IN (?)", courses.map { |course| course[:teacher_id] }).each_with_object({}) { |v, hash| hash[v[:id]] = v }

After

@users_by_id ||= db.xquery("SELECT * FROM `users` WHERE `id` IN (?)", ...).each_with_object({}) { |v, hash| hash[v[:id]] = v }
teacher = @users_by_id[course[:teacher_id]]


75
76
77
78
79
80
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 75

def replace_to_2_lines
  # rubocop:enable Layout/LineLength

  replace_to_2_lines_for_1st_line
  replace_to_2_lines_for_2nd_line
end

#replace_to_2_lines_for_1st_lineObject



82
83
84
85
86
87
88
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 82

def replace_to_2_lines_for_1st_line
  range =
    Parser::Source::Range.new(current_node.loc.expression.source_buffer,
                              xquery_lvar.loc.expression.begin_pos, current_node.loc.expression.begin_pos)

  corrector.replace(range, "#{instance_var_name} ||= ")
end

#replace_to_2_lines_for_2nd_lineObject



95
96
97
98
99
100
101
102
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 95

def replace_to_2_lines_for_2nd_line
  indent_level = indent_level(current_node)

  pos = xquery_lvar.loc.expression.end_pos + 1
  range = Parser::Source::Range.new(current_node.loc.expression.source_buffer, pos, pos)

  corrector.replace(range, "#{' ' * indent_level}#{generate_second_line}")
end

#replace_where_condition_in_sqlObject

Replace where condition in SQL (e.g. id = ? -> id IN (?))



18
19
20
21
22
23
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 18

def replace_where_condition_in_sql
  loc = offense_location(type: type, node: current_node, gda_location: where_condition_gda_loc)
  return unless loc

  corrector.replace(loc, "#{where_column} IN (?)")
end

#replace_xquery_2nd_argObject

Replace 2nd arg in db.xquery (e.g. course[:teacher_id] -> courses.map { |course| course[:teacher_id] })



26
27
28
29
30
31
32
33
34
35
36
# File 'lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb', line 26

def replace_xquery_2nd_arg
  object_source = xquery_arg.node_parts[0].source
  symbol_source = xquery_arg.node_parts[2].source

  corrector.replace(
    xquery_arg.loc.expression,
    # e.g.
    # courses.map { |course| course[:teacher_id] }
    "#{parent_receiver.source}.map { |#{object_source}| #{object_source}[#{symbol_source}] }",
  )
end