Class: SimpleMaster::Storage::Table

Inherits:
Object
  • Object
show all
Defined in:
lib/simple_master/storage/table.rb

Direct Known Subclasses

OndemandTable, TestTable

Defined Under Namespace

Classes: AssignmentError, ColumnNotExist

Constant Summary collapse

METADATA_PREFIX =
"__"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass, dataset, loader) ⇒ Table

Returns a new instance of Table.



8
9
10
11
12
13
14
15
# File 'lib/simple_master/storage/table.rb', line 8

def initialize(klass, dataset, loader)
  @klass = klass
  @dataset = dataset
  @loader = loader
  @method_cache = Hash.new { |k, v| k[v] = {}.compare_by_identity }.compare_by_identity

  @sub_tables = nil
end

Instance Attribute Details

#allObject

Returns the value of attribute all.



50
51
52
# File 'lib/simple_master/storage/table.rb', line 50

def all
  @all
end

#applied_diffObject

Returns the value of attribute applied_diff.



55
56
57
# File 'lib/simple_master/storage/table.rb', line 55

def applied_diff
  @applied_diff
end

#class_method_cacheObject

Returns the value of attribute class_method_cache.



53
54
55
# File 'lib/simple_master/storage/table.rb', line 53

def class_method_cache
  @class_method_cache
end

#datasetObject

Returns the value of attribute dataset.



47
48
49
# File 'lib/simple_master/storage/table.rb', line 47

def dataset
  @dataset
end

#digestObject

Returns the value of attribute digest.



56
57
58
# File 'lib/simple_master/storage/table.rb', line 56

def digest
  @digest
end

#grouped_hashObject

Returns the value of attribute grouped_hash.



52
53
54
# File 'lib/simple_master/storage/table.rb', line 52

def grouped_hash
  @grouped_hash
end

#id_hashObject

Returns the value of attribute id_hash.



51
52
53
# File 'lib/simple_master/storage/table.rb', line 51

def id_hash
  @id_hash
end

#klassObject

Returns the value of attribute klass.



46
47
48
# File 'lib/simple_master/storage/table.rb', line 46

def klass
  @klass
end

#loaderObject

Returns the value of attribute loader.



48
49
50
# File 'lib/simple_master/storage/table.rb', line 48

def loader
  @loader
end

#method_cacheObject

Returns the value of attribute method_cache.



54
55
56
# File 'lib/simple_master/storage/table.rb', line 54

def method_cache
  @method_cache
end

#sub_tablesObject

Returns the value of attribute sub_tables.



49
50
51
# File 'lib/simple_master/storage/table.rb', line 49

def sub_tables
  @sub_tables
end

Instance Method Details

#apply_diffObject



130
131
132
133
134
135
136
137
138
139
# File 'lib/simple_master/storage/table.rb', line 130

def apply_diff
  update_id_hash
  if diff.present?
    apply_diff_to_id_hash
    self.all = id_hash.values.freeze
  end
  update_grouped_hash

  self
end

#apply_diff_to_id_hashObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/simple_master/storage/table.rb', line 141

def apply_diff_to_id_hash
  id_hash = self.id_hash.dup
  diff&.each do |key, record_diff|
    next if (key)
    id = key.to_i

    if record_diff.nil?
      id_hash.delete(id)
      next
    end

    original_record = id_hash[id]

    record =
      if original_record.nil?
        new_klass = klass.sti_column && record_diff[klass.sti_column.to_s]&.constantize || klass
        new_klass.new(id: id)
      elsif klass.sti_column && record_diff[klass.sti_column.to_s]
        record_diff[klass.sti_column.to_s].constantize.new(original_record.attributes)
      else
        original_record.create_copy
      end

    record_diff.each do |k, v|
      next if (k)
      # Handle expanded JSON columns by converting keys back to JSON strings before assignment
      if v.is_a?(Hash) || v.is_a?(Array)
        v = v.to_json
      end
      record.send(:"#{k}=", v)
    rescue NoMethodError => _e
      raise ColumnNotExist, "Column #{k} does not exist on #{klass}."
    rescue => e
      raise AssignmentError, "Failed to assign data: #{klass}.#{k} = #{v.inspect}. Message: #{e.message}."
    end

    id_hash[id] = record
  end

  self.applied_diff = diff
  self.id_hash = id_hash.freeze
