Module: Funes::Associations::ClassMethods

Defined in:
lib/funes/associations.rb

Instance Method Summary collapse

Instance Method Details

#refers_to(name, class_name: nil, foreign_key: nil, required: false) ⇒ void

This method returns an undefined value.

Declares a reference to another model. See Funes::Associations for details.

Parameters:

  • name (Symbol)

    The reference name (defines name / name= accessors).

  • class_name (String, Symbol, nil) (defaults to: nil)

    Name of the referenced class (not the class itself). Defaults to name.camelize.

  • foreign_key (Symbol, String, nil) (defaults to: nil)

    Attribute storing the id. Defaults to "#{name}_id".

  • required (Boolean) (defaults to: false)

    Whether to validate presence of the foreign key. Defaults to false.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/funes/associations.rb', line 107

def refers_to(name, class_name: nil, foreign_key: nil, required: false)
  if class_name.instance_of?(Class)
    raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
  end

  fk        = (foreign_key || "#{name}_id").to_sym
  klass_str = (class_name || name.to_s.camelize).to_s

  # Untyped (pass-through Value) attribute so integer and string/UUID ids both round-trip
  # through JSON unchanged. This is what gets serialized into +props+.
  attribute fk

  define_method(name) do
    id = public_send(fk)
    return nil if id.nil?

    # Cache entries are [id, record] pairs so a direct write to the foreign key attribute
    # invalidates the memoized record, as in ActiveRecord's stale-target handling.
    @__reference_cache ||= {}
    cached_id, cached_record = @__reference_cache[name]
    return cached_record if cached_id == id

    record = klass_str.constantize.find_by(id: id)
    @__reference_cache[name] = [ id, record ]
    record
  end

  define_method("#{name}=") do |record|
    @__reference_cache ||= {}
    if record.nil?
      public_send("#{fk}=", nil)
      @__reference_cache.delete(name)
    else
      public_send("#{fk}=", record.id)
      @__reference_cache[name] = [ record.id, record ]
    end
  end

  validates fk, presence: true if required
end