Skip to content

Commit

Permalink
Merge pull request #154 from domcleal/400-prep
Browse files Browse the repository at this point in the history
Bump to 4.0.0
  • Loading branch information
domcleal authored Dec 5, 2016
2 parents 1e502ea + f5aba04 commit bf658f5
Show file tree
Hide file tree
Showing 12 changed files with 31 additions and 252 deletions.
5 changes: 0 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ rvm:
- jruby-head

gemfile:
- Gemfile.activerecord32
- Gemfile.activerecord40
- Gemfile.activerecord41
- Gemfile.activerecord42
- Gemfile.activerecord50

Expand All @@ -36,5 +33,3 @@ matrix:
gemfile: Gemfile.activerecord50
- rvm: "2.1"
gemfile: Gemfile.activerecord50
- rvm: "2.3.1"
gemfile: Gemfile.activerecord32
20 changes: 20 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,26 @@ Please add an entry to the "Unreleased changes" section in your pull requests.

*Nothing yet*

=== Version 4.0.0

- Drop support for Ruby 1.9
- Drop support for ActiveRecord 3, 4.0, and 4.1
- `scoped_search` registration deprecates `:in` in favor of `:relation`, and
`:alias` in favor of `aliases: [..]` to avoid clash with Ruby reserved words.
- `sort` helper: any HTML options must now be given as a keyword argument,
instead of the third argument, i.e. `html_options: {..}`
- `sort` helper now takes hash of `url_options`, defaulting to an unfiltered
`params`. Call `params.permit(..)` and pass in the result to safely generate
URLs and prevent non-sanitized errors under Rails 5.
- Auto completion: remove autocomplete_* JavaScript helpers in favor of jQuery
method provided via asset pipeline. Call `$(..).scopedSearch` on autocomplete
text boxes to activate.
- Auto completion: escape quotes in listed values (#138)
- Add :validator option to definitions to validate user's search inputs
- Fix incorrect association conditions being used on through relations
- Use `#distinct` on Rails 4+
- Test Rails 5.0.x releases, test latest AR adapters

=== Version 3.3.0

- Support for ActiveRecord 5.0
Expand Down
16 changes: 0 additions & 16 deletions Gemfile.activerecord32

This file was deleted.

16 changes: 0 additions & 16 deletions Gemfile.activerecord40

This file was deleted.

16 changes: 0 additions & 16 deletions Gemfile.activerecord41

This file was deleted.

3 changes: 3 additions & 0 deletions README.rdoc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Add the following line in your Gemfile, and run <tt>bundle install</tt>:

gem "scoped_search"

Scoped search 4.x supports Rails 4.2 to 5.0, with Ruby 2.0.0 or higher. Use
previous versions, e.g. 3.x to support older versions of Rails or Ruby.

== Usage

Scoped search requires you to define the fields you want to search in:
Expand Down
9 changes: 2 additions & 7 deletions lib/scoped_search/auto_complete_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def complete_key(name, field, val)
.where(value_conditions(field_name, val))
.select(field_name)
.limit(20)
.send(scope_distinct_method)
.distinct
.map(&field.key_field)
.compact
.map { |f| "#{name}.#{f} " }
Expand All @@ -205,17 +205,12 @@ def complete_value
.where(value_conditions(field.quoted_field, val))
.select(field.quoted_field)
.limit(20)
.send(scope_distinct_method)
.distinct
.map(&field.field)
.compact
.map { |v| v.to_s =~ /\s/ ? "\"#{v.gsub('"', '\"')}\"" : v }
end

# distinct is present on Rails 4.0 and higher, use uniq for 3.2
def scope_distinct_method
ActiveRecord::VERSION::MAJOR >= 4 ? :distinct : :uniq
end

def completer_scope(field)
klass = field.klass
scope = klass.respond_to?(:completer_scope) ? klass.completer_scope(@options) : klass
Expand Down
21 changes: 3 additions & 18 deletions lib/scoped_search/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,7 @@ def column
if klass.columns_hash.has_key?(field.to_s)
klass.columns_hash[field.to_s]
else
if "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}".to_f < 4.1
raise ActiveRecord::UnknownAttributeError, "#{klass.inspect} doesn't have column #{field.inspect}."
else
raise ActiveRecord::UnknownAttributeError.new(klass, field)
end
raise ActiveRecord::UnknownAttributeError.new(klass, field)
end
end
end
Expand Down Expand Up @@ -282,24 +278,13 @@ def register_named_scope! # :nodoc
@klass.scope(:search_for, proc { |query, options|
klass = definition.klass

search_scope = case ActiveRecord::VERSION::MAJOR
when 3
klass.scoped
when 4
(ActiveRecord::VERSION::MINOR < 1) ? klass.where(nil) : klass.all
when 5
klass.all
else
raise ScopedSearch::DefinitionError, 'version ' \
"#{ActiveRecord::VERSION::MAJOR} of activerecord is not supported"
end

search_scope = klass.all
find_options = ScopedSearch::QueryBuilder.build_query(definition, query || '', options || {})
search_scope = search_scope.where(find_options[:conditions]) if find_options[:conditions]
search_scope = search_scope.includes(find_options[:include]) if find_options[:include]
search_scope = search_scope.joins(find_options[:joins]) if find_options[:joins]
search_scope = search_scope.reorder(find_options[:order]) if find_options[:order]
search_scope = search_scope.references(find_options[:include]) if find_options[:include] && ActiveRecord::VERSION::MAJOR >= 4
search_scope = search_scope.references(find_options[:include]) if find_options[:include]

search_scope
})
Expand Down
161 changes: 0 additions & 161 deletions lib/scoped_search/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,171 +64,10 @@ def sort(field, as: nil, default: "ASC", html_options: {}, url_options: params)
a_link(as, html_escape(url_for(url_options)),html_options)
end

# Adds AJAX auto complete functionality to the text input field with the
# DOM ID specified by +field_id+.
#
# Required +options+ is:
# <tt>:url</tt>:: URL to call for auto completion results
# in url_for format.
#
# Additional +options+ are:
# <tt>:update</tt>:: Specifies the DOM ID of the element whose
# innerHTML should be updated with the auto complete
# entries returned by the AJAX request.
# Defaults to <tt>field_id</tt> + '_auto_complete'
# <tt>:with</tt>:: A JavaScript expression specifying the
# parameters for the XMLHttpRequest. This defaults
# to 'fieldname=value'.
# <tt>:frequency</tt>:: Determines the time to wait after the last keystroke
# for the AJAX request to be initiated.
# <tt>:indicator</tt>:: Specifies the DOM ID of an element which will be
# displayed while auto complete is running.
# <tt>:tokens</tt>:: A string or an array of strings containing
# separator tokens for tokenized incremental
# auto completion. Example: <tt>:tokens => ','</tt> would
# allow multiple auto completion entries, separated
# by commas.
# <tt>:min_chars</tt>:: The minimum number of characters that should be
# in the input field before an Ajax call is made
# to the server.
# <tt>:on_hide</tt>:: A Javascript expression that is called when the
# auto completion div is hidden. The expression
# should take two variables: element and update.
# Element is a DOM element for the field, update
# is a DOM element for the div from which the
# innerHTML is replaced.
# <tt>:on_show</tt>:: Like on_hide, only now the expression is called
# then the div is shown.
# <tt>:after_update_element</tt>:: A Javascript expression that is called when the
# user has selected one of the proposed values.
# The expression should take two variables: element and value.
# Element is a DOM element for the field, value
# is the value selected by the user.
# <tt>:select</tt>:: Pick the class of the element from which the value for
# insertion should be extracted. If this is not specified,
# the entire element is used.
# <tt>:method</tt>:: Specifies the HTTP verb to use when the auto completion
# request is made. Defaults to POST.
def auto_complete_field(field_id, options = {})
function = "var #{field_id}_auto_completer = new Ajax.Autocompleter("
function << "'#{field_id}', "
function << "'" + (options[:update] || "#{field_id}_auto_complete") + "', "
function << "'#{url_for(options[:url])}'"

js_options = {}
js_options[:tokens] = array_or_string_for_javascript(options[:tokens]) if options[:tokens]
js_options[:callback] = "function(element, value) { return #{options[:with]} }" if options[:with]
js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator]
js_options[:select] = "'#{options[:select]}'" if options[:select]
js_options[:paramName] = "'#{options[:param_name]}'" if options[:param_name]
js_options[:frequency] = "#{options[:frequency]}" if options[:frequency]
js_options[:method] = "'#{options[:method].to_s}'" if options[:method]

