In my application_controller I have this:
def tag_cloud
@tags = Tag.top_20.sort{ |x,y| x.id <=> y.id } if current_user
end
which will make the tag count work for all tags from all users, but instead I want the tag_cloud
to show only the tags of the current_user
. I tried this:
def tag_cloud
@tags = current_user.tags.top_20.sort{ |x,y| x.id <=> y.id } if current_user
end
but that gave me an error:
NoMethodError (undefined method `top_20' for #<ActiveRecord::Associations::CollectionProxy []>):
app/controllers/application_controller.rb:13:in `tag_cloud'
even though top_20
is defined in tag.rb:
class Tag < ActiveRecord::Base
has_many :taggings
scope :top_20, -> {
where("taggings_count != 0").order("taggings_count DESC").limit(15)
}
end
I'm using the acts-as-taggable-on gem. Thank you!
UPDATES
user.rb
class User < ActiveRecord::Base
acts_as_tagger
acts_as_taggable
has_many :notifications
has_many :activities
has_many :activity_likes
has_many :liked_activities, through: :activity_likes, class_name: 'Activity', source: :liked_activity
has_many :liked_comments, through: :comment_likes, class_name: 'Comment', source: :liked_comment
has_many :valuation_likes
has_many :habit_likes
has_many :goal_likes
has_many :quantified_likes
has_many :comment_likes
has_many :authentications
has_many :habits, dependent: :destroy
has_many :levels
has_many :combine_tags
has_many :valuations, dependent: :destroy
has_many :comments
has_many :goals, dependent: :destroy
has_many :quantifieds, dependent: :destroy
has_many :results, through: :quantifieds
has_many :notes
accepts_nested_attributes_for :habits, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :notes, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :quantifieds, :reject_if => :all_blank, :allow_destroy => true
accepts_nested_attributes_for :results, :reject_if => :all_blank, :allow_destroy => true
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }, unless: -> { from_omniauth? }
has_secure_password
validates :password, length: { minimum: 6 }
scope :publish, ->{ where(:conceal => false) }
User.tag_counts_on(:tags)
def count_mastered
@res = habits.reduce(0) do |count, habit|
habit.current_level == 6 ? count + 1 : count
end
end
def count_challenged
@challenged_count = habits.count - @res
end
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_initialize.tap do |user|
user.provider = auth.provider
user.image = auth.info.image
user.uid = auth.uid
user.name = auth.info.name
user.oauth_token = auth.credentials.token
user.oauth_expires_at = Time.at(auth.credentials.expires_at)
user.password = (0...8).map { (65 + rand(26)).chr }.join
user.email = (0...8).map { (65 + rand(26)).chr }.join+"@mailinator.com"
user.save!
end
end
def self.koala(auth)
access_token = auth['token']
facebook = Koala::Facebook::API.new(access_token)
facebook.get_object("me?fields=name,picture")
end
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Forgets a user. NOT SURE IF I REMOVE
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
def good_results_count
results.good_count
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
private
def from_omniauth?
provider && uid
end
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase unless from_omniauth?
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
tags_controller
class TagsController < ApplicationController
def index
@tags = Tag.all
end
def show
@tag = Tag.find(params[:id])
end
end
routes.rb
get 'tags/:tag', to: 'pages#home', as: :tag
schema.rb
create_table "taggings", force: true do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: true do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "users", force: true do |t|
t.string "name"
t.boolean "conceal", default: false
t.string "email"
t.text "missed_days"
t.text "missed_levels"
t.string "provider"
t.string "uid"
t.string "oauth_token"
t.datetime "oauth_expires_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.string "image"
end
goal.rb
class Goal < ActiveRecord::Base
scope :publish, ->{ where(:conceal => false) }
belongs_to :user
has_many :comments
has_many :notifications
has_many :notes
acts_as_taggable
scope :accomplished, -> { where(accomplished: true) }
scope :unaccomplished, -> { where(accomplished: false) }
scope :private_submit, -> { where(private_submit: true) }
scope :public_submit, -> { where(private_submit: false) }
validates :name, presence: true
has_many :goal_likes
has_many :likers, through: :goal_likes, class_name: 'User', source: :liker
scope :top_3, -> do
order("deadline").
limit(3)
end
end
Your issue is that
acts-as-taggable-on
does not actually use your Tag class. Or to be more specific - it works when you doTags.top_20
since you are calling it on your tag class. But theUser#tags
relationship actually usesActsAsTaggableOn::Tag
which explains theNoMethodError
.ActsAsTaggableOn seems to have this functionality built in already though:
Addition:
There is nothing in this requirement which says that you need to create a
Tag
orTagging
class. In fact doing so is likely to cause you and other developers a bunch of grief. The fact that the tag cloud on your home page works does not change the fact that you most likely are creating a bunch of future issues by replacing two relatively complex components (Tagging and Tag classes) with tiny stubs willy-nilly.If you look at the
tag_cloud
implementation is pretty easy to see that it takes a ActiveRecord::Relation or a collection or just any old enumerable such as an array.Since collections are just fancy pants versions of arrays merging them together is as easy as:
However joining relations together is a bit more complex since Rails currently does not support the SQL OR clause. (Its coming in Rails 5). You would either have to load and merge the collections as above or create your own where clause with AREL if multiple SQL queries is a performance issue.