Module: EasyTags::TaggableMethods

Defined in:
lib/easy_tags/taggable_methods.rb

Overview

Taggable instance methods

Class Method Summary collapse

Class Method Details

.inject(class_instance:) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity



8
9
10
11
12
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
93
# File 'lib/easy_tags/taggable_methods.rb', line 8

def inject(class_instance:)
  # rubocop:disable Metrics/BlockLength
  class_instance.class_eval do
    has_many(
      :taggings,
      as: :taggable,
      dependent: :destroy,
      class_name: '::EasyTags::Tagging',
      inverse_of: :taggable
    )

    after_save :_update_taggings, :_refresh_tagging
    after_find :_refresh_tagging

    # override ActiveRecord::Persistence#reload
    # to refresh tags each time the model instance gets reloaded
    def reload(*args)
      _refresh_tagging
      super
    end

    private

    attr_accessor :_taggable_contexts

    def _taggable_contexts
      @_taggable_contexts ||= {}
    end

    def _update_taggings
      tagging_contexts.each do |context|
        context_tags = _taggable_context(context)

        next unless context_tags.changed?

        context_tags.new_tags.each do |tag_name|
          tag = Tag.find_or_create_by!(name: tag_name)
          taggings.create!(context: context, tag: tag)
        end

        taggings
          .joins(:tag)
          .where(context: context)
          .where(Tag.table_name => { name: context_tags.removed_tags })
          .destroy_all
      end
    end

    def _refresh_tagging
      tagging_contexts.each do |context|
        _taggable_context(context).refresh
      end
    end

    include DirtyMethods

    def _taggable_context(context)
      _taggable_contexts[context] ||= TaggableContext.new(
        context: context,
        tags_association: public_send("#{context}_tags"),
        on_change: lambda { |tag_context|
          _mark_dirty(context: context, taggable_context: tag_context)
        }
      )
    end

    def _notify_tag_change(type:, tagging:)
      callbacks_for_type = tagging_callbacks[tagging.context.to_sym].select do |callback|
        callback.type == type
      end

      callbacks_for_type.each do |callback|
        callback.run(taggable: self, tagging: tagging)
      end
    end

    def _notify_tag_add(tagging)
      _notify_tag_change(type: :after_add, tagging: tagging)
    end

    def _notify_tag_remove(tagging)
      _notify_tag_change(type: :after_remove, tagging: tagging)
    end
  end
  # rubocop:enable Metrics/BlockLength
end