Search code examples
ruby-on-railspostgresqltaggingacts-as-taggable-onjquery-tokeninput

Tagging from Scratch: the Querying Database issue


@saverio was successful on answer this database query question on Tagging from Scratch: the Tag Cloud Issue

Now I'm trying to connect the tagging system with the jQuery-tokenInput to Create and Find tags dynamically as on http://railscasts.com/episodes/258-token-fields-revised.

  • My guess is that is a Query problem to the Posgresql Database.
  • I've got Postgresql correctly installed
  • jQuery-tokenInput is on its place on Application.js //= require jquery.tokeninput
  • Somehow it can load the tags from what was already on the database, but it fails querying the same words dynamically as listed below on pictures.js.coffee code.

Following all the relevant scope:

pictures.js.coffee

jQuery ->
  $('#picture_tag_tokens').tokenInput '/tags.json'
  theme: 'facebook'
  prePopulate: $('#picture_tag_tokens').data('load')

/views/pictures/_form

<div class="field">
  <%= f.label :tag_tokens, "Tags (separated by commas)" %><br />
  <%= f.text_field :tag_tokens, data: {load: @picture.tags} %>
</div>

Here my logic get lost a bit

/models/picture.rb

class Picture < ActiveRecord::Base
  attr_accessible :description, :title, :tag_tokens
  has_many :taggings
  has_many :tags, through: :taggings
  attr_reader :tag_tokens

  #The **below** is the relevant part for the #view/pictures/_form
  def tag_tokens=(tokens)
    self.tag_ids = Tag.ids_from_tokens(tokens)
  end

  def self.tagged_with(name)
    Tag.find_by_name!(name).pictures
  end

  def self.tag_counts
    Tag.select("tags.*, count(taggings.tag_id) as count").
    joins(:taggings).group("tags.id")
  end

  def tag_list
    tags.map(&:name).join(", ")
  end

  def tag_list=(names)
    self.tags = names.split(",").map do |n|
      Tag.where(name: n.strip).first_or_create!
    end
  end
end

Below I could figure out that I'm not being able to query the Database

/models/tag.rb

class Tag < ActiveRecord::Base
  attr_accessible :name
  has_many :taggings
  has_many :pictures, through: :taggings

  def self.tokens(query)
    tags = where("name like ?", "%#{query}%")
    if tags.empty?
      [{id: "<<<#{query}>>>", name: "New: \"#{query}\""}]
    else
      tags
    end
  end

  def self.ids_from_tokens(tokens)
    tokens.gsub!(/<<<(.+?)>>>/) { create!(name: $1).id }
    tokens.split(',')
  end
end

And so was how I set my Tags controller behavior

#controllers/tags_controller.rb

class TagsController < ApplicationController
  def index
   @tags = Tag.all
   respond_to do |format|
     format.html
     format.json { render json: @tags.tokens(params[:q]) }
   end
  end
end

So, Why I can't Query the Postgresql and I'm not able to Create or Find Dynamically?


Solution

  • I incorporated a solution that's easy enough: using the Gem ActsAsTaggableOn with jQuery Tokeninput

    If you go through the algorithm below, you will figure out that the question above was just a misconception about the configuration between the Tag System and the jQuery. the following steps will help you in your path:

    1 Put the jQuery Tokeninput files in the right places:

    Vendor > assets > javascripts > jquery-tokeninput.js

    Vendor > assets > stylesheets > token-input-facebook.css (and 2 others)

    2 on the file in apps > assets > javascripts > application.js wright this line before //= require_tree . this make the javascript work

    //= require jquery.tokeninput

    3 on the file in apps > assets > stylesheets > application.css.scss wright before *= require_tree . this make the css work

    *= require token-input-facebook

    4 Put the gem 'acts-as-taggable-on' on your gemfile and run bundle install and the very same steps as here.

    5 make a coustom route in routes.rb for a tag controller index (we are about to define) to enable the json property

    get '/tag.json', to: 'tag#index', as: :pictures_tags

    The Following Code and the above allows us to talke properly to the database

    6 run a rails g tag controller index and on this file wright the following:

    #tag_controller.rb

    def index

         query = params[:q]
         query = query.chomp(" ").downcase
         @tags = ActsAsTaggableOn::Tag.where("tags.name LIKE '%#{query}%' OR tags.name LIKE '#{query}'")
    
    if @tags.empty?
        ActsAsTaggableOn::Tag.find_or_create_by_name_and_id(id: "#{query}", name: "#{query}")
    else 
        respond_to do |format|
            format.html
            format.json { render json: @tags.collect{ |tag| {id: tag.name, name: tag.name } } }
        end
     end            
    

    end

    7 and finally glue everything with the code you will place on app > assets > javascript > your_file_with_tags.js.coffee

    #your_file_with_tags.js.coffee

    jQuery ->

    $('#field_with_tag_list').tokenInput '/tag.json'
    
        theme: 'facebook'
    
        prePopulate: $('#field_with_tag_list').data('load')
    
        preventDuplicates: true
    
        resultsLimit: 3
    
        tokenLimit: 7
    
        tokenDelimiter: ","
    
        searchingText: "hit space for New Tag"
    

    PS: #field_with_tag_list is in your view(usualy _form partial) that you generates to impute the tags like this: (#controller_tag_list) i.e. #pictures_tag_list

    <div class="field">

    `<%= f.label :tag_list %><br />`
    
    `<%= f.text_field :tag_list, data: {load: @picture.tag_list.map {|tag| { id: tag, name: tag }}} %>`
    

    </div>

    All these are experimented code I'm sure that will help you =]