rails4-autocomplete 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +374 -0
  4. data/Rakefile +21 -0
  5. data/lib/assets/javascripts/autocomplete-rails-uncompressed.js +136 -0
  6. data/lib/assets/javascripts/autocomplete-rails.js +16 -0
  7. data/lib/cucumber/autocomplete.rb +6 -0
  8. data/lib/generators/autocomplete/install_generator.rb +14 -0
  9. data/lib/generators/autocomplete/uncompressed_generator.rb +14 -0
  10. data/lib/rails4-autocomplete.rb +23 -0
  11. data/lib/rails4-autocomplete/autocomplete.rb +96 -0
  12. data/lib/rails4-autocomplete/form_helper.rb +47 -0
  13. data/lib/rails4-autocomplete/formtastic.rb +41 -0
  14. data/lib/rails4-autocomplete/formtastic_plugin.rb +44 -0
  15. data/lib/rails4-autocomplete/orm.rb +8 -0
  16. data/lib/rails4-autocomplete/orm/active_record.rb +52 -0
  17. data/lib/rails4-autocomplete/orm/mongo_mapper.rb +30 -0
  18. data/lib/rails4-autocomplete/orm/mongoid.rb +34 -0
  19. data/lib/rails4-autocomplete/rails/engine.rb +5 -0
  20. data/lib/rails4-autocomplete/simple_form_plugin.rb +84 -0
  21. data/lib/rails4-autocomplete/version.rb +3 -0
  22. data/lib/steak/autocomplete.rb +12 -0
  23. data/test/form_helper_test.rb +25 -0
  24. data/test/generators/autocomplete/install_generator_test.rb +25 -0
  25. data/test/generators/autocomplete/uncompressed_generator_test.rb +25 -0
  26. data/test/lib/rails3-jquery-autocomplete/autocomplete_test.rb +59 -0
  27. data/test/lib/rails3-jquery-autocomplete/orm/active_record_test.rb +145 -0
  28. data/test/lib/rails3-jquery-autocomplete/orm/mongo_mapper_test.rb +60 -0
  29. data/test/lib/rails3-jquery-autocomplete/orm/mongoid_test.rb +60 -0
  30. data/test/lib/rails3-jquery-autocomplete/simple_form_plugin_test.rb +33 -0
  31. data/test/lib/rails3-jquery-autocomplete_test.rb +38 -0
  32. data/test/test_helper.rb +36 -0
  33. data/test/view_test_helper.rb +108 -0
  34. metadata +268 -0
