Module: Anchormodel::ModelMixin

Extended by:
ActiveSupport::Concern
Defined in:
lib/anchormodel/model_mixin.rb

Overview

All Rails models making use of #belongs_to_anchormodel must include this mixin. Typically, it is included in application_record.rb.

Class Method Summary collapse

Class Method Details

.belongs_to_anchormodel(attribute_name, anchormodel_class = nil, optional: false, model_readers: true, model_writers: true, model_scopes: true, model_methods: nil) ⇒ Object

Creates an attribute linking to an Anchormodel. The attribute should be present in the DB and the column should be named the same as attribute_name.

Parameters:

  • attribute_name (String, Symbol)

    The name and database column of the attribute

  • anchormodel_class (Class) (defaults to: nil)

    Class of the Anchormodel (omit if attribute :foo_bar holds a FooBar)

  • optional (Boolean) (defaults to: false)

    If true, a presence validation is added to the model.

  • model_readers (Boolean) (defaults to: true)

    If true, the model is given an ActiveRecord::Enum style method my_model.my_key? reader for each key in the anchormodel

  • model_writers (Boolean) (defaults to: true)

    If true, the model is given an ActiveRecord::Enum style method my_model.my_key! writer for each key in the anchormodel

  • model_scopes (Boolean) (defaults to: true)

    If true, the model is given an ActiveRecord::Enum style scope MyModel.mykey for each key in the anchormodel

  • model_methods (Boolean, NilClass) (defaults to: nil)

    If non-nil, this mass-assigns and overrides model_readers, model_writers and model_scopes



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
69
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
# File 'lib/anchormodel/model_mixin.rb', line 20

def belongs_to_anchormodel(attribute_name, anchormodel_class = nil, optional: false, model_readers: true,
                           model_writers: true, model_scopes: true, model_methods: nil)
  anchormodel_class ||= attribute_name.to_s.classify.constantize
  attribute_name = attribute_name.to_sym
  attribute = Anchormodel::Attribute.new(self, attribute_name, anchormodel_class, optional)

  # Mass configurations if model_methods was specfied
  unless model_methods.nil?
    model_readers = model_methods
    model_writers = model_methods
    model_scopes = model_methods
  end

  # Register attribute
  self.anchormodel_attributes = anchormodel_attributes.merge({ attribute_name => attribute }).freeze

  # Add presence validation if required
  unless optional
    validates attribute_name, presence: true
  end

  # Make casting work
  # Define serializer/deserializer
  active_model_type_value = Anchormodel::ActiveModelTypeValue.new(attribute)

  # Overwrite reader to force building anchors at every retrieval
  define_method(attribute_name.to_s) do
    active_model_type_value.deserialize(read_attribute(attribute_name))
  end

  # Override writer to fail early when an invalid target value is specified
  define_method("#{attribute_name}=") do |new_value|
    write_attribute(attribute_name, active_model_type_value.serialize(new_value))
  end

  # Supply serializer and deserializer
  attribute attribute_name, active_model_type_value

  # Create ActiveRecord::Enum style reader directly in the model if asked to do so
  # For a model User with anchormodel Role with keys :admin and :guest, this creates user.admin? and user.guest? (returning true iff role is admin/guest)
  if model_readers
    anchormodel_class.all.each do |entry|
      if respond_to?(:"#{entry.key}?")
        fail("Anchormodel reader #{entry.key}? already defined for #{self}, add `model_readers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
      end
      define_method(:"#{entry.key}?") do
        public_send(attribute_name.to_s) == entry
      end
    end
  end

  # Create ActiveRecord::Enum style writer directly in the model if asked to do so
  # For a model User with anchormodel Role with keys :admin and :guest, this creates user.admin! and user.guest! (setting the role to admin/guest)
  if model_writers
    anchormodel_class.all.each do |entry|
      if respond_to?(:"#{entry.key}!")
        fail("Anchormodel writer #{entry.key}! already defined for #{self}, add `model_writers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
      end
      define_method(:"#{entry.key}!") do
        public_send(:"#{attribute_name}=", entry)
      end
    end
  end

  # Create ActiveRecord::Enum style scope directly in the model class if asked to do so
  # For a model User with anchormodel Role with keys :admin and :guest, this creates user.admin! and user.guest! (setting the role to admin/guest)
  if model_scopes
    anchormodel_class.all.each do |entry|
      if respond_to?(entry.key)
        fail("Anchormodel scope #{entry.key} already defined for #{self}, add `model_scopes: false` to `belongs_to_anchormodel :#{attribute_name}`.")
      end
      scope(entry.key, -> { where(attribute_name => entry.key) })
    end
  end
end