tadyjp/rendezvous

View on GitHub
app/models/tag.rb

Summary

Maintainability
A
0 mins
Test Coverage
# == Schema Information
#
# Table name: tags
#
#  id          :integer          not null, primary key
#  name        :string(255)
#  created_at  :datetime
#  updated_at  :datetime
#  ancestry    :string(255)
#  body        :text
#  posts_count :integer          default(0), not null
#

class Tag < ActiveRecord::Base
  # for tree structure
  has_ancestry

  # for versioning
  has_paper_trail

  ######################################################################
  # Associations
  ######################################################################
  has_many :post_tags
  has_many :posts, through: :post_tags

  ######################################################################
  # Named scope
  ######################################################################
  default_scope { order(updated_at: :desc) }

  scope :posts_exist, lambda {
    select('tags.*, count(posts.id) as posts_count')
      .joins(:posts)
      .group('tags.id')
      .having('posts_count > 0')
  }

  scope :recently_created, (lambda do |limit = 10|
    order(created_at: :desc).limit(limit)
  end)

  ######################################################################
  # Class method
  ######################################################################
  class << self
    # 最近投稿されたTagを取得
    def recent(limit = 10)
      Post.recent(20).map(&:tags).flatten.compact.uniq.take(limit)
    end

    # TODO: cache
    def monthly_popular(limit = 10)
      tags_this_month = Hash.new { |h, k| h[k] = 0 } # { <tag.id> => <count> }
      tags_last_month = Hash.new { |h, k| h[k] = 0 } # { <tag.id> => <count> }

      Post.this_month.each do |post|
        post.tags.each { |tag| tags_this_month[tag.id] += 1 }
      end

      Post.last_month.each do |post|
        post.tags.each { |tag| tags_last_month[tag.id] += 1 }
      end

      tags_this_month_with_score = {}
      tags_this_month.each do |tag_id, this_month_count|
        next if this_month_count <= 3
        tags_this_month_with_score[tag_id] = this_month_count.to_f / (tags_last_month[tag_id] + 1)
      end

      sorted = tags_this_month_with_score.sort_by { |_k, v| -v }.take(limit).to_h

      Tag.find(sorted.keys).to_a.zip(sorted.values).to_h
    end
  end

  ######################################################################
  # Instance method
  ######################################################################

  def recent_posts(limit = 30)
    posts.recent(limit)
  end

  # 自分のタグに紐づくPostをすべて`other_tag`へ移動する
  def move_all_posts_to!(other_tag)
    posts.each do |moving_post|
      moving_post.tags.delete(self)
      moving_post.tags << other_tag unless moving_post.tags.include?(other_tag)
    end
  end

  # 親タグを設定する
  def parent_tag=(other_tag)
    self.parent = other_tag
    self.save!
  end
end