elastic_ransack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f63dff989c1fc3d17945abaa3bdea9dbde2d5481
4
+ data.tar.gz: 567674e6eb6459ca4bda929cb9829c8698741289
5
+ SHA512:
6
+ metadata.gz: 007262a391eb9046e8acdd188fd60b5c890a81f6f74784ac0beeae71d5392dcbbfe239b7c99058944cad5d851bc840b779ab87628b47159368bc843d4b0ae13c
7
+ data.tar.gz: e56a8010464704f31371314aa69f70b03b111ad483437ae68b4738dfee974a5876bcbd2a7b92d5c889fbe19da1be5e7232e90a248dd6961b83ae22eb20203000
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ .idea
20
+ .rvmrc
21
+ .rspec
22
+ TODO
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in elastic_ransack.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alex Leschenko
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,47 @@
1
+ # ElasticRansack
2
+
3
+ ElasticRansack provides condition predicate searching to your elasticsearch models like ransack or meta_search gems.
4
+ Your can just create serach form with `name_cont` or `created_at_gt` fields and elastic_ransack build a search query for you.
5
+ It is compatible with most of the Ransack helper methods and predicates.
6
+
7
+ It uses [Tire](https://github.com/karmi/tire) and [Elasticsearch](http://www.elasticsearch.org/) for searching.
8
+
9
+ Inspired by [Ransack](https://github.com/ernie/ransack) gem.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'elastic_ransack'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install elastic_ransack
24
+
25
+ ## Usage
26
+
27
+ Include ElasticRansack extension to your model:
28
+
29
+ ```ruby
30
+ class User < ActiveRecord::Base
31
+ include ElasticRansack::Model
32
+ end
33
+ ```
34
+
35
+ Search with `elastic_ransack` method. It return `Tire::Results::Collection` instance:
36
+
37
+ ```ruby
38
+ User.elastic_ransack({name_cont: 'alex', role_id_eq: 1, state_id_in: [2, 3], created_at_gt: 1.day.ago})
39
+ ```
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'elastic_ransack/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'elastic_ransack'
8
+ spec.version = ElasticRansack::VERSION
9
+ spec.authors = ['Alex Leschenko']
10
+ spec.email = %w(leschenko.al@gmail.com)
11
+ spec.summary = %q{Conditions searching adapter for elasticsearch models like ransack}
12
+ spec.description = %q{Conditions searching using predicates such as 'name_cont' or 'created_at_gt' for elasticsearch models}
13
+ spec.homepage = 'https://github.com/leschenko/elastic_ransack'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = %w(lib)
20
+
21
+ spec.add_dependency 'tire', '~> 0.6.0'
22
+
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec'
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ end
@@ -0,0 +1,45 @@
1
+ require 'tire'
2
+ require 'elastic_ransack/version'
3
+ require 'elastic_ransack/core_ext'
4
+ require 'elastic_ransack/predicate'
5
+ require 'elastic_ransack/naming'
6
+ require 'elastic_ransack/search'
7
+ require 'elastic_ransack/model'
8
+
9
+ module ElasticRansack
10
+ mattr_accessor :predicates
11
+ self.predicates = []
12
+
13
+ BASE_PREDICATES = [
14
+ ['eq', {query: proc { |attr, v| {term: {attr => v}} }}],
15
+ ['in', {query: proc { |attr, v| {terms: {attr => ElasticRansack.val_to_array(v)}} }}],
16
+ ['in_all', {query: proc { |attr, v| {terms: {attr => ElasticRansack.val_to_array(v), execution: 'and'}} }}],
17
+ ['gt', {query: proc { |attr, v| {range: {attr => {gt: v}}} }}],
18
+ ['lt', {query: proc { |attr, v| {range: {attr => {lt: v}}} }}],
19
+ ['gteq', {query: proc { |attr, v| {range: {attr => {gte: v}}} }}],
20
+ ['lteq', {query: proc { |attr, v| {range: {attr => {lte: v}}} }}]
21
+ ]
22
+
23
+ class << self
24
+ def setup
25
+ yield self
26
+ end
27
+
28
+ def add_predicate(*args)
29
+ self.predicates << Predicate.new(*args)
30
+ end
31
+
32
+ def val_to_array(val)
33
+ return [] unless val
34
+ return val if val.is_a?(Array)
35
+ val.to_s.split(',').map(&:to_i).reject(&:zero?)
36
+ end
37
+ end
38
+ end
39
+
40
+ ElasticRansack.setup do |config|
41
+ ElasticRansack::BASE_PREDICATES.each do |args|
42
+ config.add_predicate *args
43
+ end
44
+ end
45
+
@@ -0,0 +1,9 @@
1
+ class String
2
+ # http://lucene.apache.org/core/old_versioned_docs/versions/2_9_1/queryparsersyntax.html
3
+ LUCENE_ESCAPE_REGEX = /(\+|-|&&|\|\||!|\(|\)|{|}|\[|\]|\^|"|~|\*|\?|:|\\|\/)/
4
+
5
+
6
+ def lucene_escape
7
+ self.gsub(LUCENE_ESCAPE_REGEX, "\\\\\\1")
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ module ElasticRansack
2
+ module Model
3
+
4
+ def self.included(base)
5
+ base.send :extend, ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ # === options
11
+ # [:s]
12
+ # sorting column
13
+ # can contain sort mode separated by space
14
+ # example: 'id desc'
15
+ # [:q_cont]
16
+ # search against _all fields
17
+ #
18
+ # ==== condition predicates:
19
+ # '_cont' contains string value
20
+ # '_eq' equal value
21
+ # '_in' include any of the values
22
+ # '_in_all' include all values
23
+ # '_gt' greater then value
24
+ # '_lt' less then value
25
+ # '_gteq' greater or equal the value
26
+ # '_lteq ' less or equal the value
27
+ #
28
+ # === search_options
29
+ # [:globalize]
30
+ # For search on localized attributes like 'name_en' via 'translations_' prefixed field
31
+ # example: <code>User.elastic_ransack({translations_name_cont: 'text'}, globalize: true)</code>
32
+ # will search on 'name_en' field (depending on current locale)
33
+ #
34
+ # === Examples
35
+ # Product.elastic_ransack(name_cont: 'alex', category_id_eq: 1, tag_ids_in: [2, 3])
36
+ # Product.elastic_ransack(tag_ids_in: '2,3,4')
37
+ # Product.elastic_ransack(created_at_gt: 1.day.ago)
38
+ # Product.elastic_ransack(q_cont: 'table')
39
+ # Product.elastic_ransack(s: 'price desc')
40
+ # Product.elastic_ransack({translations_name_cont: 'chair'}, globalize: true)
41
+ #
42
+ def elastic_ransack(options={}, search_options={})
43
+ elastic_ransack = Search.new(self, options, search_options)
44
+ elastic_ransack.search
45
+ elastic_ransack
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ module ElasticRansack
2
+ module Naming
3
+
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ def persisted?
9
+ false
10
+ end
11
+
12
+ def to_key
13
+ nil
14
+ end
15
+
16
+ def to_param
17
+ nil
18
+ end
19
+
20
+ def to_model
21
+ self
22
+ end
23
+ end
24
+
25
+ class Name < String
26
+ attr_reader :singular, :plural, :element, :collection, :partial_path, :human, :param_key, :route_key, :i18n_key
27
+ alias_method :cache_key, :collection
28
+
29
+ def initialize
30
+ super('Search')
31
+ @singular = 'search'.freeze
32
+ @plural = 'searches'.freeze
33
+ @element = 'search'.freeze
34
+ @human = 'Search'.freeze
35
+ @collection = 'elastic_ransack/searches'.freeze
36
+ @partial_path = "#{@collection}/#{@element}".freeze
37
+ @param_key = 'q'.freeze
38
+ @route_key = 'searches'.freeze
39
+ @i18n_key = :elastic_ransack
40
+ end
41
+ end
42
+
43
+ module ClassMethods
44
+ def model_name
45
+ @_model_name ||= Name.new
46
+ end
47
+
48
+ def i18n_scope
49
+ :elastic_ransack
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,12 @@
1
+ module ElasticRansack
2
+ class Predicate
3
+ attr_reader :name, :regexp, :query
4
+
5
+ def initialize(name, opts={})
6
+ @name = name
7
+ @regexp = opts[:regexp] || Regexp.new("^(.+)_#{@name}$")
8
+ @query = opts[:query]
9
+ raise(ArgumentError, ':query option is required') unless @query
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,103 @@
1
+ module ElasticRansack
2
+ class Search
3
+ include ::ElasticRansack::Naming
4
+ include Enumerable
5
+
6
+ attr_reader :options, :search_options, :model, :search_results, :sorts, :globalize
7
+
8
+ delegate :results, :each, :empty?, :size, :[], to: :search_results
9
+ delegate :total_entries, :per_page, :total_pages, :current_page, :previous_page, :next_page, :offset, :out_of_bounds?,
10
+ to: :search_results
11
+
12
+ alias_method :klass, :model
13
+
14
+ DATETIME_REGEXP = /\d{2}\.\d{2}.\d{4} \d{2}\:\d{2}/
15
+ DATE_REGEXP = /\d{2}\.\d{2}.\d{4}/
16
+
17
+ def initialize(model, options, search_options)
18
+ search_options ||= {}
19
+ search_options.reverse_merge!(globalize: true)
20
+ @model = model
21
+ @options = options.stringify_keys
22
+ @search_options = search_options || {}
23
+ @sorts = []
24
+ @globalize = @search_options.delete(:globalize)
25
+ sorting = @options.delete('s')
26
+ if sorting.blank?
27
+ add_sort('id', 'desc')
28
+ else
29
+ sorting_split = sorting.split(/\s+/, 2)
30
+ add_sort(sorting_split[0], sorting_split[1] || 'asc')
31
+ end
32
+ end
33
+
34
+ def add_sort(name, dir)
35
+ @sorts << OpenStruct.new(name: name, dir: dir)
36
+ end
37
+
38
+ def search
39
+ @search_results ||= begin
40
+ that = self
41
+ query_string = []
42
+ tire.search(@search_options) do
43
+ and_filters = []
44
+ sort do
45
+ that.sorts.each do |s|
46
+ by s.name, s.dir
47
+ end
48
+ end
49
+
50
+ that.options.each do |k, v|
51
+ next if v.blank?
52
+
53
+ if k == 'q_cont' || k == 'q_eq'
54
+ query_string << "#{v.lucene_escape}" if v.present?
55
+ next
56
+ end
57
+
58
+ if k =~ /^(.+)_cont$/
59
+ attr = $1
60
+ attr = "#{$1.sub(/^translations_/, '')}_#{I18n.locale}" if that.globalize && attr =~ /^translations_(.+)/
61
+ attr_query = [
62
+ v.split.map { |part| "#{attr}:*#{part.lucene_escape}*" }.join(' AND '),
63
+ v.split.map { |part| "#{attr}:\"#{part.lucene_escape}\"" }.join(' AND ')
64
+ ]
65
+ query_string << attr_query.map { |q| "(#{q})" }.join(' OR ')
66
+ next
67
+ else
68
+ v = that.format_value(v)
69
+ end
70
+
71
+ ElasticRansack.predicates.each do |predicate|
72
+ if k =~ predicate.regexp
73
+ and_filters << predicate.query.call($1, v)
74
+ end
75
+ end
76
+ end
77
+
78
+ query { string query_string.join(' ') } unless query_string.blank?
79
+ filter(:and, filters: and_filters) unless and_filters.blank?
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ def translate(*args)
86
+ model.human_attribute_name(args.first)
87
+ end
88
+
89
+ def format_value(v)
90
+ if v =~ DATETIME_REGEXP
91
+ Date.parse(v)
92
+ elsif v =~ DATE_REGEXP
93
+ Time.parse(v)
94
+ else
95
+ v
96
+ end
97
+ end
98
+
99
+ def method_missing(*args, &block)
100
+ @model.send(*args, &block)
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,3 @@
1
+ module ElasticRansack
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe ElasticRansack do
4
+ subject { ModelSearch }
5
+
6
+ before :all do
7
+ ModelSearch.setup_index
8
+ end
9
+
10
+ it 'setup yields ElasticRansack' do
11
+ ElasticRansack.setup do |config|
12
+ config.should == ElasticRansack
13
+ end
14
+ end
15
+
16
+ it 'add predicates' do
17
+ expect do
18
+ ElasticRansack.add_predicate 'test', {query: proc {}}
19
+ end.to change { ElasticRansack.predicates.size }.by(1)
20
+
21
+ ElasticRansack.predicates.last.name.should == 'test'
22
+ end
23
+
24
+ it 'add elastic_ransack method' do
25
+ should respond_to(:elastic_ransack)
26
+ end
27
+
28
+ describe '#val_to_array' do
29
+ it 'convert comma separated string to array' do
30
+ ElasticRansack.val_to_array('2,3,4').should == [2, 3, 4]
31
+ end
32
+
33
+ it 'return if array' do
34
+ array = [1, 2, 3]
35
+ ElasticRansack.val_to_array(array).should == array
36
+ end
37
+
38
+ it 'return empty array if value evaluates to false' do
39
+ ElasticRansack.val_to_array(nil).should == []
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'string lucene query escaping' do
4
+ it do
5
+ 'string'.should respond_to(:lucene_escape)
6
+ end
7
+
8
+ %w( + - && || ! ( ) { } [ ] ^ " ~ * ? : / ).each do |char|
9
+ it "escape #{char}" do
10
+ "abc #{char} edf".lucene_escape.should == "abc \\#{char} edf"
11
+ end
12
+ end
13
+
14
+ it 'escape \ ' do
15
+ "abc \\ edf".lucene_escape.should == "abc \\\\ edf"
16
+ end
17
+
18
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe ElasticRansack::Predicate do
4
+ it 'require :query option' do
5
+ expect { ElasticRansack::Predicate.new('test', {}) }.to raise_error(ArgumentError, ':query option is required')
6
+ end
7
+
8
+ it 'creates regexp based on name' do
9
+ predicate = ElasticRansack::Predicate.new('test', {query: proc {} })
10
+ predicate.regexp.should == /^(.+)_test$/
11
+ end
12
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'predicates' do
4
+ before :all do
5
+ @model = ModelSearch
6
+ @model.setup_index
7
+ end
8
+
9
+ it 'delegate methods to model' do
10
+ [:results, :each, :empty?, :size, :[], :total_entries, :per_page, :total_pages,
11
+ :current_page, :previous_page, :next_page, :offset, :out_of_bounds?].each do |meth|
12
+ @model.elastic_ransack.should.respond_to?(meth)
13
+ end
14
+ end
15
+
16
+ context 'string fields' do
17
+ it '_cont' do
18
+ @model.elastic_ransack(text_cont: 'test').map(&:id).map(&:to_i).should =~ [1, 3]
19
+ end
20
+
21
+ it '_cont localized' do
22
+ I18n.locale = :ru
23
+ @model.elastic_ransack({translations_text_cont: 'alexandr'}, {globalize: true}).map(&:id).map(&:to_i).should =~ [1]
24
+ end
25
+
26
+ it 'q_cont param search on _all fields' do
27
+ @model.elastic_ransack(q_cont: 'test').map(&:id).map(&:to_i).should =~ [1, 3]
28
+ end
29
+ end
30
+
31
+ it '_eq' do
32
+ @model.elastic_ransack(one_assoc_id_eq: 2).map(&:id).map(&:to_i).should =~ [2, 3]
33
+ end
34
+
35
+ it '_in' do
36
+ @model.elastic_ransack(many_assoc_ids_in: [3, 99]).map(&:id).map(&:to_i).should =~ [1, 2]
37
+ end
38
+
39
+ it '_in_all' do
40
+ @model.elastic_ransack(many_assoc_ids_in_all: [4, 5]).map(&:id).map(&:to_i).should =~ [2]
41
+ end
42
+
43
+ context '_gt' do
44
+ it 'integer' do
45
+ @model.elastic_ransack(int_attr_gt: 20).map(&:id).map(&:to_i).should =~ [3]
46
+ end
47
+
48
+ it 'datetime' do
49
+ @model.elastic_ransack(created_at_gt: 2.days.ago).map(&:id).map(&:to_i).should =~ [1, 3]
50
+ end
51
+
52
+ it 'datetime string' do
53
+ @model.elastic_ransack(created_at_gt: 2.days.ago.strftime('%d.%m.%Y %H:%M')).map(&:id).map(&:to_i).should =~ [1, 3]
54
+ end
55
+
56
+ it 'date' do
57
+ @model.elastic_ransack(created_at_gt: Date.yesterday).map(&:id).map(&:to_i).should =~ [1, 3]
58
+ end
59
+
60
+ it 'date string' do
61
+ @model.elastic_ransack(created_at_gt: Date.yesterday.strftime('%d.%m.%Y')).map(&:id).map(&:to_i).should =~ [1, 3]
62
+ end
63
+ end
64
+
65
+ context '_lt' do
66
+ it 'integer' do
67
+ @model.elastic_ransack(int_attr_lt: 20).map(&:id).map(&:to_i).should =~ [1]
68
+ end
69
+
70
+ it 'datetime' do
71
+ @model.elastic_ransack(created_at_lt: 2.days.ago).map(&:id).map(&:to_i).should =~ [2]
72
+ end
73
+
74
+ it 'datetime string' do
75
+ @model.elastic_ransack(created_at_lt: 2.days.ago.strftime('%d.%m.%Y %H:%M')).map(&:id).map(&:to_i).should =~ [2]
76
+ end
77
+
78
+ it 'date' do
79
+ @model.elastic_ransack(created_at_lt: Date.yesterday).map(&:id).map(&:to_i).should =~ [2]
80
+ end
81
+
82
+ it 'date string' do
83
+ @model.elastic_ransack(created_at_lt: Date.yesterday.strftime('%d.%m.%Y')).map(&:id).map(&:to_i).should =~ [2]
84
+ end
85
+ end
86
+
87
+ it '_gteq' do
88
+ @model.elastic_ransack(int_attr_gteq: 20).map(&:id).map(&:to_i).should =~ [2, 3]
89
+ end
90
+
91
+ it '_lteq' do
92
+ @model.elastic_ransack(int_attr_lteq: 20).map(&:id).map(&:to_i).should =~ [1, 2]
93
+ end
94
+
95
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'sorting' do
4
+ before :all do
5
+ @model = ModelSearch
6
+ @model.setup_index
7
+ end
8
+
9
+ it 'default sort by id desc' do
10
+ @model.elastic_ransack.map(&:id).map(&:to_i).should =~ [3, 2, 1]
11
+ end
12
+
13
+ it 'custom sort' do
14
+ @model.elastic_ransack(s: 'float_attr').map(&:id).map(&:to_i).should =~ [1, 3, 2]
15
+ end
16
+
17
+ it 'custom sort mode' do
18
+ @model.elastic_ransack(s: 'float_attr desc').map(&:id).map(&:to_i).should =~ [2, 3, 1]
19
+ end
20
+ end
@@ -0,0 +1,103 @@
1
+ require 'active_model'
2
+ require 'tire'
3
+ require 'active_support/core_ext'
4
+ require 'elastic_ransack'
5
+
6
+ Tire.configure do
7
+ logger 'tmp/elasticsearch.log' # Commented out logger line here so that it doesn't break specs when tmp directory doesn't exist.
8
+ url 'http://localhost:9200'
9
+ pretty 1
10
+ end
11
+
12
+ class ActiveModelBase
13
+ include ActiveModel::AttributeMethods
14
+ include ActiveModel::Serialization
15
+ include ActiveModel::Serializers::JSON
16
+ include ActiveModel::Naming
17
+
18
+ extend ActiveModel::Callbacks
19
+ define_model_callbacks :save, :destroy
20
+
21
+ attr_reader :attributes
22
+ attr_accessor :id, :created_at
23
+
24
+ def initialize(attributes = {})
25
+ @attributes = attributes
26
+ end
27
+
28
+ def to_indexed_json
29
+ @attributes.to_json
30
+ end
31
+
32
+ def method_missing(id, *args, &block)
33
+ attributes[id.to_sym] || attributes[id.to_s] || super
34
+ end
35
+
36
+ def persisted?
37
+ true
38
+ end
39
+
40
+ def save
41
+ _run_save_callbacks do
42
+ end
43
+ end
44
+
45
+ def destroy
46
+ _run_destroy_callbacks do
47
+ @destroyed = true
48
+ end
49
+ end
50
+
51
+ def destroyed?
52
+ !!@destroyed
53
+ end
54
+ end
55
+
56
+
57
+ class ModelSearch < ActiveModelBase
58
+ include Tire::Model::Search
59
+ include Tire::Model::Callbacks
60
+
61
+ include ElasticRansack::Model
62
+
63
+ mapping do
64
+ indexes :id, type: 'integer'
65
+ indexes :int_attr, type: 'integer'
66
+ end
67
+
68
+ def self.setup_index
69
+ tire.index.delete
70
+ tire.create_elasticsearch_index
71
+ populate
72
+ tire.index.refresh
73
+ end
74
+
75
+ def self.test_data
76
+ [
77
+ {
78
+ id: 1, text: 'alex test', text_ru: 'alexandr',
79
+ one_assoc_id: 1, many_assoc_ids: [2, 3, 4],
80
+ int_attr: 10, float_attr: 5.5, created_at: 1.day.ago
81
+ },
82
+ {
83
+ id: 2, text: 'andrey black', text_ru: 'andrey rus',
84
+ one_assoc_id: 2, many_assoc_ids: [3, 4, 5, 6],
85
+ int_attr: 20, float_attr: 27.5, created_at: 1.month.ago
86
+ },
87
+ {
88
+ id: 3, text: 'mike test', text_ru: 'mike rus',
89
+ one_assoc_id: 2, many_assoc_ids: [10, 14, 17],
90
+ int_attr: 80, float_attr: 7.5, created_at: 1.hour.ago
91
+ }
92
+ ]
93
+ end
94
+
95
+ def self.populate
96
+ test_data.each do |attrs|
97
+ u = new(attrs)
98
+ u.id = attrs[:id]
99
+ u.save
100
+ end
101
+ end
102
+
103
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elastic_ransack
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Leschenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: tire
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ description: Conditions searching using predicates such as 'name_cont' or 'created_at_gt'
70
+ for elasticsearch models
71
+ email:
72
+ - leschenko.al@gmail.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - elastic_ransack.gemspec
83
+ - lib/elastic_ransack.rb
84
+ - lib/elastic_ransack/core_ext.rb
85
+ - lib/elastic_ransack/model.rb
86
+ - lib/elastic_ransack/naming.rb
87
+ - lib/elastic_ransack/predicate.rb
88
+ - lib/elastic_ransack/search.rb
89
+ - lib/elastic_ransack/version.rb
90
+ - spec/elastic_ransack/base_spec.rb
91
+ - spec/elastic_ransack/core_ext_spec.rb
92
+ - spec/elastic_ransack/predicate_spec.rb
93
+ - spec/elastic_ransack/search_spec.rb
94
+ - spec/elastic_ransack/sorting_spec.rb
95
+ - spec/spec_helper.rb
96
+ homepage: https://github.com/leschenko/elastic_ransack
97
+ licenses:
98
+ - MIT
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.0.3
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Conditions searching adapter for elasticsearch models like ransack
120
+ test_files:
121
+ - spec/elastic_ransack/base_spec.rb
122
+ - spec/elastic_ransack/core_ext_spec.rb
123
+ - spec/elastic_ransack/predicate_spec.rb
124
+ - spec/elastic_ransack/search_spec.rb
125
+ - spec/elastic_ransack/sorting_spec.rb
126
+ - spec/spec_helper.rb