Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails : Using jquery tokeninput (railscast #258) to create new entries

I have successfully been able to implement the addition of new artist entries which was not included in Ryan Bates railscast #258 http://railscasts.com/episodes/258-token-fields

So in other words, a user can enter an artist name which will autocomplete using jquery tokinput. However, I'd like the autocomplete results to only display the artist names which were created by that individual user.

Does this make sense? An better and more understandable example would be for a collection.rb, where users create posts and specify a 'collection' for the post to belong to, but they should only be able to add posts to the collections which they created themselves.

This is the post form with artist_tokens as a virtual attribute:

<%= form_for @post, :validate => true, :html => {:multipart => true} do |f| %>
  <%= render 'shared/error_messages', :object => f.object %>
    <div class="field">
      <%= f.label :title, 'Title:' %><br /> 
      <%= f.text_field :title %><br />

      <%= f.label :artist_tokens, "Artists" %><br />
      <%= f.text_field :artist_tokens, "data-pre" => 
       @post.artists.map(&:attributes).to_json %>

    </div>
  <div class="actions">
    <%= f.submit "Submit" %>
  </div>
<% end %>

This finds the artist by the value entered into the artist_tokens field on the post form, and I also added the option "Add {params[:q]}" to add new entries.

class ArtistsController < ApplicationController

  def index
    @artists = Artist.where("name like ?", "%#{params[:q]}%")
    results = @artists.map(&:attributes)
    results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}

    respond_to do |format|
      format.html
      format.json { render :json => results }
    end
  end

I added the additional code to parse the 'new' entries by id and then create a new artist with them. Then the artist_ids are assigned again.

post.rb
 def artist_tokens=(ids)
    ids.gsub!(/CREATE_(.+?)_END/) do
      Artist.create!(:name => $1).id
    end
    self.artist_ids = ids.split(",")
  end

Everything works great except the ability to narrow the json results by only the current_user's entries. How would I go about doing this? Do I need to store the user_id of the entries creator in the table? How can I do this?

EDIT: associations for models

# app/models/user.rb

class User < ActiveRecord::Base
  has_many :posts
  has_many :artists, :through => :posts

end



# app/models/post.rb

class Post < ActiveRecord::Base

  belongs_to :user
  has_many :artisanships
  has_many :artists, :through => :artisanships

end

# all/models/artist.rb

class Artist < ActiveRecord::Base

  has_many :artisanships
  has_many :users, :through => :artisanships
  has_many :posts, :through => :artisanships

end


# app/models/artisanship.rb

class Artisanships < ActiveRecord::Base

  belongs_to :post
  belongs_to :artist
  has_one :user, :through => :post

end

EDIT: posts_controller.rb

class PostsController < ApplicationController

  before_filter :authenticate_user!, :only => [:create, :edit, :update, :destroy]
  before_filter :authorized_user, :only => [:destroy, :edit, :update]

  def create
    @user = current_user
    @post = current_user.posts.build(params[:post])
    if @post.save
      flash[:success] = "Post created!"
      redirect_to root_path
    else
       @feed_items = current_user.feed.paginate(:per_page => "10", :page => params[:page])
      render 'pages/home'
    end
  end

  def index
    @posts = Post.paginate(:page => params[:page])
  end

  def show
    @post = Post.find(params[:id])
  end

  def edit
    @post = Post.find(params[:id])
  end

  def update
      @post = Post.find(params[:id])
      respond_to do |format|
        if @post.update_attributes(params[:post])
          format.html { redirect_to(post_path(@post), :notice => 'Post was successfully updated.') }
        else
          format.html { render :action => "edit" }  
        end
      end
    end

  def destroy
    @post.destroy
    redirect_to root_path
  end

  def likers
     @title = "Likers"
     @post = Post.find(params[:id])
     @likers = @post.likers.paginate(:page => params[:page])
     render 'show_likers' 
   end

   def search
     if params[:q]
       query = params[:q]
       @search = Post.search do
         keywords query
       end
       @posts = @search.results
     end
   end


private
  def authorized_user
    @post = Post.find(params[:id])
    redirect_to root_path unless current_user?(@post.user)
  end

Edit: Attempted alias_method_chain to set post's user_id attribute first. (didn't work to fix the NULL db entries) referneced from: Rails 3: alias_method_chain still used?

 def attributes_with_user_id_first=(attributes = {})
    # Make sure not to accidentally blank out the important_attribute when none is passed in
    if attributes.include?(:user_id)
      self.user_id = attributes.delete(:user_id)
    end

    self.attributes_without_user_id_first = attributes
  end
  alias_method_chain :attributes=, :user_id_first
like image 274
trying_hal9000 Avatar asked Jul 17 '11 22:07

trying_hal9000


2 Answers

If you don't want to modify anything in your models, then you can do it like this:

def index
  @artists = current_user.posts.join("artisianships").join("artists").
               where("artisianships.post_id = posts.id").
               where("artists.name like ?", "#{params[:q]}").
               select("artists.name as name, artists.id as id")
  results = @artists.map(&:attributes)
  results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}

  respond_to do |format|
    format.html
    format.json { render :json => results }
  end
end

Notice that there are a lot of joins going on here, so not recommended.

To debug why Artist.create!(:name => $1, :user_id => self.user_id) would not work, it would be great if we can see some more code, particularly the action to create a Post

UPDATE: Did you try changing PostController#create to following, although I feel curent code should work as it is,

@post = current_user.posts.build
if @post.update_attributes(params[:post])
   # do something
else
   # do something else
end

I am not a rails pro and don't understand the alias_method_chain so can't comment on why it didn't work.

like image 77
rubish Avatar answered Oct 09 '22 09:10

rubish


if you're using some authentication solution like devise, clearance or authlogic, you probably already have current_user method. Therefore add a column user_id:integer to the Artist model and do the following:

#User model
has_many :artists

#Artist model
belongs_to :user

#Artists controller
def index
    @artists = current_user.artists.where("name like ?", "%#{params[:q]}%") #this answers your question!
    ... blah blah
end

And of course do not forget to set the user_id column when you create an artist.

like image 36
Tadas T Avatar answered Oct 09 '22 09:10

Tadas T