Class: Offroad::ReceivedRecordState

Inherits:
ActiveRecord::Base show all
Defined in:
lib/app/models/offroad/received_record_state.rb

Constant Summary

Constants included from ModelExtensions

ModelExtensions::OFFROAD_GROUP_MODES, ModelExtensions::OFFROAD_VALID_MODES

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ModelExtensions

#acts_as_offroadable, #acts_as_offroadable?, #offroad_global_data?, #offroad_group_base?, #offroad_group_data?, #offroad_model_state, #safe_to_load_from_cargo_stream?

Class Method Details

.redirect_to_local_ids(records, column, model, group) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/app/models/offroad/received_record_state.rb', line 60

def self.redirect_to_local_ids(records, column, model, group)
  column = column.to_sym
  source = self.for_model_and_group_if_apropos(model, group)
  already_allocated = source.all(:conditions => { :remote_record_id => records.map{|r| r[column]} }).index_by(&:remote_record_id)

  remaining = {} # Maps newly discovered remote id to list of records in batch that reference that id
  records.each do |r|
    remote_id = r[column]
    next unless remote_id && remote_id > 0
    # TODO Check for illegal references here (i.e. group model referencing global model)
    if already_allocated.has_key?(remote_id)
      r[column] = already_allocated[remote_id].local_record_id
    else
      # Target doesn't exist yet, we'll figure out what its local id will be later
      if remaining.has_key?(remote_id)
        remaining[remote_id] << r
      else
        remaining[remote_id] = [r]
      end
    end
  end

  return unless remaining.size > 0

  # Reserve access to a block of local ids
  if model.connection.adapter_name.downcase.include?("postgres")
    nextval_cmd = model.connection.select_value("SELECT column_default FROM information_schema.columns WHERE table_name = '#{model.table_name}' AND column_name = '#{model.primary_key}'")
    local_ids = model.connection.select_values("SELECT #{nextval_cmd} FROM generate_series(1,#{remaining.size})").map(&:to_i)
  else
    # Reserve access to a block of local ids by creating temporary records to advance the autoincrement counter
    # TODO I'm pretty sure this is safe because it'll always be used in a transaction, but I should check
    model.import([model.primary_key.to_sym], [[nil]]*remaining.size, :validate => false, :timestamps => false)
    last_id = model.last(:select => model.primary_key, :order => model.primary_key).id
    local_ids = ((last_id+1-remaining.size)..last_id).to_a
    model.delete(local_ids)
  end

  # Create the corresponding RRSes
  model_state_id = model.offroad_model_state.id
  group_state = model.offroad_group_data? && group ? group.group_state : nil
  group_state_id = group_state ? group_state.id : 0
  self.import(
    [:model_state_id, :group_state_id, :local_record_id, :remote_record_id],
    local_ids.zip(remaining.keys).map{|here, there| [model_state_id, group_state_id, here, there]},
    :validate => false, :timestamps => false
  )
  
  # Finally do the redirection to the new ids
  remaining.each_key.each_with_index do |remote_id, i|
    remaining[remote_id].each do |r|
      r[column] = local_ids[i]
    end
  end
end

Instance Method Details

#app_recordObject



56
57
58
# File 'lib/app/models/offroad/received_record_state.rb', line 56

def app_record
  model_state.app_model.find(local_record_id)
end

#validateObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/app/models/offroad/received_record_state.rb', line 11

def validate
  unless model_state
    errors.add_to_base "Cannot find associated model state"
    return
  end
  model = model_state.app_model

  if Offroad::app_offline?
    if model.offroad_group_data?
      errors.add_to_base "Cannot allow received record state for group data in offline app"
    end
  elsif Offroad::app_online?
    if model.offroad_global_data?
      errors.add_to_base "Cannot allow received record state for global records in online app"
    elsif group_state.nil?
      errors.add_to_base "Cannot allow received record state for online group records in online app"
    end
  end

  if model.offroad_global_data? && group_state
    errors.add_to_base "Cannot allow received record state for global records to also be assoc with a group"
  end

  begin
    app_record
  rescue ActiveRecord::RecordNotFound
    errors.add_to_base "Cannot find associated app record"
  end
end