README.md in token_field-1.1.0 vs README.md in token_field-2.0.0

- old
+ new

@@ -1,24 +1,36 @@ # TokenField -This gem add helper for form_for to Ruby on Rails 3.+ +[![Build Status](https://travis-ci.org/mirrec/token_field.svg?branch=master)](https://travis-ci.org/mirrec/token_field) + +This gem add helper for form_for to Ruby on Rails form_for helper for token input with jquery token input plugin for has_many and belongs_to association http://railscasts.com/episodes/258-token-fields + http://loopj.com/jquery-tokeninput/ + https://github.com/foohey/jquery-tokeninput-rails +Supported Ruby on Rails versions: +* ~> 3.2.1 +* ~> 4.0.1 +* ~> 4.1.1 +* ~> 4.2.1 + helper will render standard text field input with javascript. javascript will change standard input to token field input ## Installation Add this line to your application's Gemfile: - gem "jquery-tokeninput-rails" # dependency - gem "token_field" +```ruby +gem 'jquery-tokeninput-rails' # dependency +gem 'token_field' +``` And then execute: $ bundle @@ -36,172 +48,218 @@ usage for tree we have a model Category like this - class Category < ActiveRecord::Base - attr_accessible :name, :parent_id - has_many :categories, :foreign_key => :parent_id - belongs_to :parent, :foreign_key => :parent_id, :class_name => "Category" +```ruby +class Category < ActiveRecord::Base + attr_accessible :name, :parent_id - # method for converting array of categories to array of hashes in format that token input accepts - def self.token_json(items) - items.map{|i| {:id => i.id, :name => i.name} } - end - end + has_many :categories, :foreign_key => :parent_id + belongs_to :parent, :foreign_key => :parent_id, :class_name => "Category" + # format that token input use + def to_token + {:id => id, :name => name} + end +end +``` + we have action for responding for token input ajax calls, simple autocomplete - class CategoriesController < ApplicationController +```ruby +class CategoriesController < ApplicationController - # action for autocomplete - def token - @categories = Category.where("categories.name like ?", "%#{params[:q]}%") - respond_to do |format| - format.json { render :json => Category.token_json(@categories) } - end - end + # action for autocomplete + def token + categories = Category.where("categories.name like ?", "%#{params[:q]}%") - # rest of the class - end + respond_to do |format| + format.json { render :json => categories.map(&:to_token) } + end + end + # rest of the class +end +``` + in routes we have route for token ajax call - MyApplication::Application.routes.draw do - resources :categories do - collection do - get :token # route for token -> token_categories_path - end - end +```ruby +MyApplication::Application.routes.draw do + resources :categories do + collection do + get :token # route for token -> token_categories_path end + end +end +``` then in view we call token_field with param :model => :category - <%= form_for @category do |f| %> - <%= f.token_field :parent_id, :model => :category, :token_url => token_categories_path %> - <% end %> +```erb +<%= form_for @category do |f| %> + <%= f.token_field :parent_id, :model => :category, :token_url => token_categories_path %> +<% end %> +``` in case model is in namespace for example MyApp you should use :model like this - <%= form_for @category do |f| %> - <%= f.token_field :parent_id, :model => "MyApp::Category", :token_url => token_categories_path %> - <% end %> +```erb +<%= form_for @category do |f| %> + <%= f.token_field :parent_id, :model => "MyApp::Category", :token_url => token_categories_path %> +<% end %> +``` if you need dynamically evaluated url of token input, you can pass inline javascript function to `:token_url` options but with `:token_url_is_function` set to `true` - <%= form_for @category do |f| %> - <%= f.token_field :parent_id, :model => :category, :token_url => "function(){ return '/hello' }", :token_url_is_function => true %> - <% end %> +```erb +<%= form_for @category do |f| %> + <%= f.token_field :parent_id, :model => :category, :token_url => "function(){ return '/hello' }", :token_url_is_function => true %> +<% end %> +``` also own separate function are supported - <%= form_for @category do |f| %> - <%= f.token_field :parent_id, :model => :category, :token_url => "myDynamicUrl", :token_url_is_function => true %> - <% end %> +```erb +<%= form_for @category do |f| %> + <%= f.token_field :parent_id, :model => :category, :token_url => "myDynamicUrl", :token_url_is_function => true %> +<% end %> - <script type="text/javascript"> - function myDynamicUrl() { - return "/my-url/"; - } - </script> +<script type="text/javascript"> + function myDynamicUrl() { + return "/my-url/"; + } +</script> +``` if there would be model Parent, we can omit :model parameter. for example in Product model like this - class Product < ActiveRecord::Base - belongs_to :category - end +```ruby +class Product < ActiveRecord::Base + belongs_to :category +end +``` we can use this code in view - <%= form_for @product do |f| %> - <%= f.token_field :category_id, :token_url => token_categories_path %> - <% end %> +```erb +<%= form_for @product do |f| %> + <%= f.token_field :category_id, :token_url => token_categories_path %> +<% end %> +``` helper will allow you to enter only one element. ### has_many with token_input We can use token_input also for mapping categories to product we will use ActiveRecord method category_ids which be default return array of ids from association in model we have to change category_ids= method like this - class Product < ActiveRecord::Base - has_many :product_has_categories - has_many :categories, :through => :product_has_categories +```ruby +class Product < ActiveRecord::Base + has_many :product_has_categories + has_many :categories, :through => :product_has_categories - alias_method :category_ids_old=, :category_ids= - def category_ids=(ids) - ids = ids.split(",").map(&:to_i) if ids.is_a?(String) - self.category_ids_old=ids - end + alias_method :category_ids_old=, :category_ids= + def category_ids=(ids) + ids = ids.split(",").map(&:to_i) if ids.is_a?(String) + self.category_ids_old=ids + end - # rest of the class... - end + # rest of the class... +end +``` in view you will use attribute category_ids. token input will expected more than one category. so you can enter more than one category. - <%= form_for @product do |f| %> - <%= f.token_field :category_ids, :token_url => token_categories_path %> - <% end %> +```erb +<%= form_for @product do |f| %> + <%= f.token_field :category_ids, :token_url => token_categories_path %> +<% end %> +``` if you want to use multiple token inputs on page, and id of element would be the same, you can user option :append_to_id. - <%= form_for @product do |f| %> - <%= f.token_field :category_ids, :token_url => token_categories_path, :append_to_id => :id %> - <% end %> +```erb +<%= form_for @product do |f| %> + <%= f.token_field :category_ids, :token_url => token_categories_path, :append_to_id => :id %> +<% end %> +``` if @product.id is present and for example "3" it will use this id and generate id of html input like this "product_categories_ids_3" if @product.id is nil id of html input will be "product_categories_ids" other value for :append_id option will be used like this - <%= form_for @product do |f| %> - <%= f.token_field :category_ids, :token_url => token_categories_path, :append_to_id => 5 %> - <% end %> +```erb +<%= form_for @product do |f| %> + <%= f.token_field :category_ids, :token_url => token_categories_path, :append_to_id => 5 %> +<% end %> +``` id of html input will be "product_categories_ids_5" ### SimpleForm support if you use simple_form in your project, TokenInput field will be loaded. you can use it in view like this - <%= simple_form_for(@item) do |f| %> - <%= f.input :category_id, :as => :token, :token_url => token_categories_path %> - <% end %> +```erb +<%= simple_form_for(@item) do |f| %> + <%= f.input :category_id, :as => :token, :token_url => token_categories_path %> +<% end %> +``` all options for token_field helper can be used in simple form helper as well. +## Updating from 1.x to 2.0 + +* Add `#to_token` method to model you are using +* Remove `.token_json` method, we don't use it anymore +* require dsl helper for tests (see below) + ## Testing add support for helpers in rspec - RSpec.configure do |config| - config.include TokenField::Capybara::Dsl - end +```ruby +require 'token_field/capybara/dsl' +RSpec.configure do |config| + config.include TokenField::Capybara::Dsl +end +``` + ### test helpers for capybara filling token input - fill_in "category_parent_id", :with => new_parent.id # standard input - fill_in_token "category_parent_id", :with => "wood" +```ruby +fill_in "category_parent_id", :with => new_parent.id # standard input +fill_in_token "category_parent_id", :with => "wood" +fill_in_token "category_parent_id", :with => "wood", :waiting_call => lambda { sleep(4) } +``` clearing token input - clear_token "category_parent_id" +```ruby +clear_token "category_parent_id" +``` project is covered by integration tests. using rspec, capybara and selenium how to run test - bundle # install dependency - rake db:create - rake db:migrate RAILS_ENV=test - rspec spec/ - +```bash +bundle # install dependency +rake db:create +rake db:migrate RAILS_ENV=test +rspec spec/ +``` ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) \ No newline at end of file