Class: Hayfork::UpdateSql

Inherits:
Object
  • Object
show all
Defined in:
lib/hayfork/update_sql.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(haystack, relation, bindings) ⇒ UpdateSql



8
9
10
11
12
# File 'lib/hayfork/update_sql.rb', line 8

def initialize(haystack, relation, bindings)
  @haystack = haystack
  @relation = relation
  @bindings = bindings
end

Instance Attribute Details

#bindingsObject (readonly)

Returns the value of attribute bindings.



6
7
8
# File 'lib/hayfork/update_sql.rb', line 6

def bindings
  @bindings
end

#haystackObject (readonly)

Returns the value of attribute haystack.



6
7
8
# File 'lib/hayfork/update_sql.rb', line 6

def haystack
  @haystack
end

#relationObject (readonly)

Returns the value of attribute relation.



6
7
8
# File 'lib/hayfork/update_sql.rb', line 6

def relation
  @relation
end

Instance Method Details

#modelObject



26
27
28
# File 'lib/hayfork/update_sql.rb', line 26

def model
  relation.model
end

#to_sqlObject Also known as: to_s



14
15
16
17
18
19
20
21
22
23
# File 'lib/hayfork/update_sql.rb', line 14

def to_sql
  sql = values_to_check_on_update.map { |field| "OLD.#{field} IS DISTINCT FROM NEW.#{field}" }.join(" OR ")

  <<-SQL
IF #{sql} THEN
  #{delete.to_sql.strip}
  #{insert.to_sql.strip}
END IF;
  SQL
end

#values_to_check_on_updateObject



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/hayfork/update_sql.rb', line 30

def values_to_check_on_update
  foreign_keys_by_table_name = {}
  (relation.joins_values + relation.left_outer_joins_values).each do |join_value|
    if join_value.is_a? String
      fail NotImplementedError, "Unhandled literal join: #{join_value.inspect}"
    end

    reflection = reflection_for(join_value)
    table_name = reflection.table_name
    reflection = reflection.through_reflection if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)

    case reflection
    when ActiveRecord::Reflection::BelongsToReflection
      foreign_keys_by_table_name[table_name] = [ reflection.foreign_key.to_s ]
    when ActiveRecord::Reflection::HasManyReflection
      foreign_keys_by_table_name[table_name] = [] # assume identity keys won't change
    when ActiveRecord::Reflection::HasAndBelongsToManyReflection
      foreign_keys_by_table_name[reflection.join_table] = [] # assume identity keys won't change
      foreign_keys_by_table_name[table_name] = [] # assume identity keys won't change
    else
      fail NotImplementedError, "Unhandled reflection: #{reflection.class} (join_value: #{join_value.inspect})"
    end
  end


  values_being_written = bindings.pluck(:raw_value) + predicate_fields
  values_to_check_on_update = Set.new

  values_being_written.each do |value|
    next if value.is_a?(String) # constant
    if value.relation.name == relation.table_name
      values_to_check_on_update << value.name.to_s
    else
      # The value isn't a field of the current record but of a joined record.
      # That record hasn't changed so we don't care about its value; but we
      # do care whether this record's foreign keys have changed (which would
      # cause it to be associated with a different joined record).
      values_to_check_on_update.merge foreign_keys_by_table_name.fetch(value.relation.name)
    end
  end

  values_to_check_on_update - model.readonly_attributes
end