Update owner tags via form
Asked Answered
C

7

7

I would like to uniquely use owner tags in my app. My problem is that when I create / update a post via a form I only have f.text_field :tag_list which only updates the tags for the post but has no owner. If I use f.text_field :all_tags_list it doesn't know the attribute on create / update. I could add in my controller:

User.find(:first).tag( @post, :with => params[:post][:tag_list], :on => :tags )

but then I have duplicate tags, for post and for the owner tags. How can I just work with owner tags?

Coachman answered 1/9, 2010 at 14:52 Comment(4)
I'm trying to achieve the same thing. Did you get anywhere with this?Metallic
I've asked about this on github: github.com/mbleigh/acts-as-taggable-on/issues/issue/111/#issue/…Metallic
Have you thought about having an owner_tags model that belongs to Owner and Post? It would require a bit more legwork, but then you will know who owns the tags as well as which post they belong to. You would probably need to have attr_accessor :tag_list so that the form views still work and then get the model to split them out to the owner_tags model on create/udpate.Winne
In the readme of acts_as_taggable_on it shows you how to declare ownership tags. I don't 'get' what you are trying to do. How can a tag have a post but no owner?Theobald
G
4

The answer proposed by customersure (tsdbrown on SO) on https://github.com/mbleigh/acts-as-taggable-on/issues/111 works for me

# In a taggable model:
before_save :set_tag_owner
def set_tag_owner
    # Set the owner of some tags based on the current tag_list
    set_owner_tag_list_on(account, :tags, self.tag_list)
    # Clear the list so we don't get duplicate taggings
    self.tag_list = nil
 end

# In the view:
<%= f.text_field :tag_list, :value => @obj.all_tags_list %>
Gatefold answered 1/7, 2011 at 8:17 Comment(2)
This doesn't seem to be working for me in this example #6934159Sokul
This worked great for me. However, be sure to put conditions on the before_save unless you REALLY want this code executed before EVERY save (for example, on saves outside of the form). For example if you update an attribute elsewhere in your code and those attributes don't include "tag_list", you will get burned (the owner's tag list will get set to nil accidentally). I found this out first hand :)Experienced
B
2

I used an observer to solve this. Something like:

in /app/models/tagging_observer.rb

class TaggingObserver < ActiveRecord::Observer
  observe ActsAsTaggableOn::Tagging

  def before_save(tagging)
    tagging.tagger = tagging.taggable.user if (tagging.taggable.respond_to?(:user) and tagging.tagger != tagging.taggable.user)
  end
end

Don't forget to declare your observer in application.rb

config.active_record.observers = :tagging_observer
Barone answered 13/9, 2011 at 8:9 Comment(0)
A
1

Late to the party, but I found guillaume06's solution worked well, and I added some additional functionality to it:

What this will enable: You will be able to specify the tag owner by the name of the relationship between the tagged model and the tag owner model.

How: write a module and include in your lib on initialization (require 'lib/path/to/tagger'):

  module Giga::Tagger
    extend ActiveSupport::Concern
    included do
      def self.tagger owner
        before_save :set_tag_owner
        def set_tag_owner
          self.tag_types.each do |tag|
            tag_type = tag.to_s
            # Set the owner of some tags based on the current tag_list
            set_owner_tag_list_on(owner, :"#{tag_type}", self.send(:"#{tag_type.chop}_list"))
            # Clear the list so we don't get duplicate taggings
            self.send(:"#{tag_type.chop}_list=",nil)
          end
        end

      end
    end
  end

Usage Instructions:

  Given: A model, Post, that is taggable
         A model, User, that is the tag owner
         A post is owned by the user through a relationship called :owner
  Then add to Post.rb:
         include Tagger
         acts_as_taggable_on :skills, :interests, :tags
         tagger :owner
  Make sure Post.rb already has called acts_as_taggable_on, and that User.rb has acts_as_tagger
  Note: This supports multiple tag contexts, not just tags (eg skills, interests)..
Arianna answered 9/7, 2013 at 10:52 Comment(0)
I
1

the set_tag_owner before_save worked for me. But as bcb mentioned, I had to add a condition (tag_list_changed?) to prevent the tags from being deleted on update:

def set_tag_owner
  if tag_list_changed?
    set_owner_tag_list_on(account, :tags, tag_list)
    self.tag_list = nil
  end
end
Indecency answered 29/7, 2014 at 14:59 Comment(1)
Please share your code, I don't find this to be a valid answer.Emulate
B
1

When working with ownership the taggable model gets its tags a little different. Without ownership it can get its tags like so:

@photo.tag_list << 'a tag' # adds a tag to the existing list
@photo.tag_list = 'a tag' # sets 'a tag' to be the tag of the @post

However, both of these opperations create taggins, whose tagger_id and tagger_type are nil.

In order to have these fields set, you have to use this method:

@user.tag(@photo, on: :tags, with: 'a tag')

Suppose you add this line to the create/update actions of your PhotosController:

@user.tag(@photo, on: :tags, with: params[:photo][:tag_list])

This will create two taggings (one with and one without tagger_id/_type), because params[:photo][:tag_list] is already included in photo_params. So in order to avoid that, just do not whitelist :tag_list.

For Rails 3 - remove :tag_list from attr_accessible.

For Rails 4 - remove :tag_list from params.require(:photo).permit(:tag_list).

At the end your create action might look like this:

def create
  @photo = Photo.new(photo_params) # at this point @photo will not have any tags, because :tag_list is not whitelisted
  current_user.tag(@photo, on: :tags, with: params[:photo][:tag_list])

  if @photo.save
    redirect_to @photo
  else
    render :new
  end
end

Also note that when tagging objects this way you cannot use the usual tag_list method to retrieve the tags of a photo, because it searches for taggings, where tagger_id IS NULL. You have to use instead

@photo.tags_from(@user)

In case your taggable object belongs_to a single user you can also user all_tags_list.

Bridgeboard answered 28/7, 2015 at 21:34 Comment(0)
E
0

Try using delegation:

class User < ActiveRecord::Base
  acts_as_taggable_on
end

class Post < ActiveRecord::Base
  delegate :tag_list, :tag_list=, :to => :user
end

So when you save your posts it sets the tag on the user object directly.

Eleazar answered 1/2, 2011 at 18:1 Comment(0)
K
0

I ended up creating a virtual attribute that runs the User.tag statement:

In my thing.rb Model:

attr_accessible :tags
belongs_to :user
acts_as_taggable

def tags
    self.all_tags_list
end

def tags=(tags)
    user = User.find(self.user_id)
    user.tag(self, :with => tags, :on => :tags, :skip_save => true)
end

The only thing you have to do is then change your views and controllers to update the tag_list to tags and make sure you set the user_id of the thing before the tags of the thing.

Kinship answered 30/3, 2013 at 18:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.