Class: LcpRuby::Metadata::AssociationDefinition

Inherits:
Object
  • Object
show all
Defined in:
lib/lcp_ruby/metadata/association_definition.rb

Constant Summary collapse

VALID_TYPES =
%w[belongs_to has_many has_one].freeze
NESTED_ATTRIBUTES_KEYS =
%w[allow_destroy reject_if limit update_only].freeze
SAFE_NESTED_DEFAULTS =
{ "reject_if" => "all_blank", "update_only" => true }.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs = {}) ⇒ AssociationDefinition

Returns a new instance of AssociationDefinition.



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 14

def initialize(attrs = {})
  @type = attrs[:type].to_s
  @name = attrs[:name].to_s
  @target_model = attrs[:target_model]&.to_s
  @class_name = attrs[:class_name]
  @owner_name = attrs[:owner_name]&.to_s

  # Polymorphic and through must be set before infer_foreign_key
  @polymorphic = attrs.fetch(:polymorphic, false)
  @as = attrs[:as]&.to_s
  @through = attrs[:through]&.to_s
  @source = attrs[:source]&.to_s

  @foreign_key = attrs[:foreign_key]&.to_s || infer_foreign_key
  @primary_key = attrs[:primary_key]&.to_s
  @dependent = attrs[:dependent]&.to_sym
  required = attrs[:required]
  # `required_explicit` lets the validator distinguish "key absent" from
  # "key present and false". Used by C1 three-layer warning
  # (school-improve-tasks-details-aggregated.md): hand-written
  # belongs_to without an explicit `required:` defaults to true under
  # AR Rails 5+ — the warning surfaces this footgun.
  @required_explicit = !required.nil?
  @required = required.nil? ? (@type == "belongs_to") : required

  # Simple pass-throughs
  @inverse_of = attrs[:inverse_of]&.to_sym
  @counter_cache = attrs[:counter_cache]
  @touch = attrs[:touch]

  # Tier 2: autosave / validate
  @autosave = attrs[:autosave]
  @validate = attrs[:validate]

  # Nested attributes
  @nested_attributes = parse_nested_attributes(attrs[:nested_attributes])

  # Order (for has_many/has_one default ordering)
  @order = attrs[:order]

  # Per-item flag for bind_to: models. When true, LCP applies the AR macro
  # on the host class (and for belongs_to, additively adds the FK column).
  # Ignored on dynamic models. See docs/design/bind_to_managed_fields_and_associations.md.
  @lcp_managed = attrs[:lcp_managed] == true

  # Suppresses the missing-inverse warning from `lcp_ruby:validate` (#20).
  # Use when a `belongs_to` is intentionally one-directional and the
  # parent doesn't need a `has_many` (e.g. audit references like
  # `created_by`, where reverse traversal would be noise).
  # Only valid on belongs_to — has_many/has_one lacking a belongs_to is
  # a real bug (FK has nowhere to go), enforced in validate!.
  @skip_inverse_check = attrs[:skip_inverse_check] == true

  validate!
end

Instance Attribute Details

#asObject (readonly)

Returns the value of attribute as.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def as
  @as
end

#autosaveObject (readonly)

Returns the value of attribute autosave.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def autosave
  @autosave
end

#class_nameObject (readonly)

Returns the value of attribute class_name.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def class_name
  @class_name
end

#counter_cacheObject (readonly)

Returns the value of attribute counter_cache.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def counter_cache
  @counter_cache
end

#dependentObject (readonly)

Returns the value of attribute dependent.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def dependent
  @dependent
end

#foreign_keyObject (readonly)

Returns the value of attribute foreign_key.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def foreign_key
  @foreign_key
end

#inverse_ofObject (readonly)

Returns the value of attribute inverse_of.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def inverse_of
  @inverse_of
end

#lcp_managedObject (readonly)

Returns the value of attribute lcp_managed.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def lcp_managed
  @lcp_managed
end

#nameObject (readonly)

Returns the value of attribute name.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def name
  @name
end

#nested_attributesObject (readonly)

Returns the value of attribute nested_attributes.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def nested_attributes
  @nested_attributes
end

#orderObject (readonly)

Returns the value of attribute order.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def order
  @order
end

#owner_nameObject (readonly)

Returns the value of attribute owner_name.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def owner_name
  @owner_name
end

#polymorphicObject (readonly)

Returns the value of attribute polymorphic.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def polymorphic
  @polymorphic
end

#primary_keyObject (readonly)

Returns the value of attribute primary_key.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def primary_key
  @primary_key
end

#requiredObject (readonly)

Returns the value of attribute required.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def required
  @required
end

#required_explicitObject (readonly)

Returns the value of attribute required_explicit.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def required_explicit
  @required_explicit
end

#skip_inverse_checkObject (readonly)

Returns the value of attribute skip_inverse_check.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def skip_inverse_check
  @skip_inverse_check
end

#sourceObject (readonly)

Returns the value of attribute source.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def source
  @source
end

#target_modelObject (readonly)

Returns the value of attribute target_model.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def target_model
  @target_model
end

#throughObject (readonly)

Returns the value of attribute through.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def through
  @through
end

#touchObject (readonly)

Returns the value of attribute touch.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def touch
  @touch
end

#typeObject (readonly)

Returns the value of attribute type.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def type
  @type
end

#validateObject (readonly)

Returns the value of attribute validate.



8
9
10
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 8

def validate
  @validate
end

Class Method Details

.from_hash(hash, owner_name = nil) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 70

def self.from_hash(hash, owner_name = nil)
  new(
    type: hash["type"],
    name: hash["name"],
    target_model: hash["target_model"],
    class_name: hash["class_name"],
    foreign_key: hash["foreign_key"],
    primary_key: hash["primary_key"],
    dependent: hash["dependent"],
    required: hash["required"],
    inverse_of: hash["inverse_of"],
    counter_cache: hash["counter_cache"],
    touch: hash["touch"],
    polymorphic: hash["polymorphic"],
    as: hash["as"],
    through: hash["through"],
    source: hash["source"],
    autosave: hash["autosave"],
    validate: hash["validate"],
    nested_attributes: hash["nested_attributes"],
    order: hash["order"],
    lcp_managed: hash["lcp_managed"],
    skip_inverse_check: hash["skip_inverse_check"],
    owner_name: owner_name
  )
end

Instance Method Details

#belongs_to?Boolean

Returns:

  • (Boolean)


105
106
107
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 105

def belongs_to?
  @type == "belongs_to"
end

#lcp_managed?Boolean

Returns:

  • (Boolean)


97
98
99
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 97

def lcp_managed?
  @lcp_managed
end

#lcp_model?Boolean

Returns:

  • (Boolean)


101
102
103
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 101

def lcp_model?
  target_model.present?
end

#resolved_class_nameObject



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 121

def resolved_class_name
  if lcp_model?
    target_def = LcpRuby.loader.model_definitions[target_model]
    if target_def&.bind_to.present?
      target_def.bind_to
    else
      "LcpRuby::Dynamic::#{target_model.camelize}"
    end
  else
    class_name
  end
end

#singular?Boolean

Returns:

  • (Boolean)


109
110
111
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 109

def singular?
  @type == "belongs_to" || @type == "has_one"
end

#through?Boolean

Returns:

  • (Boolean)


117
118
119
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 117

def through?
  @through.present?
end

#traversable?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/lcp_ruby/metadata/association_definition.rb', line 113

def traversable?
  lcp_model? && !polymorphic && !through?
end