elastic_ransack 0.0.1

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.
@@ -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