I'm trying to roll my own tagging system. My setup is (at the moment) much like acts_as_taggable_on, with Tags, Taggable objects, and Taggings to relate the one to the other. Taggable is a module, which will be included in Events, Users, and probably a few other kinds of objects that will be taggable. At the moment I'm just trying to hook it up to work with Events.
I'm following Railscast #167.
In the railscast, the virtual attribute tag_names is made accessible with attr_writer :tag_names
.
My problem is, I can't get the tag_names field to accept input unless I use attr_accessible :tag_names
(ie, 'attr_accessible' instead of 'attr_writer').
when specifying attr_writer :tag_names
, I submit the form and get the error: "Can't mass-assign protected attributes: tag_names". When I put attr_accessible :tag_names
instead, it seems to work okay, but this is a security issue, right? (Please note: there isn't a tag_names field in the DB for the Event objects.)
And why can't I replicate the Railscast? I'm running Rails 3.2.11, and the Railscast is from 2009, but I can't find anything saying that attr_writer has been replaced with attr_accessible in this later version or anything like that.
Thanks for any help!
The relevant part of my Event form:
<%= f.input :tag_names, label: "Tags (separate by commas)" %>
My Event model:
class Event < ActiveRecord::Base
include Taggable
# Default - order by start time
default_scope :order => 'events.start_time ASC'
belongs_to :creator, :foreign_key => "creator_id", :class_name => "User"
validates_presence_of :creator
(etc)
My Taggable module:
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
attr_accessible :tag_names
end
def tag(name)
name.strip!
tag = Tag.find_or_create_by_name(name)
self.taggings.find_or_create_by_tag_id(tag.id)
end
def untag(name)
name.strip!
t = Tag.find_by_name(name)
self.taggings.find_by_tag_id(t).destroy
end
# Return an array of tags applied to this taggable object
def tag_list
Tag.joins(:taggings).where(taggings: {taggable_id: self})
end
# Getter method for virtual attribute tag_names
def tag_names
@tag_names || tags.map(&:name).join(', ')
end
# Setter method for virtual attribute tag_names
def tag_names=(names)
@tag_names = names.split(",").map do |n|
Tag.find_or_create_by_name(n.strip)
end
end
end