Class: PaperTrailDiff::PreparedEdgeLoader
- Inherits:
-
Object
- Object
- PaperTrailDiff::PreparedEdgeLoader
- Defined in:
- lib/paper_trail_diff/prepared_edge_loader.rb,
sig/generated/paper_trail_diff/prepared_edge_loader.rbs
Overview
Loads every historical identity that can participate in one association edge.
Constant Summary collapse
- EDGE_LOADERS =
{ belongs_to: :belongs_to_edge, has_one: :child_edge, has_many: :child_edge, has_and_belongs_to_many: :habtm_edge }.freeze
Instance Method Summary collapse
-
#add_identity(groups, model_class, id) ⇒ void
: (Hash[String, Hash[Symbol, untyped]], untyped, untyped) -> void.
-
#belongs_to_edge(owner_class, owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], nil ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], nil].
-
#belongs_to_groups(owner_class, owner_ids, reflection) ⇒ Hash[String, Hash[Symbol, untyped]]
: (untyped, Array, untyped) -> Hash[String, Hash[Symbol, untyped]].
-
#call(owner_class, owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]? ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]?].
-
#child_candidate_scope(version_class) ⇒ VersionAssociationCandidateScope
: (untyped) -> VersionAssociationCandidateScope.
-
#child_edge(owner_class, owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], nil ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], nil].
-
#child_groups(owner_class, owner_ids, reflection) ⇒ Hash[String, Hash[Symbol, untyped]]
: (untyped, Array, untyped) -> Hash[String, Hash[Symbol, untyped]].
-
#group_for(model_class, ids, owners: nil) ⇒ Hash[String, Hash[Symbol, untyped]]
: (untyped, Array, ?owners: Hash[String, Array[untyped]]?) -> Hash[String, Hash[Symbol, untyped]].
-
#group_ids_by_owner(pairs) ⇒ Hash[String, Array[untyped]]
Keyed rather than scanned so that a wide edge stays linear in its width.
-
#habtm_edge(owner_class, _owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]] ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]].
-
#habtm_groups(owner_class, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]] ]
: (untyped, untyped) -> [Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]].
-
#habtm_rows(owner_class, reflection) ⇒ Array[untyped]
: (untyped, untyped) -> Array.
-
#historical_child_ids(owner_class, owner_ids, reflection, foreign_key) ⇒ Hash[String, Array[untyped]]
: (untyped, Array, untyped, String) -> Hash[String, Array[untyped]].
-
#historical_child_relation(owner_class, owner_ids, reflection, foreign_key) ⇒ Object
: (untyped, Array, untyped, String) -> untyped.
-
#initialize(records, root_versions, start_at: root_versions.first&.created_at) ⇒ PreparedEdgeLoader
constructor
: (PreparedRecordIndex, Array, ?start_at: untyped) -> void.
-
#live_child_ids(owner_class, owner_ids, reflection, foreign_key) ⇒ Hash[String, Array[untyped]]
: (untyped, Array, untyped, String) -> Hash[String, Array[untyped]].
- #membership_ids(rows, transaction_id) ⇒ Array[untyped]
-
#merge_owners(left, right) ⇒ Hash[String, Array[untyped]]
: (Hash[String, Array[untyped]], Hash[String, Array[untyped]]) -> Hash[String, Array[untyped]].
-
#parent_types(owner_class) ⇒ Array[String?]
: (untyped) -> Array.
-
#scalar_foreign_key(reflection) ⇒ String?
: (untyped) -> String?.
-
#target_class(reflection, owner) ⇒ Object
: (untyped, untyped) -> untyped.
Constructor Details
#initialize(records, root_versions, start_at: root_versions.first&.created_at) ⇒ PreparedEdgeLoader
: (PreparedRecordIndex, Array, ?start_at: untyped) -> void
16 17 18 19 20 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 16 def initialize(records, root_versions, start_at: root_versions.first&.created_at) @records = records @transaction_ids = root_versions.filter_map(&:transaction_id).uniq.freeze @start_at = start_at end |
Instance Method Details
#add_identity(groups, model_class, id) ⇒ void
This method returns an undefined value.
: (Hash[String, Hash[Symbol, untyped]], untyped, untyped) -> void
152 153 154 155 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 152 def add_identity(groups, model_class, id) group = groups[model_class.name.to_s] ||= { model: model_class, ids: [] } group.fetch(:ids) << id unless group.fetch(:ids).include?(id) end |
#belongs_to_edge(owner_class, owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], nil ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], nil]
35 36 37 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 35 def belongs_to_edge(owner_class, owner_ids, reflection) [belongs_to_groups(owner_class, owner_ids, reflection), nil] end |
#belongs_to_groups(owner_class, owner_ids, reflection) ⇒ Hash[String, Hash[Symbol, untyped]]
: (untyped, Array, untyped) -> Hash[String, Hash[Symbol, untyped]]
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 50 def belongs_to_groups(owner_class, owner_ids, reflection) key = scalar_foreign_key(reflection) return {} unless key grouped = {} #: Hash[String, Hash[Symbol, untyped]] @records.records_for(owner_class, owner_ids).each do |owner| id = owner.public_send(key) model_class = target_class(reflection, owner) add_identity(grouped, model_class, id) if model_class && id end grouped end |
#call(owner_class, owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]? ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]?]
23 24 25 26 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 23 def call(owner_class, owner_ids, reflection) loader = EDGE_LOADERS.fetch(reflection.macro) send(loader, owner_class, owner_ids, reflection) end |
#child_candidate_scope(version_class) ⇒ VersionAssociationCandidateScope
: (untyped) -> VersionAssociationCandidateScope
126 127 128 129 130 131 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 126 def child_candidate_scope(version_class) VersionAssociationCandidateScope.new( version_class, @start_at ) end |
#child_edge(owner_class, owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], nil ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], nil]
40 41 42 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 40 def child_edge(owner_class, owner_ids, reflection) [child_groups(owner_class, owner_ids, reflection), nil] end |
#child_groups(owner_class, owner_ids, reflection) ⇒ Hash[String, Hash[Symbol, untyped]]
: (untyped, Array, untyped) -> Hash[String, Hash[Symbol, untyped]]
64 65 66 67 68 69 70 71 72 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 64 def child_groups(owner_class, owner_ids, reflection) key = scalar_foreign_key(reflection) return {} unless key historical = historical_child_ids(owner_class, owner_ids, reflection, key) live = live_child_ids(owner_class, owner_ids, reflection, key) owners = merge_owners(historical, live) group_for(reflection.klass, owners.values.flatten, owners: owners) end |
#group_for(model_class, ids, owners: nil) ⇒ Hash[String, Hash[Symbol, untyped]]
: (untyped, Array, ?owners: Hash[String, Array[untyped]]?) -> Hash[String, Hash[Symbol, untyped]]
158 159 160 161 162 163 164 165 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 158 def group_for(model_class, ids, owners: nil) unique = ids.compact.uniq return {} if unique.empty? group = { model: model_class, ids: unique } #: Hash[Symbol, untyped] group[:owners] = owners if owners { model_class.name.to_s => group } end |
#group_ids_by_owner(pairs) ⇒ Hash[String, Array[untyped]]
Keyed rather than scanned so that a wide edge stays linear in its width. : (Array[Array[untyped]]) -> Hash[String, Array[untyped]]
169 170 171 172 173 174 175 176 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 169 def group_ids_by_owner(pairs) grouped = {} #: Hash[String, Hash[untyped, true]] pairs.each do |owner_id, child_id| ids = grouped[owner_id.to_s] ||= {} #: Hash[untyped, true] ids[child_id] = true end grouped.transform_values(&:keys) end |
#habtm_edge(owner_class, _owner_ids, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]] ]
: (untyped, Array, untyped) -> [Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]]
45 46 47 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 45 def habtm_edge(owner_class, _owner_ids, reflection) habtm_groups(owner_class, reflection) end |
#habtm_groups(owner_class, reflection) ⇒ [ Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]] ]
: (untyped, untyped) -> [Hash[String, Hash[Symbol, untyped]], Hash[String, Array[untyped]]]
75 76 77 78 79 80 81 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 75 def habtm_groups(owner_class, reflection) rows = habtm_rows(owner_class, reflection) memberships = @transaction_ids.to_h do |transaction_id| [transaction_id.to_s, membership_ids(rows, transaction_id)] end [group_for(reflection.klass, rows.filter_map(&:foreign_key_id)), memberships] end |
#habtm_rows(owner_class, reflection) ⇒ Array[untyped]
: (untyped, untyped) -> Array
84 85 86 87 88 89 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 84 def habtm_rows(owner_class, reflection) owner_class.paper_trail.version_association_class.where( version_id: @transaction_ids, foreign_key_name: reflection.name.to_s ).to_a end |
#historical_child_ids(owner_class, owner_ids, reflection, foreign_key) ⇒ Hash[String, Array[untyped]]
: (untyped, Array, untyped, String) -> Hash[String, Array[untyped]]
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 99 def historical_child_ids( owner_class, owner_ids, reflection, foreign_key ) version_class = owner_class.paper_trail.version_class association_class = owner_class.paper_trail.version_association_class relation = historical_child_relation(owner_class, owner_ids, reflection, foreign_key) pairs = child_candidate_scope(version_class).call(relation).distinct.pluck( association_class.arel_table[:foreign_key_id], version_class.arel_table[:item_id] ) group_ids_by_owner(pairs) end |
#historical_child_relation(owner_class, owner_ids, reflection, foreign_key) ⇒ Object
: (untyped, Array, untyped, String) -> untyped
116 117 118 119 120 121 122 123 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 116 def historical_child_relation(owner_class, owner_ids, reflection, foreign_key) version_class = owner_class.paper_trail.version_class owner_class.paper_trail.version_association_class.joins(:version).where( foreign_key_name: foreign_key, foreign_key_id: owner_ids, foreign_type: parent_types(owner_class) ).where(version_class.table_name => { item_type: reflection.klass.base_class.name }) end |
#live_child_ids(owner_class, owner_ids, reflection, foreign_key) ⇒ Hash[String, Array[untyped]]
: (untyped, Array, untyped, String) -> Hash[String, Array[untyped]]
134 135 136 137 138 139 140 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 134 def live_child_ids(owner_class, owner_ids, reflection, foreign_key) relation = reflection.klass.base_class.unscoped.where(foreign_key => owner_ids) if reflection.[:as] relation = relation.where(reflection.type => parent_types(owner_class)) end group_ids_by_owner(relation.pluck(foreign_key, reflection.klass.primary_key)) end |
#membership_ids(rows, transaction_id) ⇒ Array[untyped]
92 93 94 95 96 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 92 def membership_ids(rows, transaction_id) rows.filter_map do |row| row.foreign_key_id if row.version_id.to_s == transaction_id.to_s end.uniq end |
#merge_owners(left, right) ⇒ Hash[String, Array[untyped]]
: (Hash[String, Array[untyped]], Hash[String, Array[untyped]]) -> Hash[String, Array[untyped]]
179 180 181 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 179 def merge_owners(left, right) left.merge(right) { |_owner, first, second| first | second } end |
#parent_types(owner_class) ⇒ Array[String?]
: (untyped) -> Array
190 191 192 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 190 def parent_types(owner_class) [nil, '', owner_class.name.to_s, owner_class.base_class.name.to_s].uniq end |
#scalar_foreign_key(reflection) ⇒ String?
: (untyped) -> String?
184 185 186 187 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 184 def scalar_foreign_key(reflection) key = reflection.foreign_key key.to_s unless key.is_a?(Array) end |
#target_class(reflection, owner) ⇒ Object
: (untyped, untyped) -> untyped
143 144 145 146 147 148 149 |
# File 'lib/paper_trail_diff/prepared_edge_loader.rb', line 143 def target_class(reflection, owner) return reflection.klass unless reflection.polymorphic? Object.const_get(owner.public_send(reflection.foreign_type).to_s) rescue NameError nil end |