Class: Tag

Inherits:
ApplicationRecord show all
Defined in:
app/models/tag.rb

Overview

== Schema Information

Table name: tags
Database name: primary

id :integer not null, primary key
name :string(255)
public :boolean default(FALSE), not null
slug :string
taggings_count :integer
created_at :datetime not null
updated_at :datetime not null

Indexes

index_tags_on_name (name) UNIQUE
index_tags_on_name_lower_unique (lower((name)::text)) UNIQUE
index_tags_on_public (public)
index_tags_on_slug_unique (slug) UNIQUE
index_tags_on_taggings_count (taggings_count)

Constant Summary collapse

NAME_FORMAT =
/\A[a-z0-9]+(?:-[a-z0-9]+)*\z/

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_scopes, #to_relation

Methods included from Models::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#nameObject (readonly)



37
# File 'app/models/tag.rb', line 37

validates :name, presence: true

#slugObject (readonly)



41
# File 'app/models/tag.rb', line 41

validates :slug, uniqueness: true, allow_nil: true

Class Method Details

.by_taggable_typeActiveRecord::Relation<Tag>

A relation of Tags that are by taggable type. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Tag>)

See Also:



56
# File 'app/models/tag.rb', line 56

scope :by_taggable_type, ->(type) { joins(:taggings).where(taggings: { taggable_type: type }).distinct }

.canonicalize_input(name) ⇒ Object

Convert human-entered separators to the canonical dash form without
transliterating Unicode or discarding punctuation. The model validation
remains responsible for rejecting everything outside the ASCII domain.



94
95
96
# File 'app/models/tag.rb', line 94

def self.canonicalize_input(name)
  name.to_s.strip.downcase.gsub(/[[:space:]_]+/, '-')
end

.find_or_create_all_by_name(names) ⇒ Array<Tag>

Find or create multiple tags by name

Parameters:

  • names (Array<String>)

    The tag names to find or create

Returns:

  • (Array<Tag>)

    The found or newly created tags



101
102
103
# File 'app/models/tag.rb', line 101

def self.find_or_create_all_by_name(names)
  Array(names).filter_map { |name| find_or_create_by_name(name) }
end

.find_or_create_by_name(name) ⇒ Tag

Find or create a tag by canonical name (case-insensitive).

Parameters:

  • name (String)

    The tag name to find or create

Returns:

  • (Tag)

    The found or newly created tag



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'app/models/tag.rb', line 74

def self.find_or_create_by_name(name)
  return nil if name.blank?

  normalized = canonicalize_input(name)
  find_by(name: normalized) ||
    transaction(requires_new: true) { create!(name: normalized) }
rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid => e
  # A concurrent create can lose either the name or slug uniqueness race.
  # Recover by the dimension that collided, but re-raise unrelated validation
  # failures instead of silently dropping the tag (AppSignal #1358/#1370).
  raise e unless NAME_FORMAT.match?(normalized)

  find_by(slug: normalized) ||
    find_by(name: normalized) ||
    raise(e)
end

.most_usedActiveRecord::Relation<Tag>

A relation of Tags that are most used. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Tag>)

See Also:



54
# File 'app/models/tag.rb', line 54

scope :most_used, -> { order(taggings_count: :desc) }

.orderedActiveRecord::Relation<Tag>

A relation of Tags that are ordered. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Tag>)

See Also:



53
# File 'app/models/tag.rb', line 53

scope :ordered, -> { order(:name) }

.public_tagsActiveRecord::Relation<Tag>

A relation of Tags that are public tags. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Tag>)

See Also:



55
# File 'app/models/tag.rb', line 55

scope :public_tags, -> { where(public: true) }

.ransackable_associations(_auth_object = nil) ⇒ Object



63
64
65
# File 'app/models/tag.rb', line 63

def self.ransackable_associations(_auth_object = nil)
  %w[taggings]
end

.ransackable_attributes(_auth_object = nil) ⇒ Object

Ransack configuration



59
60
61
# File 'app/models/tag.rb', line 59

def self.ransackable_attributes(_auth_object = nil)
  %w[id name slug public taggings_count created_at updated_at]
end

.ransortable_attributes(_auth_object = nil) ⇒ Object



67
68
69
# File 'app/models/tag.rb', line 67

def self.ransortable_attributes(_auth_object = nil)
  %w[name taggings_count created_at updated_at]
end

.searchActiveRecord::Relation<Tag>

A relation of Tags that are search. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Tag>)

See Also:



48
49
50
51
52
# File 'app/models/tag.rb', line 48

scope :search, ->(query) {
  return all if query.blank?

  where('LOWER(name) LIKE LOWER(?)', "%#{sanitize_sql_like(query)}%")
}

Instance Method Details

#activity_typesActiveRecord::Relation<ActivityType>

Returns:

See Also:



35
# File 'app/models/tag.rb', line 35

has_many :activity_types, through: :taggings, source: :taggable, source_type: 'ActivityType'

#articlesActiveRecord::Relation<Article>

Polymorphic associations through taggings

Returns:

  • (ActiveRecord::Relation<Article>)

See Also:



29
# File 'app/models/tag.rb', line 29

has_many :articles, through: :taggings, source: :taggable, source_type: 'Article'

#digital_assetsActiveRecord::Relation<DigitalAsset>

Returns:

See Also:



31
# File 'app/models/tag.rb', line 31

has_many :digital_assets, through: :taggings, source: :taggable, source_type: 'DigitalAsset'

#floor_plan_displaysActiveRecord::Relation<FloorPlanDisplay>

Returns:

See Also:



32
# File 'app/models/tag.rb', line 32

has_many :floor_plan_displays, through: :taggings, source: :taggable, source_type: 'FloorPlanDisplay'

#itemsActiveRecord::Relation<Item>

Returns:

  • (ActiveRecord::Relation<Item>)

See Also:



33
# File 'app/models/tag.rb', line 33

has_many :items, through: :taggings, source: :taggable, source_type: 'Item'

#product_linesActiveRecord::Relation<ProductLine>

Returns:

See Also:



34
# File 'app/models/tag.rb', line 34

has_many :product_lines, through: :taggings, source: :taggable, source_type: 'ProductLine'

#showcasesActiveRecord::Relation<Showcase>

Returns:

See Also:



30
# File 'app/models/tag.rb', line 30

has_many :showcases, through: :taggings, source: :taggable, source_type: 'Showcase'

#taggingsActiveRecord::Relation<Tagging>

Returns:

  • (ActiveRecord::Relation<Tagging>)

See Also:



26
# File 'app/models/tag.rb', line 26

has_many :taggings, dependent: :destroy

#to_paramObject



120
121
122
# File 'app/models/tag.rb', line 120

def to_param
  slug || id.to_s
end

#to_sObject



116
117
118
# File 'app/models/tag.rb', line 116

def to_s
  name
end

#usage_by_typeHash

Get usage count by taggable type

Returns:

  • (Hash)

    Hash of taggable_type => count



107
108
109
# File 'app/models/tag.rb', line 107

def usage_by_type
  taggings.group(:taggable_type).count
end

#usage_countObject

Total usage count (alternative to taggings_count for accuracy)



112
113
114
# File 'app/models/tag.rb', line 112

def usage_count
  taggings_count || taggings.count
end