Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the possibility to use scopes with arguments, by passing a array or a lambda via the scopes option and more. #121

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions lib/assets/javascripts/autocomplete-rails-uncompressed.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,22 @@ $(document).ready(function(){
function extractLast( term ) {
return split( term ).pop().replace(/^\s+/,"");
}
function bindClearId() {
var remember_string = this.value;
$(this).bind('keyup.clearId', function(){
if($(this).val().trim() != remember_string.trim()){
$($(this).attr('data-id-element')).val("").change();
$(this).unbind('keyup.clearId');
}
});
}

$(e).autocomplete({
source: function( request, response ) {
$.getJSON( $(e).attr('data-autocomplete'), {
term: extractLast( request.term )
}, function() {
var data = { term: extractLast( request.term ) };
var extraData = $(e).triggerHandler('railsAutocomplete.source', data);
data = extraData ? extraData : data;
$.getJSON( $(e).attr('data-autocomplete'), data, function() {
$(arguments[0]).each(function(i, el) {
var obj = {};
obj[el.id] = el;
Expand All @@ -73,6 +83,9 @@ $(document).ready(function(){
// prevent value inserted on focus
return false;
},
create: function() {
bindClearId.call(this);
},
select: function( event, ui ) {
var terms = split( this.value );
// remove the current input
Expand All @@ -86,7 +99,7 @@ $(document).ready(function(){
} else {
this.value = terms.join("");
if ($(this).attr('data-id-element')) {
$($(this).attr('data-id-element')).val(ui.item.id);
$($(this).attr('data-id-element')).val(ui.item.id).change();
}
if ($(this).attr('data-update-elements')) {
var data = $(this).data(ui.item.id.toString());
Expand All @@ -96,13 +109,7 @@ $(document).ready(function(){
}
}
}
var remember_string = this.value;
$(this).bind('keyup.clearId', function(){
if($(this).val().trim() != remember_string.trim()){
$($(this).attr('data-id-element')).val("");
$(this).unbind('keyup.clearId');
}
});
bindClearId.call(this);
$(this).trigger('railsAutocomplete.select', ui);

return false;
Expand Down
2 changes: 1 addition & 1 deletion lib/assets/javascripts/autocomplete-rails.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 31 additions & 10 deletions lib/rails3-jquery-autocomplete/autocomplete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,30 @@ def self.included(target)
#
module ClassMethods
def autocomplete(object, method, options = {})
define_method("autocomplete_#{object}_#{method}") do
define_method(options[:action_name] ||
"autocomplete_#{object}_#{method}") do

method = options[:column_name] if options.has_key?(:column_name)

term = params[:term]

# allow specifying fully qualified class name for model object
class_name = options[:class_name] || object
parameters = {
:model => get_object(class_name),
:controller => self,
:term => term,
:method => method,
:options => options
}

if term && !term.blank?
#allow specifying fully qualified class name for model object
class_name = options[:class_name] || object
items = get_autocomplete_items(:model => get_object(class_name), \
:options => options, :term => term, :method => method)
items = get_autocomplete_items(parameters)
else
items = {}
end

render :json => json_for_autocomplete(items, options[:display_value] ||= method, options[:extra_data])
render :json => json_for_autocomplete(items, parameters)
end
end
end
Expand All @@ -81,11 +89,24 @@ def get_object(model_sym)
# Can be overriden to show whatever you like
# Hash also includes a key/value pair for each method in extra_data
#
def json_for_autocomplete(items, method, extra_data=[])
def json_for_autocomplete(items, parameters)
display_value = parameters[:options][:display_value] ||= parameters[:method]
label = parameters[:options][:label] ||= parameters[:method]
extra_data = parameters[:options][:extra_data]

items.collect do |item|
hash = {"id" => item.id.to_s, "label" => item.send(method), "value" => item.send(method)}
extra_data.each do |datum|
hash[datum] = item.send(datum)
hash = {
"id" => item.id.to_s,
"label" => item.send(label),
"value" => item.send(display_value)
}

extra_data.each do |k, v|
if v
hash[k] = v.is_a?(Proc) ? v.call(item, parameters) : item.send(v)
else
hash[k] = item.send(k)
end
end if extra_data
# TODO: Come back to remove this if clause when test suite is better
hash
Expand Down
22 changes: 20 additions & 2 deletions lib/rails3-jquery-autocomplete/formtastic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,28 @@ class AutocompleteInput
include Base
include Base::Stringish

def id_field_dom_id(method)
[ builder.custom_namespace,
sanitized_object_name,
dom_index,
method.to_s.gsub(/[\?\/\-]$/, '')
].reject { |x| x.blank? }.join('_')
end

def input_html_options
if options[:id_field]
{ :id_element => '#' + id_field_dom_id(options[:id_field])
}.merge(super)
else
super
end
end

def to_html
input_wrapping do
label_html <<
builder.autocomplete_field(method, options.delete(:url), input_html_options)
result = label_html <<
builder.autocomplete_field(method, options.delete(:url), input_html_options) <<
(options[:id_field] ? builder.hidden_field(options.delete :id_field) : '')
end
end
end
Expand Down
18 changes: 16 additions & 2 deletions lib/rails3-jquery-autocomplete/orm/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ def get_autocomplete_items(parameters)

items = model.scoped

scopes.each { |scope| items = items.send(scope) } unless scopes.empty?
scopes.each do |scope|
if scope.is_a? Proc
items = scope.call(items, parameters)
else
items = items.send(*Array(scope))
end
end unless scopes.empty?

items = items.select(get_autocomplete_select_clause(model, method, options)) unless options[:full_model]
items = items.where(get_autocomplete_where_clause(model, term, method, options)).
Expand All @@ -29,7 +35,15 @@ def get_autocomplete_items(parameters)

def get_autocomplete_select_clause(model, method, options)
table_name = model.table_name
(["#{table_name}.#{model.primary_key}", "#{table_name}.#{method}"] + (options[:extra_data].blank? ? [] : options[:extra_data]))
extra_columns =
if options[:extra_data].nil?
[]
elsif options[:extra_data].is_a? Hash
options[:extra_data].keys
else
options[:extra_data]
end
(["#{table_name}.#{model.primary_key}", "#{table_name}.#{method}"] + extra_columns)
end

def get_autocomplete_where_clause(model, term, method, options)
Expand Down
77 changes: 62 additions & 15 deletions test/lib/rails3-jquery-autocomplete/autocomplete_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,29 +29,76 @@ class AutocompleteTest < Test::Unit::TestCase

context '#json_for_autocomplete' do
should 'parse items to JSON' do
item = mock(Object)
mock(item).send(:name).times(2) { 'Object Name' }
mock(item).id { 1 }
items = [item]
response = self.json_for_autocomplete(items, :name).first
item = generate_mocked_model_instance

parameters = { :method => :name, :options => {} }
items = [item]
response = self.json_for_autocomplete(items, parameters).first

assert_equal response["id"], "1"
assert_equal response["value"], "Object Name"
assert_equal response["label"], "Object Name"
end

context 'with extra data' do
context 'with extra data as an Array' do
should 'add that extra data to result' do
item = mock(Object)
mock(item).send(:name).times(2) { 'Object Name' }
mock(item).id { 1 }
mock(item).send("extra") { 'Object Extra ' }
item = generate_mocked_model_instance
mock(item).send("extra") { 'Object Extra' }

parameters = { :method => :name, :options => { :extra_data => ["extra"] } }
items = [item]
response = self.json_for_autocomplete(items, parameters).first

assert_equal "1" , response["id"]
assert_equal "Object Name" , response["value"]
assert_equal "Object Name" , response["label"]
assert_equal "Object Extra" , response["extra"]
end
end

context 'with extra data as a Hash' do
context 'having lambdas as values' do
should 'add that extra data to result' do
item = generate_mocked_model_instance

parameters = {
:method => :name,
:options => {
:extra_data => {
"extra" => lambda { |item, parameters| 'Lambda Extra' }
}
}
}
items = [item]
response = self.json_for_autocomplete(items, parameters).first

assert_equal "1" , response["id"]
assert_equal "Object Name" , response["value"]
assert_equal "Object Name" , response["label"]
assert_equal "Lambda Extra" , response["extra"]
end
end

context 'having non-lambdas as values' do
should 'add that extra data to result' do
item = generate_mocked_model_instance
mock(item).send("exxtra") { 'Object Exxtra' }

items = [item]
response = self.json_for_autocomplete(items, :name, ["extra"]).first
parameters = {
:method => :name,
:options => { :extra_data => {
"extra" => "exxtra"
}
}
}
items = [item]
response = self.json_for_autocomplete(items, parameters).first

assert_equal "1" , response["id"]
assert_equal "Object Name" , response["value"]
assert_equal "Object Name" , response["label"]
assert_equal "1" , response["id"]
assert_equal "Object Name" , response["value"]
assert_equal "Object Name" , response["label"]
assert_equal 'Object Exxtra' , response["extra"]
end
end
end
end
Expand Down
17 changes: 11 additions & 6 deletions test/lib/rails3-jquery-autocomplete_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,29 @@ class ::Movie ; end
@controller = ActorsController.new
@items = {}
@options = { :display_value => :name }
@parameters = {
:term => nil,
:model => Movie,
:method => :name,
:controller => @controller,
:options => {}
}
end

should 'respond to the action' do
assert_respond_to @controller, :autocomplete_movie_name
end

should 'render the JSON items' do
mock(@controller).get_autocomplete_items({
:model => Movie, :method => :name, :options => @options, :term => "query"
}) { @items }

mock(@controller).json_for_autocomplete(@items, :name, nil)
parameters = @parameters.merge(:term => "query")
mock(@controller).get_autocomplete_items(parameters) { @items }
mock(@controller).json_for_autocomplete(@items, parameters)
get :autocomplete_movie_name, :term => 'query'
end

context 'no term is specified' do
should "render an empty hash" do
mock(@controller).json_for_autocomplete({}, :name, nil)
mock(@controller).json_for_autocomplete({}, @parameters)
get :autocomplete_movie_name
end
end
Expand Down
7 changes: 7 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,12 @@ class Application < ::Rails::Application

class Test::Unit::TestCase
include RR::Adapters::TestUnit

def generate_mocked_model_instance
item = mock(Object)
mock(item).send(:name).times(2) { 'Object Name' }
mock(item).id { 1 }
item
end
end