Module: MakeTaggable::Taggable::Ownership

Defined in:
lib/make_taggable/taggable/ownership.rb

Overview

Tags applied by a tagger, kept separate from the record's own tags.

Owned tags do not appear in tag_list, which only ever returns unowned tags. Use all_tags_list to see both together.

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Parameters:

  • base (Class)

    the model being made taggable



19
20
21
22
23
24
25
26
27
# File 'lib/make_taggable/taggable/ownership.rb', line 19

def self.included(base)
  base.extend MakeTaggable::Taggable::Ownership::ClassMethods

  base.class_eval do
    after_save :save_owned_tags
  end

  base.initialize_make_taggable_ownership
end

Instance Method Details

#cached_owned_tag_list_on(context) ⇒ Hash{ActiveRecord::Base => MakeTaggable::TagList}

The per-owner tag lists held in memory for a context, keyed by owner.

Parameters:

  • context (Symbol, String)

    the tagging context

Returns:



111
112
113
114
# File 'lib/make_taggable/taggable/ownership.rb', line 111

def cached_owned_tag_list_on(context)
  variable_name = "@owned_#{context}_list"
  (instance_variable_defined?(variable_name) && instance_variable_get(variable_name)) || instance_variable_set(variable_name, {})
end

#owner_tag_list_on(owner, context) ⇒ MakeTaggable::TagList

One owner's tag list for a context.

Parameters:

  • owner (ActiveRecord::Base)

    the tagger

  • context (Symbol, String)

    the tagging context

Returns:



123
124
125
126
127
128
129
# File 'lib/make_taggable/taggable/ownership.rb', line 123

def owner_tag_list_on(owner, context)
  add_custom_context(context)

  cache = cached_owned_tag_list_on(context)

  cache[owner] ||= MakeTaggable::TagList.new(*owner_tags_on(owner, context).map(&:name))
end

#owner_tags(owner) ⇒ ActiveRecord::Relation

This record's tags belonging to one owner, across every context.

Parameters:

  • owner (ActiveRecord::Base, NilClass)

    the tagger, or nil for every tag on the record

Returns:

  • (ActiveRecord::Relation)


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/make_taggable/taggable/ownership.rb', line 66

def owner_tags(owner)
  scope = if owner.nil?
    base_tags
  else
    base_tags.where(
      MakeTaggable::Tagging.table_name.to_s => {
        tagger_id: owner.id,
        tagger_type: owner.class.base_class.to_s
      }
    )
  end

  # when preserving tag order, return tags in created order
  # if we added the order to the association this would always apply
  if self.class.preserve_tag_order?
    scope.order("#{MakeTaggable::Tagging.table_name}.id")
  else
    scope
  end
end

#owner_tags_on(owner, context) ⇒ ActiveRecord::Relation

This record's tags belonging to one owner, in one context.

Examples:

@photo.owner_tags_on(@user, :locations)

Parameters:

  • owner (ActiveRecord::Base, NilClass)

    the tagger, or nil for every tag on the record

  • context (Symbol, String)

    the tagging context

Returns:

  • (ActiveRecord::Relation)


97
98
99
100
101
102
103
# File 'lib/make_taggable/taggable/ownership.rb', line 97

def owner_tags_on(owner, context)
  owner_tags(owner).where(
    MakeTaggable::Tagging.table_name.to_s => {
      context: context
    }
  )
end

#reload(*args) ⇒ ActiveRecord::Base

Reloads the record, discarding the owned tag lists held in memory.

Parameters:

  • args (Array<Object>)

    arguments forwarded to Active Record

Returns:

  • (ActiveRecord::Base)

    self



153
154
155
156
157
158
159
# File 'lib/make_taggable/taggable/ownership.rb', line 153

def reload(*args)
  self.class.tag_types.each do |context|
    instance_variable_set("@owned_#{context}_list", nil)
  end

  super
end

#save_owned_tagsTrueClass

Writes every owner's tag lists to the database. Runs after save.

Returns:

  • (TrueClass)


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/make_taggable/taggable/ownership.rb', line 166

def save_owned_tags
  assigned_tagging_contexts.each do |context|
    cached_owned_tag_list_on(context).each do |owner, tag_list|
      # Find existing tags or create non-existing tags:
      tags = find_or_create_tags_from_list_with_context(tag_list.uniq, context)

      # Tag objects for owned tags
      owned_tags = owner_tags_on(owner, context).to_a

      # Tag maintenance based on whether preserving the created order of tags
      if self.class.preserve_tag_order?
        old_tags, new_tags = owned_tags - tags, tags - owned_tags

        shared_tags = owned_tags & tags

        if shared_tags.any? && tags[0...shared_tags.size] != shared_tags
          index = shared_tags.each_with_index { |_, i| break i unless shared_tags[i] == tags[i] }

          # Update arrays of tag objects
          old_tags |= owned_tags.from(index)
          new_tags |= owned_tags.from(index) & shared_tags

          # Order the array of tag objects to match the tag list
          new_tags = tags.map { |t| new_tags.find { |n| n.name.downcase == t.name.downcase } }.compact
        end
      else
        # Delete discarded tags and create new tags
        old_tags = owned_tags - tags
        new_tags = tags - owned_tags
      end

      # Find all taggings that belong to the taggable (self), are owned by the owner,
      # have the correct context, and are removed from the list.
      if old_tags.present?
        MakeTaggable::Tagging.where(taggable_id: id, taggable_type: self.class.base_class.to_s,
          tagger_type: owner.class.base_class.to_s, tagger_id: owner.id,
          tag_id: old_tags, context: context).destroy_all
      end

      # Create new taggings, in a consistent order -- see the note in
      # Core#save_tags on why the order matters.
      new_tags = new_tags.sort_by(&:id) unless self.class.preserve_tag_order?

      new_tags.each do |tag|
        taggings.create!(tag_id: tag.id, context: context.to_s, tagger: owner, taggable: self)
      end
    end
  end

  true
end

#set_owner_tag_list_on(owner, context, new_list) ⇒ MakeTaggable::TagList

Replaces one owner's tag list for a context. Saved with the record.

Parameters:

  • owner (ActiveRecord::Base)

    the tagger

  • context (Symbol, String)

    the tagging context

  • new_list (String, Array<String>)

    the tags to apply

Returns:



139
140
141
142
143
144
145
# File 'lib/make_taggable/taggable/ownership.rb', line 139

def set_owner_tag_list_on(owner, context, new_list)
  add_custom_context(context)

  cache = cached_owned_tag_list_on(context)

  cache[owner] = MakeTaggable.default_parser.new(new_list).parse
end