end

#diffObject



126
127
128
# File 'lib/simple_master/storage/table.rb', line 126

def diff
  dataset.diff[klass.table_name]
end

#duplicate_for(dataset, table_klass = self.class) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/simple_master/storage/table.rb', line 21

def duplicate_for(dataset, table_klass = self.class)
  table = table_klass.new(klass, dataset, loader)

  if table.diff == applied_diff
    table.all = all
    table.applied_diff = applied_diff
    table.digest = digest

    # If already loaded, copy existing id_hash and grouped_hash
    table.id_hash = id_hash if @id_hash
    table.grouped_hash = grouped_hash if @grouped_hash
    table.sub_tables = sub_tables&.transform_values { |sub_table|
      sub_table.duplicate_for(dataset)
    }
  end

  table
end

#freeze_allObject



58
59
60
# File 'lib/simple_master/storage/table.rb', line 58

def freeze_all
  all.each(&:freeze).tap { run_on_sub_tables(:freeze_all) }
end

#load_recordsObject



40
41
42
43
44
# File 'lib/simple_master/storage/table.rb', line 40

def load_records
  @method_cache.clear

  loader.load_records(self)
end

#run_on_sub_tables(method) ⇒ Object



122
123
124
# File 'lib/simple_master/storage/table.rb', line 122

def run_on_sub_tables(method)
  sub_tables&.each_value(&method)
end

#sub_table(klass) ⇒ Object



17
18
19
# File 'lib/simple_master/storage/table.rb', line 17

def sub_table(klass)
  @sub_tables[klass]
end

#tap_instance_methodsObject



95
96
97
98
99
100
# File 'lib/simple_master/storage/table.rb', line 95

def tap_instance_methods
  klass.instance_methods_need_tap&.each do |method_name|
    all.each(&method_name)
  end
  run_on_sub_tables(:tap_instance_methods)
end

#update_class_method_cacheObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/simple_master/storage/table.rb', line 77

def update_class_method_cache
  self.class_method_cache = class_method_cache = {}.compare_by_identity
  # Use instance_eval so a block runs in the declaring class context
  klass.all_class_method_cache_info.each do |args, initializer|
    result = klass.instance_eval(&initializer)
    if args.length == 1
      result = [result]
    end

    args.zip(result).each do |arg, value|
      class_method_cache[arg] = value
    end
  end
  class_method_cache.freeze.tap {
    run_on_sub_tables(:update_class_method_cache)
  }
end

#update_grouped_hashObject



66
67
68
69
70
71
72
73
74
75
# File 'lib/simple_master/storage/table.rb', line 66

def update_grouped_hash
  grouped_hash = {}.compare_by_identity

  klass.group_keys.each do |group_key|
    grouped_hash[group_key] = all.group_by(&group_key).freeze.each_value(&:freeze)
  end
  grouped_hash.freeze

  self.grouped_hash = grouped_hash
end

#update_id_hashObject



62
63
64
# File 'lib/simple_master/storage/table.rb', line 62

def update_id_hash
  self.id_hash = all.index_by(&:id).freeze
end

#update_sub_tablesObject



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/simple_master/storage/table.rb', line 102

def update_sub_tables
  sub_klasses = klass.descendants.reject(&:abstract_class)
  return if sub_klasses.empty?

  @sub_tables = sub_klasses.index_with { |sub_klass| self.class.new(sub_klass, dataset, loader) }

  grouped = all.group_by(&:class)

  sub_klasses.each do |sub_klass|
    sub_sub_klasses = [sub_klass, *sub_klass.descendants].reject(&:abstract_class)

    sub_table = self.sub_table(sub_klass)
    sub_table.all = sub_sub_klasses.flat_map { grouped[_1] || EMPTY_ARRAY }.freeze
    sub_table.digest = digest
    sub_table.applied_diff = applied_diff
    sub_table.update_id_hash
    sub_table.update_grouped_hash
  end
end