class GarlandRails::Base
Public Class Methods
any?(belongs_to = nil)
click to toggle source
# File lib/garland_rails/utils.rb, line 55 def self.any?(belongs_to = nil) self.thread(belongs_to).any? end
belongs_to(name, scope = nil, options = {}, &extension)
click to toggle source
everything seems to work without `polymorphic: true`, but we will set it just for accordance to docs
Calls superclass method
# File lib/garland_rails/base.rb, line 24 def self.belongs_to(name, scope = nil, options = {}, &extension) if scope.class == Hash options = scope scope = nil end options = options.merge(foreign_key: "belongs_to_id") super(name, scope, options, &extension) end
continuous?(belongs_to = nil)
click to toggle source
# File lib/garland_rails/utils.rb, line 30 def self.continuous?(belongs_to = nil) tail = self.tail(belongs_to) head = self.head(belongs_to) return false unless tail && head current_bulb = tail current_hash = tail.safe_eval_entity items_counted = 1 while current_bulb.next_id do items_counted += 1 current_bulb = current_bulb.next if current_bulb.entity_type == DIFF current_hash = HashDiffSym.patch!(current_hash, current_bulb.safe_eval_entity) else break end end items_counted == self.thread(belongs_to).size && current_hash == head.safe_eval_entity end
head(belongs_to = nil)
click to toggle source
# File lib/garland_rails/utils.rb, line 19 def self.head(belongs_to = nil) self.thread(belongs_to).find_by(next_id: nil) end
init(hash, belongs_to = nil)
click to toggle source
# File lib/garland_rails/push.rb, line 33 def self.init(hash, belongs_to = nil) common_props = self._split_belongs_to(belongs_to) tail_props = common_props.merge(entity: {}.to_s, entity_type: SNAPSHOT) brand_new_tail = self.new(tail_props) diff = HashDiffSym.diff({}, hash) first_diff_props = common_props.merge(entity: diff.to_s, entity_type: DIFF) first_diff = self.new(first_diff_props) head_props = common_props.merge(entity: hash.to_s, entity_type: SNAPSHOT) brand_new_head = self.new(head_props) self.transaction do ActiveRecord::Base.connection.create_savepoint("savepoint_before_init") # first id: tail ({}) # second id: head (latest snapshot) # third+: diffs unless brand_new_tail.save Rails.logger.error("Unable to create new tail with props '#{tail_props}'") ActiveRecord::Base.connection.release_savepoint("savepoint_before_init") return nil end # belongs_to validations were in `brand_new_tail.save` # here and below validations may be skipped as long as we check for continuity later brand_new_head.save(validate: false) first_diff.save(validate: false) brand_new_tail.update_attribute(:next_id, first_diff.id) first_diff.update_attribute(:previous_id, brand_new_tail.id) first_diff.update_attribute(:next_id, brand_new_head.id) brand_new_head.update_attribute(:previous_id, first_diff.id) unless self.continuous?(belongs_to) Rails.logger.error("Initialized garland is not continuous") ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_init") ActiveRecord::Base.connection.release_savepoint("savepoint_before_init") return nil end end first_diff end
insert_diff(hash, belongs_to = nil)
click to toggle source
# File lib/garland_rails/push.rb, line 78 def self.insert_diff(hash, belongs_to = nil) head = self.head(belongs_to) last_diff = self.find_by(id: head.previous_id) common_props = self._split_belongs_to(belongs_to) diff = HashDiffSym.diff(head.safe_eval_entity, hash) return unless diff.any? new_diff_props = common_props.merge( entity: diff.to_s, entity_type: DIFF, previous_id: last_diff.id, next_id: head.id, ) new_diff = self.new(new_diff_props) self.transaction do ActiveRecord::Base.connection.create_savepoint("savepoint_before_insert_diff") # insert_diff should not use skipping valudatuons methods # because we don't want to check for continuity on every push unless new_diff.save Rails.logger.error("Unable to create new_diff with props '#{new_diff_props}'") ActiveRecord::Base.connection.release_savepoint("savepoint_before_init") return nil end last_diff.next_id = new_diff.id unless last_diff.save Rails.logger.error("Unable to save last_diff with 'next_id' = '#{new_diff.id}'") ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff") ActiveRecord::Base.connection.release_savepoint("savepoint_before_init") return nil end head.previous_id = new_diff.id unless head.save Rails.logger.error("Unable to save head with 'previous_id' = '#{new_diff.id}'") ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff") ActiveRecord::Base.connection.release_savepoint("savepoint_before_init") return nil end head.entity = hash.to_s unless head.save Rails.logger.error("Unable to save head with 'entity' = '#{hash}'") ActiveRecord::Base.connection.exec_rollback_to_savepoint("savepoint_before_insert_diff") ActiveRecord::Base.connection.release_savepoint("savepoint_before_init") return nil end end new_diff end
last_diff(belongs_to = nil)
click to toggle source
# File lib/garland_rails/utils.rb, line 23 def self.last_diff(belongs_to = nil) head = self.head(belongs_to) return nil unless head head.previous end
push(args)
click to toggle source
# File lib/garland_rails/push.rb, line 3 def self.push(args) return nil unless args.class == Hash if args[:hash] hash = args[:hash] return nil unless hash.class == Hash belongs_to = args[:belongs_to] if belongs_to belongs_to_params = self._split_belongs_to(belongs_to) belongs_to_id, belongs_to_type = belongs_to_params.values_at(:belongs_to_id, :belongs_to_type) end else hash = args belongs_to = nil belongs_to_id = nil belongs_to_type = nil end head = self.head(belongs_to) if head diff = self.insert_diff(hash, belongs_to) else diff = self.init(hash, belongs_to) end diff end
table_type(record)
click to toggle source
# File lib/garland_rails/utils.rb, line 51 def self.table_type(record) record.class.name end
tail(belongs_to = nil)
click to toggle source
# File lib/garland_rails/utils.rb, line 15 def self.tail(belongs_to = nil) self.thread(belongs_to).find_by(previous_id: nil) end
thread(belongs_to = nil)
click to toggle source
REVIEW: shouldn't thread(), tail() and head() have the same args as push()?
# File lib/garland_rails/utils.rb, line 4 def self.thread(belongs_to = nil) if belongs_to return self.where( "belongs_to_id = ? AND belongs_to_type = ?", belongs_to.id, table_type(belongs_to) ) else return self.where("belongs_to_id is null AND belongs_to_type is null") end end
Private Class Methods
_split_belongs_to(belongs_to)
click to toggle source
# File lib/garland_rails/utils.rb, line 74 def self._split_belongs_to(belongs_to) if belongs_to belongs_to_id = belongs_to.id belongs_to_type = table_type(belongs_to) else belongs_to_id = nil belongs_to_type = nil end { belongs_to_id: belongs_to_id, belongs_to_type: belongs_to_type } end
Public Instance Methods
==(other)
click to toggle source
Minitest raises “No visible difference in the *#inspect output” error while comparing Garland objects without this.
# File lib/garland_rails/base.rb, line 18 def ==(other) self.attributes == other.attributes end
next()
click to toggle source
# File lib/garland_rails/utils.rb, line 59 def next Garland.find(self.next_id) if self.next_id end
previous()
click to toggle source
# File lib/garland_rails/utils.rb, line 63 def previous Garland.find(self.previous_id) if self.previous_id end
safe_eval_entity()
click to toggle source
# File lib/garland_rails/utils.rb, line 67 def safe_eval_entity return nil unless self.entity =~ /\[.*\]/ || self.entity =~ /{.*}/ eval(self.entity) end
snapshot(patch_direction = :backward)
click to toggle source
# File lib/garland_rails/snapshot.rb, line 3 def snapshot(patch_direction = :backward) case patch_direction when :forward approach_nearest_snapshot_method = :previous back_method = :next patch_method = :patch! when :backward approach_nearest_snapshot_method = :next back_method = :previous patch_method = :unpatch! end if self.entity_type == SNAPSHOT snapshot = self.safe_eval_entity else nearest_snapshot = self while nearest_snapshot.entity_type != SNAPSHOT do nearest_snapshot = nearest_snapshot.send(approach_nearest_snapshot_method) end snapshot = nearest_snapshot.safe_eval_entity current_bulb = nearest_snapshot.send(back_method) loop do break if current_bulb == self HashDiffSym.send(patch_method, snapshot, current_bulb.safe_eval_entity) current_bulb = current_bulb.send(back_method) end # this comes from asymmetry of Garland model # # entity => producing snapshot: # # tail => {} # diff1 => hash1 # diff2 => hash2 # diff3 => hash3 # head => hash3 if patch_direction == :forward HashDiffSym.send(patch_method, snapshot, current_bulb.safe_eval_entity) end end Garland.new( entity_type: SNAPSHOT, entity: snapshot.to_s, created_at: self.created_at, type: self.type, ) end