{ :after_update_element => :afterUpdateElement,
:on_show => :onShow, :on_hide => :onHide, :min_chars => :minChars }.each do |k,v|
js_options[v] = options[k] if options[k]
end

function << (', ' + options_for_javascript(js_options) + ')')

javascript_tag(function)
end

def auto_complete_field_jquery(method, url, options = {})
function = <<-JAVASCRIPT
$.widget( "custom.catcomplete", $.ui.autocomplete, {
_renderMenu: function( ul, items ) {
var self = this,
currentCategory = "";
$.each( items, function( index, item ) {
if ( item.category != undefined && item.category != currentCategory ) {
ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
currentCategory = item.category;
}
if ( item.error != undefined ) {
ul.append( "<li class='ui-autocomplete-error'>" + item.error + "</li>" );
}
if( item.completed != undefined ) {
$( "<li></li>" ).data( "item.autocomplete", item )
.append( "<a>" + "<strong class='ui-autocomplete-completed'>" + item.completed + "</strong>" + item.part + "</a>" )
.appendTo( ul );
} else {
if(typeof(self._renderItemData) === "function") {
self._renderItemData( ul, item );
} else {
self._renderItem( ul, item );
}
}
});
}
});
$("##{method}")
.catcomplete({
source: function( request, response ) { $.getJSON( "#{url}", { #{method}: request.term }, response ); },
minLength: #{options[:min_length] || 0},
delay: #{options[:delay] || 200},
select: function(event, ui) { $( this ).catcomplete( "search" , ui.item.value); },
search: function(event, ui) { $(".auto_complete_clear").hide(); },
open: function(event, ui) { $(".auto_complete_clear").show(); }
});
$("##{method}").bind( "focus", function( event ) {
if( $( this )[0].value == "" ) {
$( this ).catcomplete( "search" );
}
});
JAVASCRIPT

javascript_tag(function)
end

def auto_complete_clear_value_button(field_id)
html_options = {:tabindex => '-1',:class=>"auto_complete_clear",:title =>'Clear Search', :onclick=>"document.getElementById('#{field_id}').value = '';"}
a_link("", "#", html_options)
end

def a_link(name, href, html_options)
tag_options = tag_options(html_options)
link = "<a href=\"#{href}\"#{tag_options}>#{name}</a>"
return link.respond_to?(:html_safe) ? link.html_safe : link
end

# Use this method in your view to generate a return for the AJAX auto complete requests.
#
# The auto_complete_result can of course also be called from a view belonging to the
# auto_complete action if you need to decorate it further.
def auto_complete_result(entries, phrase = nil)
return unless entries
items = entries.map { |entry| content_tag("li", phrase ? highlight(entry, phrase) : h(entry)) }
content_tag("ul", items)
end

# Wrapper for text_field with added AJAX auto completion functionality.
#
# In your controller, you'll need to define an action called
# auto_complete_method to respond the AJAX calls,
def auto_complete_field_tag(method, val,tag_options = {}, completion_options = {})
auto_completer_options = { :url => { :action => "auto_complete_#{method}" } }.update(completion_options)
options = tag_options.merge(:class => "auto_complete_input " << tag_options[:class].to_s)
text_field_tag(method, val,options) +
auto_complete_clear_value_button(method) +
content_tag("div", "", :id => "#{method}_auto_complete", :class => "auto_complete") +
auto_complete_field(method, auto_completer_options)
end

# Wrapper for text_field with added JQuery auto completion functionality.
#
# In your controller, you'll need to define an action called
# auto_complete_method to respond the JQuery calls,
def auto_complete_field_tag_jquery(method, val,tag_options = {}, completion_options = {})
url = url_for(:action => "auto_complete_#{method}", :filter => completion_options[:filter])
options = tag_options.merge(:class => "auto_complete_input " << tag_options[:class].to_s)
text_field_tag(method, val, options) + auto_complete_clear_value_button(method) +
auto_complete_field_jquery(method, url, completion_options)
end
deprecate :auto_complete_field_tag_jquery, :auto_complete_field_tag, :auto_complete_result

end
end
2 changes: 1 addition & 1 deletion lib/scoped_search/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module ScopedSearch
VERSION = "3.3.0"
VERSION = "4.0.0"
end
4 changes: 2 additions & 2 deletions scoped_search.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ Gem::Specification.new do |gem|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]

gem.required_ruby_version = '>= 1.9.3'
gem.add_runtime_dependency('activerecord', '>= 3.2.0')
gem.required_ruby_version = '>= 2.0.0'
gem.add_runtime_dependency('activerecord', '>= 4.2.0')
gem.add_development_dependency('rspec', '~> 3.0')
gem.add_development_dependency('rake')

Expand Down
10 changes: 0 additions & 10 deletions spec/integration/ordinal_querying_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,16 +114,6 @@
@class.search_for('date = 09-01-02').length.should == 1
end

if RUBY_VERSION.to_f == 1.8
it "should accept MM/DD/YY as date format" do
@class.search_for('date = 01/02/09').length.should == 1
end

it "should accept MM/DD/YYYY as date format" do
@class.search_for('date = 01/02/2009').length.should == 1
end
end

it "should accept YYYY/MM/DD as date format" do
@class.search_for('date = 2009/01/02').length.should == 1
end
Expand Down

0 comments on commit bf658f5

Please sign in to comment.