Class: PaperTrailDiff::AssociationDiscovery

Inherits:
Object
  • Object
show all
Defined in:
lib/paper_trail_diff/association_discovery.rb,
sig/generated/paper_trail_diff/association_discovery.rbs

Overview

Discovers finite, supported association paths for configuration UIs.

Instance Method Summary collapse

Constructor Details

#initialize(model_or_record, max_depth:) ⇒ AssociationDiscovery

: (untyped, max_depth: Integer) -> void

Parameters:

  • (Object)
  • max_depth: (Integer)


41
42
43
44
45
46
47
48
49
50
51
# File 'lib/paper_trail_diff/association_discovery.rb', line 41

def initialize(model_or_record, max_depth:)
  unless max_depth.is_a?(Integer) && max_depth.positive?
    raise ConfigurationError, 'max_depth: must be a positive integer'
  end

  @model_class = model_or_record.is_a?(Class) ? model_or_record : model_or_record.class
  @max_depth = max_depth
  return if @model_class.respond_to?(:reflect_on_all_associations)

  raise ConfigurationError, 'expected an ActiveRecord model class or record'
end

Instance Method Details

#association_path(prefix, name) ⇒ String

: (String, String) -> String

Parameters:

  • (String)
  • (String)

Returns:

  • (String)


128
129
130
# File 'lib/paper_trail_diff/association_discovery.rb', line 128

def association_path(prefix, name)
  prefix.empty? ? name : "#{prefix}.#{name}"
end

#callArray[AssociationDescriptor]

: () -> Array

Returns:



54
55
56
# File 'lib/paper_trail_diff/association_discovery.rb', line 54

def call
  discover(@model_class, prefix: '', ancestry: [@model_class], depth: 1).freeze
end

#describe_and_descend(reflection, prefix:, ancestry:, depth:) ⇒ Array[AssociationDescriptor]

: (untyped, prefix: String, ancestry: Array, depth: Integer) -> Array

Parameters:

  • (Object)
  • prefix: (String)
  • ancestry: (Array[untyped])
  • depth: (Integer)

Returns:



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/paper_trail_diff/association_discovery.rb', line 71

def describe_and_descend(reflection, prefix:, ancestry:, depth:)
  path = association_path(prefix, reflection.name.to_s)
  target_class = reflection_target(reflection)
  cycle = target_class ? ancestry.include?(target_class) : false
  descriptor = descriptor_for(reflection, path, target_class, cycle)
  return [descriptor] unless target_class && !cycle && depth < @max_depth

  descendants = discover(
    target_class,
    prefix: path,
    ancestry: [*ancestry, target_class],
    depth: depth + 1
  )
  [descriptor, *descendants]
end

#descriptor_for(reflection, path, target_class, cycle) ⇒ AssociationDescriptor

: (untyped, String, untyped, bool) -> AssociationDescriptor

Parameters:

  • (Object)
  • (String)
  • (Object)
  • (Boolean)

Returns:



88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/paper_trail_diff/association_discovery.rb', line 88

def descriptor_for(reflection, path, target_class, cycle)
  flags = [] #: Array[Symbol]
  flags << :polymorphic if reflection.polymorphic?
  flags << :cycle if cycle
  AssociationDescriptor.new(
    path: path,
    kind: reflection.macro,
    target_type: target_class&.name || reflection_target_name(reflection),
    through: reflection.options[:through]&.to_s,
    flags: flags
  )
end

#discover(model_class, prefix:, ancestry:, depth:) ⇒ Array[AssociationDescriptor]

: (untyped, prefix: String, ancestry: Array, depth: Integer) -> Array

Parameters:

  • (Object)
  • prefix: (String)
  • ancestry: (Array[untyped])
  • depth: (Integer)

Returns:



64
65
66
67
68
# File 'lib/paper_trail_diff/association_discovery.rb', line 64

def discover(model_class, prefix:, ancestry:, depth:)
  supported_reflections(model_class).flat_map do |reflection|
    describe_and_descend(reflection, prefix: prefix, ancestry: ancestry, depth: depth)
  end
end

#paper_trail_versions?(model_class, reflection) ⇒ Boolean

: (untyped, untyped) -> bool

Parameters:

  • (Object)
  • (Object)

Returns:

  • (Boolean)


110
111
112
113
# File 'lib/paper_trail_diff/association_discovery.rb', line 110

def paper_trail_versions?(model_class, reflection)
  model_class.respond_to?(:versions_association_name) &&
    reflection.name.to_s == model_class.versions_association_name.to_s
end

#reflection_target(reflection) ⇒ Object

: (untyped) -> untyped

Parameters:

  • (Object)

Returns:

  • (Object)


116
117
118
119
120
# File 'lib/paper_trail_diff/association_discovery.rb', line 116

def reflection_target(reflection)
  reflection.klass unless reflection.polymorphic?
rescue NameError
  nil
end

#reflection_target_name(reflection) ⇒ String?

: (untyped) -> String?

Parameters:

  • (Object)

Returns:

  • (String, nil)


123
124
125
# File 'lib/paper_trail_diff/association_discovery.rb', line 123

def reflection_target_name(reflection)
  reflection.class_name.to_s unless reflection.polymorphic?
end

#supported_reflections(model_class) ⇒ Array[untyped]

: (untyped) -> Array

Parameters:

  • (Object)

Returns:

  • (Array[untyped])


102
103
104
105
106
107
# File 'lib/paper_trail_diff/association_discovery.rb', line 102

def supported_reflections(model_class)
  model_class.reflect_on_all_associations
             .select { |reflection| SUPPORTED_ASSOCIATION_MACROS.include?(reflection.macro) }
             .reject { |reflection| paper_trail_versions?(model_class, reflection) }
             .sort_by { |reflection| reflection.name.to_s }
end