Module: Anchormodel::Util

Defined in:
lib/anchormodel/util.rb

Overview

A swiss army knife for common functionality

Class Method Summary collapse

Class Method Details

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

Installs an anchormodel attribute in a model class

Parameters:

  • model_class (ActiveRecord::Base)

    Internal only. The model class that the attribute should be installed to.

  • 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 false, 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



13
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/anchormodel/util.rb', line 13

def self.install_methods_in_model(model_class, 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
  model_class.anchormodel_attributes = model_class.anchormodel_attributes.merge({ attribute_name => attribute }).freeze

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

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

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

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

  # Supply serializer and deserializer
  model_class.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 model_class.respond_to?(:"#{entry.key}?")
        fail("Anchormodel reader #{entry.key}? already defined for #{self}, add `model_readers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
      end
      model_class.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 model_class.respond_to?(:"#{entry.key}!")
        fail("Anchormodel writer #{entry.key}! already defined for #{self}, add `model_writers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
      end
      model_class.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 model_class.respond_to?(entry.key)
        fail("Anchormodel scope #{entry.key} already defined for #{self}, add `model_scopes: false` to `belongs_to_anchormodel :#{attribute_name}`.")
      end
      model_class.scope(entry.key, -> { where(attribute_name => entry.key) })
    end
  end
end