@@ -0,0 +1,16 @@
1
+ /*
2
+ * Unobtrusive autocomplete
3
+ *
4
+ * To use it, you just have to include the HTML attribute autocomplete
5
+ * with the autocomplete URL as the value
6
+ *
7
+ * Example:
8
+ * <input type="text" data-autocomplete="/url/to/autocomplete">
9
+ *
10
+ * Optionally, you can use a jQuery selector to specify a field that can
11
+ * be updated with the element id whenever you find a matching value
12
+ *
13
+ * Example:
14
+ * <input type="text" data-autocomplete="/url/to/autocomplete" data-id-element="#id_field">
15
+ */
16
+ (function(e){var t=null;e.fn.railsAutocomplete=function(){var t=function(){this.railsAutoCompleter||(this.railsAutoCompleter=new e.railsAutocomplete(this))};return e.fn.on!==undefined?$(document).on("focus",this.selector,t):this.live("focus",t)},e.railsAutocomplete=function(e){_e=e,this.init(_e)},e.railsAutocomplete.fn=e.railsAutocomplete.prototype={railsAutocomplete:"0.0.1"},e.railsAutocomplete.fn.extend=e.railsAutocomplete.extend=e.extend,e.railsAutocomplete.fn.extend({init:function(t){function n(e){return e.split(t.delimiter)}function r(e){return n(e).pop().replace(/^\s+/,"")}t.delimiter=e(t).attr("data-delimiter")||null,e(t).autocomplete({source:function(n,i){e.getJSON(e(t).attr("data-autocomplete"),{term:r(n.term)},function(){arguments[0].length==0&&(arguments[0]=[],arguments[0][0]={id:"",label:"no existing match"}),e(arguments[0]).each(function(n,r){var i={};i[r.id]=r,e(t).data(i)}),i.apply(null,arguments)})},change:function(t,n){if(e(e(this).attr("data-id-element")).val()=="")return;e(e(this).attr("data-id-element")).val(n.item?n.item.id:"");var r=e.parseJSON(e(this).attr("data-update-elements")),i=n.item?e(this).data(n.item.id.toString()):{};if(r&&e(r["id"]).val()=="")return;for(var s in r)e(r[s]).val(n.item?i[s]:"")},search:function(){var e=r(this.value);if(e.length<2)return!1},focus:function(){return!1},select:function(r,i){var s=n(this.value);s.pop(),s.push(i.item.value);if(t.delimiter!=null)s.push(""),this.value=s.join(t.delimiter);else{this.value=s.join(""),e(this).attr("data-id-element")&&e(e(this).attr("data-id-element")).val(i.item.id);if(e(this).attr("data-update-elements")){var o=e(this).data(i.item.id.toString()),u=e.parseJSON(e(this).attr("data-update-elements"));for(var a in u)e(u[a]).val(o[a])}}var f=this.value;return e(this).bind("keyup.clearId",function(){e(this).val().trim()!=f.trim()&&(e(e(this).attr("data-id-element")).val(""),e(this).unbind("keyup.clearId"))}),e(t).trigger("railsAutocomplete.select",i),!1}})}}),e(document).ready(function(){e("input[data-autocomplete]").railsAutocomplete()})})(jQuery);
@@ -0,0 +1,6 @@
1
+ Given /^I choose "([^"]*)" in the autocomplete list$/ do |text|
2
+ page.execute_script %Q{ $('input[data-autocomplete]').trigger("focus") }
3
+ page.execute_script %Q{ $('input[data-autocomplete]').trigger("keydown") }
4
+ sleep 1
5
+ page.execute_script %Q{ $('.ui-menu-item a:contains("#{text}")').trigger("mouseenter").trigger("click"); }
6
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Autocomplete
4
+ class InstallGenerator < Rails::Generators::Base
5
+ def install
6
+ # Copy the unobtrusive JS file
7
+ copy_file('autocomplete-rails.js', 'public/javascripts/autocomplete-rails.js')
8
+ end
9
+
10
+ def self.source_root
11
+ File.join(File.dirname(__FILE__), '..', '..', 'assets', 'javascripts')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails/generators'
2
+
3
+ module Autocomplete
4
+ class UncompressedGenerator < Rails::Generators::Base
5
+ def install
6
+ # Copy the unobtrusive JS file
7
+ copy_file('autocomplete-rails-uncompressed.js', 'public/javascripts/autocomplete-rails.js')
8
+ end
9
+
10
+ def self.source_root
11
+ File.join(File.dirname(__FILE__), '..', '..', 'assets', 'javascripts')
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ require 'rails4-autocomplete/form_helper'
2
+ require 'rails4-autocomplete/autocomplete'
3
+
4
+ module Rails4Autocomplete
5
+ autoload :Orm , 'rails4-autocomplete/orm'
6
+ autoload :FormtasticPlugin , 'rails4-autocomplete/formtastic_plugin'
7
+
8
+ unless ::Rails.version < "3.1"
9
+ require 'rails4-autocomplete/rails/engine'
10
+ end
11
+ end
12
+
13
+ class ActionController::Base
14
+ include Rails4Autocomplete::Autocomplete
15
+ end
16
+
17
+ require 'rails4-autocomplete/formtastic'
18
+
19
+ begin
20
+ require 'simple_form'
21
+ require 'rails4-autocomplete/simple_form_plugin'
22
+ rescue LoadError
23
+ end
@@ -0,0 +1,96 @@
1
+ module Rails4Autocomplete
2
+ module Autocomplete
3
+ def self.included(target)
4
+ target.extend Rails4Autocomplete::Autocomplete::ClassMethods
5
+
6
+ if defined?(Mongoid::Document)
7
+ target.send :include, Rails4Autocomplete::Orm::Mongoid
8
+ elsif defined?(MongoMapper::Document)
9
+ target.send :include, Rails4Autocomplete::Orm::MongoMapper
10
+ else
11
+ target.send :include, Rails4Autocomplete::Orm::ActiveRecord
12
+ end
13
+ end
14
+
15
+ #
16
+ # Usage:
17
+ #
18
+ # class ProductsController < Admin::BaseController
19
+ # autocomplete :brand, :name
20
+ # end
21
+ #
22
+ # This will magically generate an action autocomplete_brand_name, so,
23
+ # don't forget to add it on your routes file
24
+ #
25
+ # resources :products do
26
+ # get :autocomplete_brand_name, :on => :collection
27
+ # end
28
+ #
29
+ # Now, on your view, all you have to do is have a text field like:
30
+ #
31
+ # f.text_field :brand_name, :autocomplete => autocomplete_brand_name_products_path
32
+ #
33
+ #
34
+ # Yajl is used by default to encode results, if you want to use a different encoder
35
+ # you can specify your custom encoder via block
36
+ #
37
+ # class ProductsController < Admin::BaseController
38
+ # autocomplete :brand, :name do |items|
39
+ # CustomJSONEncoder.encode(items)
40
+ # end
41
+ # end
42
+ #
43
+ module ClassMethods
44
+ def autocomplete(object, method, options = {})
45
+ define_method("autocomplete_#{object}_#{method}") do
46
+
47
+ method = options[:column_name] if options.has_key?(:column_name)
48
+
49
+ term = params[:term]
50
+
51
+ if term && !term.blank?
52
+ #allow specifying fully qualified class name for model object
53
+ class_name = options[:class_name] || object
54
+ items = get_autocomplete_items(:model => get_object(class_name), \
55
+ :options => options, :term => term, :method => method)
56
+ else
57
+ items = {}
58
+ end
59
+
60
+ render :json => json_for_autocomplete(items, options[:display_value] ||= method, options[:extra_data])
61
+ end
62
+ end
63
+ end
64
+
65
+ # Returns a limit that will be used on the query
66
+ def get_autocomplete_limit(options)
67
+ options[:limit] ||= 10
68
+ end
69
+
70
+ # Returns parameter model_sym as a constant
71
+ #
72
+ # get_object(:actor)
73
+ # # returns a Actor constant supposing it is already defined
74
+ #
75
+ def get_object(model_sym)
76
+ object = model_sym.to_s.camelize.constantize
77
+ end
78
+
79
+ #
80
+ # Returns a hash with three keys actually used by the Autocomplete jQuery-ui
81
+ # Can be overriden to show whatever you like
82
+ # Hash also includes a key/value pair for each method in extra_data
83
+ #
84
+ def json_for_autocomplete(items, method, extra_data=[])
85
+ items.collect do |item|
86
+ hash = {"id" => item.id.to_s, "label" => item.send(method), "value" => item.send(method)}
87
+ extra_data.each do |datum|
88
+ hash[datum] = item.send(datum)
89
+ end if extra_data
90
+ # TODO: Come back to remove this if clause when test suite is better
91
+ hash
92
+ end
93
+ end
94
+ end
95
+ end
96
+
@@ -0,0 +1,47 @@
1
+ module ActionView
2
+ module Helpers
3
+ module FormHelper
4
+ # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) and
5
+ # that is populated with jQuery's autocomplete plugin.
6
+ #
7
+ # ==== Examples
8
+ # autocomplete_field(:post, :title, author_autocomplete_path, :size => 20)
9
+ # # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" data-autocomplete="author/autocomplete"/>
10
+ #
11
+ def autocomplete_field(object_name, method, source, options ={})
12
+ options["data-autocomplete"] = source
13
+ text_field(object_name, method, rewrite_autocomplete_option(options))
14
+ end
15
+ end
16
+
17
+ module FormTagHelper
18
+ # Creates a standard text field that can be populated with jQuery's autocomplete plugin
19
+ #
20
+ # ==== Examples
21
+ # autocomplete_field_tag 'address', '', address_autocomplete_path, :size => 75
22
+ # # => <input id="address" name="address" size="75" type="text" value="" data-autocomplete="address/autocomplete"/>
23
+ #
24
+ def autocomplete_field_tag(name, value, source, options ={})
25
+ options["data-autocomplete"] = source
26
+ text_field_tag(name, value, rewrite_autocomplete_option(options))
27
+ end
28
+ end
29
+
30
+ #
31
+ # Method used to rename the autocomplete key to a more standard
32
+ # data-autocomplete key
33
+ #
34
+ private
35
+ def rewrite_autocomplete_option(options)
36
+ options["data-update-elements"] = JSON.generate(options.delete :update_elements) if options[:update_elements]
37
+ options["data-id-element"] = options.delete :id_element if options[:id_element]
38
+ options
39
+ end
40
+ end
41
+ end
42
+
43
+ class ActionView::Helpers::FormBuilder #:nodoc:
44
+ def autocomplete_field(method, source, options = {})
45
+ @template.autocomplete_field(@object_name, method, source, objectify_options(options))
46
+ end
47
+ end
@@ -0,0 +1,41 @@
1
+ #
2
+ # Load the formtastic plugin if using Formtastic
3
+ #
4
+ begin
5
+ require 'formtastic'
6
+ begin
7
+ require "formtastic/version"
8
+ rescue LoadError
9
+ end
10
+
11
+ if defined?(Formtastic::VERSION)
12
+ #
13
+ # Formtastic 2.x
14
+ #
15
+
16
+ module Formtastic
17
+ module Inputs
18
+ class AutocompleteInput
19
+ include Base
20
+ include Base::Stringish
21
+
22
+ def to_html
23
+ input_wrapping do
24
+ label_html <<
25
+ builder.autocomplete_field(method, options.delete(:url), input_html_options)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ else
32
+
33
+ #
34
+ # Formtastic 1.x
35
+ #
36
+ class Formtastic::SemanticFormBuilder < ActionView::Helpers::FormBuilder
37
+ include Rails4Autocomplete::FormtasticPlugin
38
+ end
39
+ end
40
+ rescue LoadError
41
+ end
@@ -0,0 +1,44 @@
1
+ module Rails4Autocomplete
2
+ module FormtasticPlugin
3
+ def autocomplete_input(method, options = {})
4
+ if options.key?(:selected) || options.key?(:checked) || options.key?(:default)
5
+ ::ActiveSupport::Deprecation.warn(
6
+ "The :selected, :checked (and :default) options are deprecated in Formtastic and will be removed from 1.0. " <<
7
+ "Please set default values in your models (using an after_initialize callback) or in your controller set-up. " <<
8
+ "See http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html for more information.", caller)
9
+ end
10
+
11
+ options[:required] = method_required?(method) unless options.key?(:required)
12
+ options[:as] ||= "autocompleted_string"
13
+
14
+ html_class = [ options[:as], (options[:required] ? :required : :optional) ]
15
+ html_class << 'error' if @object && @object.respond_to?(:errors) && !@object.errors[method.to_sym].blank?
16
+
17
+ wrapper_html = options.delete(:wrapper_html) || {}
18
+ wrapper_html[:id] ||= generate_html_id(method)
19
+ wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ')
20
+
21
+ if options[:input_html] && options[:input_html][:id]
22
+ options[:label_html] ||= {}
23
+ options[:label_html][:for] ||= options[:input_html][:id]
24
+ end
25
+
26
+ input_parts = self.class.inline_order.dup
27
+ input_parts = input_parts - [:errors, :hints] if options[:as] == :hidden
28
+
29
+ list_item_content = input_parts.map do |type|
30
+ send(:"inline_#{type}_for", method, options)
31
+ end.compact.join("\n")
32
+
33
+ return template.content_tag(:li, Formtastic::Util.html_safe(list_item_content), wrapper_html)
34
+ end
35
+
36
+ alias_method :autocompleted_input, :autocomplete_input
37
+
38
+
39
+ protected
40
+ def autocompleted_string_input(method, options)
41
+ self.label(method, options_for_label(options)) << autocomplete_field(method, options.delete(:url), options)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,8 @@
1
+ module Rails4Autocomplete
2
+ module Orm
3
+ autoload :ActiveRecord , 'rails4-autocomplete/orm/active_record'
4
+ autoload :Mongoid , 'rails4-autocomplete/orm/mongoid'
5
+ autoload :MongoMapper , 'rails4-autocomplete/orm/mongo_mapper'
6
+ end
7
+ end
8
+
@@ -0,0 +1,52 @@
1
+ module Rails4Autocomplete
2
+ module Orm
3
+ module ActiveRecord
4
+ def get_autocomplete_order(method, options, model=nil)
5
+ order = options[:order]
6
+
7
+ table_prefix = model ? "#{model.table_name}." : ""
8
+ order || "#{table_prefix}#{method} ASC"
9
+ end
10
+
11
+ def get_autocomplete_items(parameters)
12
+ model = parameters[:model]
13
+ term = parameters[:term]
14
+ method = parameters[:method]
15
+ options = parameters[:options]
16
+ scopes = Array(options[:scopes])
17
+ where = options[:where]
18
+ limit = get_autocomplete_limit(options)
19
+ order = get_autocomplete_order(method, options, model)
20
+
21
+
22
+ items = model.all
23
+
24
+ scopes.each { |scope| items = items.send(scope) } unless scopes.empty?
25
+
26
+ items = items.select(get_autocomplete_select_clause(model, method, options)) unless options[:full_model]
27
+ items = items.where(get_autocomplete_where_clause(model, term, method, options)).
28
+ limit(limit).order(order)
29
+ items = items.where(where) unless where.blank?
30
+
31
+ items
32
+ end
33
+
34
+ def get_autocomplete_select_clause(model, method, options)
35
+ table_name = model.table_name
36
+ (["#{table_name}.#{model.primary_key}", "#{table_name}.#{method}"] + (options[:extra_data].blank? ? [] : options[:extra_data]))
37
+ end
38
+
39
+ def get_autocomplete_where_clause(model, term, method, options)
40
+ table_name = model.table_name
41
+ is_full_search = options[:full]
42
+ like_clause = (postgres?(model) ? 'ILIKE' : 'LIKE')
43
+ ["LOWER(#{table_name}.#{method}) #{like_clause} ?", "#{(is_full_search ? '%' : '')}#{term.downcase}%"]
44
+ end
45
+
46
+ def postgres?(model)
47
+ # Figure out if this particular model uses the PostgreSQL adapter
48
+ model.connection.class.to_s.match(/PostgreSQLAdapter/)
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,30 @@
1
+ module Rails4Autocomplete
2
+ module Orm
3
+ module MongoMapper
4
+ def get_autocomplete_order(method, options, model=nil)
5
+ order = options[:order]
6
+ if order
7
+ order.split(',').collect do |fields|
8
+ sfields = fields.split
9
+ [sfields[0].downcase.to_sym, sfields[1].downcase.to_sym]
10
+ end
11
+ else
12
+ [[method.to_sym, :asc]]
13
+ end
14
+ end
15
+
16
+ def get_autocomplete_items(parameters)
17
+ model = parameters[:model]
18
+ method = parameters[:method]
19
+ options = parameters[:options]
20
+ is_full_search = options[:full]
21
+ term = parameters[:term]
22
+ limit = get_autocomplete_limit(options)
23
+ order = get_autocomplete_order(method, options)
24
+
25
+ search = (is_full_search ? '.*' : '^') + term + '.*'
26
+ items = model.where(method.to_sym => /#{search}/i).limit(limit).sort(order)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ module Rails4Autocomplete
2
+ module Orm
3
+ module Mongoid
4
+ def get_autocomplete_order(method, options, model=nil)
5
+ order = options[:order]
6
+ if order
7
+ order.split(',').collect do |fields|
8
+ sfields = fields.split
9
+ [sfields[0].downcase.to_sym, sfields[1].downcase.to_sym]
10
+ end
11
+ else
12
+ [[method.to_sym, :asc]]
13
+ end
14
+ end
15
+
16
+ def get_autocomplete_items(parameters)
17
+ model = parameters[:model]
18
+ method = parameters[:method]
19
+ options = parameters[:options]
20
+ is_full_search = options[:full]
21
+ term = parameters[:term]
22
+ limit = get_autocomplete_limit(options)
23
+ order = get_autocomplete_order(method, options)
24
+
25
+ if is_full_search
26
+ search = '.*' + Regexp.escape(term) + '.*'
27
+ else
28
+ search = '^' + Regexp.escape(term)
29
+ end
30
+ items = model.where(method.to_sym => /#{search}/i).limit(limit).order_by(order)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Rails4Autocomplete
2
+ module Rails
3
+ class Engine < ::Rails::Engine ; end
4
+ end
5
+ end