Module: YamlExporter::Nodes::OfResolution

Included in:
ManyReference, ManyThrough, OneReferenceOf
Defined in:
lib/yaml_exporter/nodes/of_resolution.rb

Overview

Shared of: navigation for reference nodes that identify their target indirectly: the YAML value names a column on a related model (of:) reachable from the target, not on the target itself.

Example: a reviewer target is a CorporateUser, and CorporateUser belongs_to :user (a User with a slug). Declaring find_by: :slug, of: :user stores the User's slug in the YAML and, on import, resolves the User first and then reverses of: back to the CorporateUser.

Mixed into OneReferenceOf, ManyReference and ManyThrough. The host must provide target_class and set @find_by and @of (both symbols).

Instance Method Summary collapse

Instance Method Details

#find_target_via(related) ⇒ Object

Navigate from a resolved related record back to the target.

belongs_to (:user on CorporateUser): FK is on the target. target_class.find_by(user_id: related.id) has_one (:profile on CorporateUser): FK is on the related record. target_class.find_by(id: related.corporate_user_id)



45
46
47
48
49
50
51
52
53
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 45

def find_target_via(related)
  if of_reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection)
    pk_val = related.public_send(of_reflection.association_primary_key)
    target_class.find_by(of_reflection.foreign_key => pk_val)
  else # has_one
    fk_val = related.public_send(of_reflection.foreign_key)
    target_class.find_by(of_reflection.association_primary_key => fk_val)
  end
end

#of_classObject

The class of the related model (CorporateUser.user -> User).



25
26
27
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 25

def of_class
  @of_class ||= of_reflection.klass
end

#of_reflectionObject

The related model that actually carries the find_by column (CorporateUser.user -> User).



20
21
22
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 20

def of_reflection
  @of_reflection ||= target_class.reflect_on_association(@of)
end

#of_value_for(target) ⇒ Object

The YAML value for a target: the find_by column on its of: relation. nil when the target has no related record (can't be represented).



57
58
59
60
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 57

def of_value_for(target)
  related = target.public_send(@of)
  related&.public_send(@find_by)
end

#resolve_target_by_of(value) ⇒ Object

Resolve a single YAML value to a target record: find the related record by find_by, then reverse of: back to the target. Returns nil if either lookup misses.



32
33
34
35
36
37
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 32

def resolve_target_by_of(value)
  related = of_class.find_by(@find_by => value)
  return nil unless related

  find_target_via(related)
end

#singular_of_association?(reflection) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 78

def singular_of_association?(reflection)
  reflection.is_a?(ActiveRecord::Reflection::BelongsToReflection) ||
    reflection.is_a?(ActiveRecord::Reflection::HasOneReflection)
end

#validate_of_reflection!(node_label) ⇒ Object

Declaration-time check: of: must name a 1:[0,1] association on the target. Raises ArgumentError at class load otherwise.

Raises:

  • (ArgumentError)


64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/yaml_exporter/nodes/of_resolution.rb', line 64

def validate_of_reflection!(node_label)
  of_ref = target_class.reflect_on_association(@of)
  unless of_ref
    raise ArgumentError,
          "`#{node_label}, of: #{@of.inspect}`: #{target_class} has no association `#{@of}`"
  end

  return if singular_of_association?(of_ref)

  raise ArgumentError,
        "`#{node_label}, of: #{@of.inspect}`: `of:` must be a 1:[0,1] association " \
        "(belongs_to or has_one); got #{of_ref.macro}"
end