ssickles-tire 0.4.2.7 → 0.4.3

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 (59) hide show
  1. data/lib/tire.rb +18 -3
  2. data/lib/tire/alias.rb +11 -35
  3. data/lib/tire/index.rb +34 -76
  4. data/lib/tire/model/callbacks.rb +40 -0
  5. data/lib/tire/model/import.rb +26 -0
  6. data/lib/tire/model/indexing.rb +128 -0
  7. data/lib/tire/model/naming.rb +100 -0
  8. data/lib/tire/model/percolate.rb +99 -0
  9. data/lib/tire/model/persistence.rb +72 -0
  10. data/lib/tire/model/persistence/attributes.rb +143 -0
  11. data/lib/tire/model/persistence/finders.rb +66 -0
  12. data/lib/tire/model/persistence/storage.rb +71 -0
  13. data/lib/tire/model/search.rb +305 -0
  14. data/lib/tire/results/collection.rb +38 -13
  15. data/lib/tire/results/item.rb +19 -0
  16. data/lib/tire/rubyext/hash.rb +8 -0
  17. data/lib/tire/rubyext/ruby_1_8.rb +54 -0
  18. data/lib/tire/rubyext/symbol.rb +11 -0
  19. data/lib/tire/search.rb +7 -8
  20. data/lib/tire/search/scan.rb +8 -8
  21. data/lib/tire/search/sort.rb +1 -1
  22. data/lib/tire/utils.rb +17 -0
  23. data/lib/tire/version.rb +7 -38
  24. data/test/integration/active_model_indexing_test.rb +51 -0
  25. data/test/integration/active_model_searchable_test.rb +114 -0
  26. data/test/integration/active_record_searchable_test.rb +446 -0
  27. data/test/integration/mongoid_searchable_test.rb +309 -0
  28. data/test/integration/persistent_model_test.rb +117 -0
  29. data/test/integration/reindex_test.rb +2 -2
  30. data/test/integration/scan_test.rb +1 -1
  31. data/test/models/active_model_article.rb +31 -0
  32. data/test/models/active_model_article_with_callbacks.rb +49 -0
  33. data/test/models/active_model_article_with_custom_document_type.rb +7 -0
  34. data/test/models/active_model_article_with_custom_index_name.rb +7 -0
  35. data/test/models/active_record_models.rb +122 -0
  36. data/test/models/mongoid_models.rb +97 -0
  37. data/test/models/persistent_article.rb +11 -0
  38. data/test/models/persistent_article_in_namespace.rb +12 -0
  39. data/test/models/persistent_article_with_casting.rb +28 -0
  40. data/test/models/persistent_article_with_defaults.rb +11 -0
  41. data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
  42. data/test/models/supermodel_article.rb +17 -0
  43. data/test/models/validated_model.rb +11 -0
  44. data/test/test_helper.rb +27 -3
  45. data/test/unit/active_model_lint_test.rb +17 -0
  46. data/test/unit/index_alias_test.rb +3 -17
  47. data/test/unit/index_test.rb +30 -18
  48. data/test/unit/model_callbacks_test.rb +116 -0
  49. data/test/unit/model_import_test.rb +71 -0
  50. data/test/unit/model_persistence_test.rb +516 -0
  51. data/test/unit/model_search_test.rb +899 -0
  52. data/test/unit/results_collection_test.rb +60 -0
  53. data/test/unit/results_item_test.rb +37 -0
  54. data/test/unit/rubyext_test.rb +3 -3
  55. data/test/unit/search_test.rb +1 -6
  56. data/test/unit/tire_test.rb +15 -0
  57. data/tire.gemspec +30 -13
  58. metadata +153 -41
  59. data/lib/tire/rubyext/to_json.rb +0 -21
@@ -0,0 +1,8 @@
1
+ class Hash
2
+
3
+ def to_json(options=nil)
4
+ MultiJson.encode(self)
5
+ end unless respond_to?(:to_json)
6
+
7
+ alias_method :to_indexed_json, :to_json
8
+ end
@@ -0,0 +1,54 @@
1
+ # Stolen from ruby core's uri/common.rb, with modifications to support 1.8.x
2
+ #
3
+ # https://github.com/ruby/ruby/blob/trunk/lib/uri/common.rb
4
+ #
5
+ #
6
+
7
+ module URI
8
+ TBLENCWWWCOMP_ = {} # :nodoc:
9
+ 256.times do |i|
10
+ TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
11
+ end
12
+ TBLENCWWWCOMP_[' '] = '+'
13
+ TBLENCWWWCOMP_.freeze
14
+ TBLDECWWWCOMP_ = {} # :nodoc:
15
+ 256.times do |i|
16
+ h, l = i>>4, i&15
17
+ TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
18
+ TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
19
+ TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
20
+ TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
21
+ end
22
+ TBLDECWWWCOMP_['+'] = ' '
23
+ TBLDECWWWCOMP_.freeze
24
+
25
+ # Encode given +s+ to URL-encoded form data.
26
+ #
27
+ # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
28
+ # (ASCII space) to + and converts others to %XX.
29
+ #
30
+ # This is an implementation of
31
+ # http://www.w3.org/TR/html5/forms.html#url-encoded-form-data
32
+ #
33
+ # See URI.decode_www_form_component, URI.encode_www_form
34
+ def self.encode_www_form_component(s)
35
+ str = s.to_s
36
+ if RUBY_VERSION < "1.9" && $KCODE =~ /u/i
37
+ str.gsub(/([^ a-zA-Z0-9_.-]+)/) do
38
+ '%' + $1.unpack('H2' * Rack::Utils.bytesize($1)).join('%').upcase
39
+ end.tr(' ', '+')
40
+ else
41
+ str.gsub(/[^*\-.0-9A-Z_a-z]/) {|m| TBLENCWWWCOMP_[m]}
42
+ end
43
+ end
44
+
45
+ # Decode given +str+ of URL-encoded form data.
46
+ #
47
+ # This decods + to SP.
48
+ #
49
+ # See URI.encode_www_form_component, URI.decode_www_form
50
+ def self.decode_www_form_component(str, enc=nil)
51
+ raise ArgumentError, "invalid %-encoding (#{str})" unless /\A(?:%[0-9a-fA-F]{2}|[^%])*\z/ =~ str
52
+ str.gsub(/\+|%[0-9a-fA-F]{2}/) {|m| TBLDECWWWCOMP_[m]}
53
+ end
54
+ end
@@ -0,0 +1,11 @@
1
+ # ActiveModel::Serialization Ruby < 1.9.x compatibility
2
+
3
+ class Symbol
4
+ def <=> other
5
+ self.to_s <=> other.to_s
6
+ end unless method_defined?(:'<=>')
7
+
8
+ def capitalize
9
+ to_s.capitalize
10
+ end unless method_defined?(:capitalize)
11
+ end
data/lib/tire/search.rb CHANGED
@@ -4,23 +4,18 @@ module Tire
4
4
 
5
5
  class Search
6
6
 
7
- attr_reader :indices, :json, :query, :facets, :filters, :options, :explain, :url
7
+ attr_reader :indices, :json, :query, :facets, :filters, :options, :explain
8
8
 
9
9
  def initialize(indices=nil, options={}, &block)
10
10
  @indices = Array(indices)
11
- @types = Array(options.delete(:type)).map { |type| EscapeUtils.escape_url(type.to_s) }
11
+ @types = Array(options.delete(:type)).map { |type| Utils.escape(type) }
12
12
  @options = options
13
- @path = ['/', @indices.join(','), @types.join(','), '_search'].compact.join('/').squeeze('/')
14
13
 
15
- self.url = options.delete(:url) || Configuration.url
14
+ @path = ['/', @indices.join(','), @types.join(','), '_search'].compact.join('/').squeeze('/')
16
15
 
17
16
  block.arity < 1 ? instance_eval(&block) : block.call(self) if block_given?
18
17
  end
19
18
 
20
- def url=( url )
21
- @url = url + @path
22
- end
23
-
24
19
  def results
25
20
  @results || (perform; @results)
26
21
  end
@@ -29,6 +24,10 @@ module Tire
29
24
  @response || (perform; @response)
30
25
  end
31
26
 
27
+ def url
28
+ Configuration.url + @path
29
+ end
30
+
32
31
  def params
33
32
  @options.empty? ? '' : '?' + @options.to_param
34
33
  end
@@ -42,22 +42,22 @@ module Tire
42
42
  class Scan
43
43
  include Enumerable
44
44
 
45
- attr_reader :indices, :options, :search, :url
45
+ attr_reader :indices, :options, :search
46
46
 
47
47
  def initialize(indices=nil, options={}, &block)
48
48
  @indices = Array(indices)
49
49
  @options = options.update(:search_type => 'scan', :scroll => '10m')
50
50
  @seen = 0
51
- @url = options.fetch(:url, Configuration.url) + '/_search/scroll'
52
51
  @search = Search.new(@indices, @options, &block)
53
52
  end
54
53
 
55
- def params() @options.empty? ? '' : '?' + @options.to_param; end
56
- def results() @results || (__perform; @results); end
57
- def response() @response || (__perform; @response); end
58
- def json() @json || (__perform; @json); end
59
- def total() @total || (__perform; @total); end
60
- def seen() @seen || (__perform; @seen); end
54
+ def url; Configuration.url + "/_search/scroll"; end
55
+ def params; @options.empty? ? '' : '?' + @options.to_param; end
56
+ def results; @results || (__perform; @results); end
57
+ def response; @response || (__perform; @response); end
58
+ def json; @json || (__perform; @json); end
59
+ def total; @total || (__perform; @total); end
60
+ def seen; @seen || (__perform; @seen); end
61
61
 
62
62
  def scroll_id
63
63
  @scroll_id ||= @search.perform.json['_scroll_id']
@@ -17,7 +17,7 @@ module Tire
17
17
  end
18
18
 
19
19
  def to_json
20
- MultiJson.encode(@value)
20
+ @value.to_json
21
21
  end
22
22
  end
23
23
 
data/lib/tire/utils.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'uri'
2
+
3
+ module Tire
4
+ module Utils
5
+
6
+ def escape(s)
7
+ URI.encode_www_form_component(s.to_s)
8
+ end
9
+
10
+ def unescape(s)
11
+ s = s.to_s.respond_to?(:force_encoding) ? s.to_s.force_encoding(Encoding::UTF_8) : s.to_s
12
+ URI.decode_www_form_component(s)
13
+ end
14
+
15
+ extend self
16
+ end
17
+ end
data/lib/tire/version.rb CHANGED
@@ -1,46 +1,9 @@
1
1
  module Tire
2
- VERSION = "0.4.2.7"
2
+ VERSION = "0.4.3"
3
3
 
4
4
  CHANGELOG =<<-END
5
5
  IMPORTANT CHANGES LATELY:
6
6
 
7
- Version 0.4.2.7
8
- ---------------
9
- * Bumped escape_utils dependency to ~> 0.3.x
10
-
11
- Version 0.4.2.6
12
- ---------------
13
- * Add support for parent / child documents.
14
-
15
- Version 0.4.2.4
16
- ---------------
17
- * Add routing support.
18
-
19
- Version 0.4.2.4
20
- ---------------
21
- * Fixing url routing for index alais commands.
22
-
23
- Version 0.4.2.3
24
- ---------------
25
- * Making the library multi-cluster capable
26
-
27
- Version 0.4.2.2
28
- ---------------
29
- * Tire::Index now removes _id/_type keys from document hashes.
30
-
31
- Version 0.4.2.1
32
- ---------------
33
- * Removed all depedence on ActiveModel
34
- * Fixed tests to work with any version of ActiveSupport
35
- * Removed wonky 1.9 backports for URL encoding
36
- * Fixed tests to work under 1.8.7
37
-
38
- Version 0.4.2
39
- -------------
40
- * Fixed incorrect handling of PUT requests in the Curb client
41
- * Fixed, that blocks passed to `Tire::Index.new` or `Tire.index` losed the scope
42
- * Added `Tire::Alias`, interface and DSL to manage aliases as resources
43
-
44
7
  Version 0.4.1
45
8
  -------------
46
9
  * Added a Index#settings method to retrieve index settings as a Hash
@@ -49,5 +12,11 @@ module Tire
49
12
  * Added basic support for index aliases
50
13
  * Changed, that Index#bulk_store runs against an index endpoint, not against `/_bulk`
51
14
  * Refactorings, fixes, Ruby 1.8 compatibility
15
+
16
+ Version 0.4.2
17
+ -------------
18
+ * Fixed incorrect handling of PUT requests in the Curb client
19
+ * Fixed, that blocks passed to `Tire::Index.new` or `Tire.index` losed the scope
20
+ * Added `Tire::Alias`, interface and DSL to manage aliases as resources
52
21
  END
53
22
  end
@@ -0,0 +1,51 @@
1
+ require 'test_helper'
2
+ require File.expand_path('../../models/supermodel_article', __FILE__)
3
+
4
+ module Tire
5
+
6
+ class ActiveModelSearchableIntegrationTest < Test::Unit::TestCase
7
+ include Test::Integration
8
+
9
+ class ::ActiveModelArticleWithCustomAsSerialization < ActiveModelArticleWithCallbacks
10
+ mapping do
11
+ indexes :title
12
+ indexes :content
13
+ indexes :characters, :as => 'content.length'
14
+ indexes :readability, :as => proc {
15
+ content.split(/\W/).reject { |t| t.blank? }.size /
16
+ content.split(/\./).size
17
+ }
18
+ end
19
+ end
20
+
21
+ def setup
22
+ super
23
+ ActiveModelArticleWithCustomAsSerialization.index.delete
24
+ end
25
+
26
+ def teardown
27
+ super
28
+ ActiveModelArticleWithCustomAsSerialization.index.delete
29
+ end
30
+
31
+ context "ActiveModel serialization" do
32
+
33
+ setup do
34
+ @model = ActiveModelArticleWithCustomAsSerialization.new \
35
+ :id => 1,
36
+ :title => 'Test article',
37
+ :content => 'Lorem Ipsum. Dolor Sit Amet.'
38
+ @model.update_index
39
+ @model.index.refresh
40
+ end
41
+
42
+ should "serialize the content length" do
43
+ m = ActiveModelArticleWithCustomAsSerialization.search('*').first
44
+ assert_equal 28, m.characters
45
+ assert_equal 2, m.readability
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,114 @@
1
+ require 'test_helper'
2
+ require File.expand_path('../../models/supermodel_article', __FILE__)
3
+
4
+ module Tire
5
+
6
+ class ActiveModelSearchableIntegrationTest < Test::Unit::TestCase
7
+ include Test::Integration
8
+
9
+ def setup
10
+ super
11
+ Redis::Persistence.config.redis = Redis.new db: ENV['REDIS_PERSISTENCE_TEST_DATABASE'] || 14
12
+ Redis::Persistence.config.redis.flushdb
13
+ @model = SupermodelArticle.new :title => 'Test'
14
+ end
15
+
16
+ def teardown
17
+ super
18
+ SupermodelArticle.all.each { |a| a.destroy }
19
+ end
20
+
21
+ context "ActiveModel integration" do
22
+
23
+ setup do
24
+ Tire.index('supermodel_articles').delete
25
+ load File.expand_path('../../models/supermodel_article.rb', __FILE__)
26
+ end
27
+ teardown { Tire.index('supermodel_articles').delete }
28
+
29
+ should "configure mapping" do
30
+ assert_equal 'czech', SupermodelArticle.mapping[:title][:analyzer]
31
+ assert_equal 15, SupermodelArticle.mapping[:title][:boost]
32
+
33
+ assert_equal 'czech', SupermodelArticle.index.mapping['supermodel_article']['properties']['title']['analyzer']
34
+ end
35
+
36
+ should "save document into index on save and find it with score" do
37
+ a = SupermodelArticle.new :title => 'Test'
38
+ a.save
39
+ id = a.id
40
+
41
+ # Store document of another type in the index
42
+ Index.new 'supermodel_articles' do
43
+ store :type => 'other-thing', :title => 'Title for other thing'
44
+ end
45
+
46
+ a.index.refresh
47
+
48
+ # The index should contain 2 documents
49
+ assert_equal 2, Tire.search('supermodel_articles') { query { all } }.results.size
50
+
51
+ results = SupermodelArticle.search 'test'
52
+
53
+ # The model should find only 1 document
54
+ assert_equal 1, results.count
55
+
56
+ assert_instance_of Results::Item, results.first
57
+ assert_equal 'Test', results.first.title
58
+ assert_not_nil results.first._score
59
+ assert_equal id.to_s, results.first.id.to_s
60
+ end
61
+
62
+ should "remove document from index on destroy" do
63
+ a = SupermodelArticle.new :title => 'Test'
64
+ a.save
65
+ assert_equal 1, SupermodelArticle.all.size
66
+
67
+ a.destroy
68
+ assert_equal 0, SupermodelArticle.all.size
69
+
70
+ a.index.refresh
71
+ results = SupermodelArticle.search 'test'
72
+
73
+ assert_equal 0, results.count
74
+ end
75
+
76
+ should "retrieve sorted documents by IDs returned from search" do
77
+ SupermodelArticle.create :title => 'foo'
78
+ SupermodelArticle.create :id => 'abc123', :title => 'bar'
79
+
80
+ SupermodelArticle.index.refresh
81
+ results = SupermodelArticle.search 'foo OR bar^100'
82
+
83
+ assert_equal 2, results.count
84
+
85
+ assert_equal 'bar', results.first.title
86
+ assert_equal 'abc123', results.first.id
87
+ end
88
+
89
+ context "within Rails" do
90
+
91
+ setup do
92
+ module ::Rails; end
93
+ end
94
+
95
+ should "load the underlying model" do
96
+ a = SupermodelArticle.new :title => 'Test'
97
+ a.save
98
+ a.index.refresh
99
+
100
+ results = SupermodelArticle.search 'test'
101
+
102
+ assert_instance_of Results::Item, results.first
103
+ assert_instance_of SupermodelArticle, results.first.load
104
+
105
+ assert_equal 'Test', results.first.load.title
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -0,0 +1,446 @@
1
+ require 'test_helper'
2
+
3
+ module Tire
4
+
5
+ class ActiveRecordSearchableIntegrationTest < Test::Unit::TestCase
6
+ include Test::Integration
7
+
8
+ def setup
9
+ super
10
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => ":memory:" )
11
+
12
+ ActiveRecord::Migration.verbose = false
13
+ ActiveRecord::Schema.define(:version => 1) do
14
+ create_table :active_record_articles do |t|
15
+ t.string :title
16
+ t.datetime :created_at, :default => 'NOW()'
17
+ end
18
+ create_table :active_record_comments do |t|
19
+ t.string :author
20
+ t.text :body
21
+ t.references :article
22
+ t.timestamps
23
+ end
24
+ create_table :active_record_stats do |t|
25
+ t.integer :pageviews
26
+ t.string :period
27
+ t.references :article
28
+ end
29
+ create_table :active_record_class_with_tire_methods do |t|
30
+ t.string :title
31
+ end
32
+ create_table :active_record_class_with_dynamic_index_names do |t|
33
+ t.string :title
34
+ end
35
+ end
36
+ end
37
+
38
+ context "ActiveRecord integration" do
39
+
40
+ setup do
41
+ ActiveRecordArticle.destroy_all
42
+ Tire.index('active_record_articles').delete
43
+
44
+ load File.expand_path('../../models/active_record_models.rb', __FILE__)
45
+ end
46
+
47
+ teardown do
48
+ ActiveRecordArticle.destroy_all
49
+ Tire.index('active_record_articles').delete
50
+ end
51
+
52
+ should "configure mapping" do
53
+ assert_equal 'snowball', ActiveRecordArticle.mapping[:title][:analyzer]
54
+ assert_equal 10, ActiveRecordArticle.mapping[:title][:boost]
55
+
56
+ assert_equal 'snowball', ActiveRecordArticle.index.mapping['active_record_article']['properties']['title']['analyzer']
57
+ end
58
+
59
+ should "save document into index on save and find it" do
60
+ a = ActiveRecordArticle.new :title => 'Test'
61
+ a.save!
62
+ id = a.id
63
+
64
+ a.index.refresh
65
+
66
+ results = ActiveRecordArticle.search 'test'
67
+
68
+ assert results.any?
69
+ assert_equal 1, results.count
70
+
71
+ assert_instance_of Results::Item, results.first
72
+ assert_not_nil results.first.id
73
+ assert_equal id.to_s, results.first.id.to_s
74
+ assert results.first.persisted?, "Record should be persisted"
75
+ assert_not_nil results.first._score
76
+ assert_equal 'Test', results.first.title
77
+ end
78
+
79
+ should "raise exception on invalid query" do
80
+ ActiveRecordArticle.create! :title => 'Test'
81
+
82
+ assert_raise Search::SearchRequestFailed do
83
+ ActiveRecordArticle.search '[x'
84
+ end
85
+ end
86
+
87
+ context "with eager loading" do
88
+ setup do
89
+ ActiveRecordArticle.destroy_all
90
+ 5.times { |n| ActiveRecordArticle.create! :title => "Test #{n+1}" }
91
+ ActiveRecordArticle.index.refresh
92
+ end
93
+
94
+ should "load records on query search" do
95
+ results = ActiveRecordArticle.search '"Test 1"', :load => true
96
+
97
+ assert results.any?
98
+ assert_equal ActiveRecordArticle.find(1), results.first
99
+ end
100
+
101
+ should "load records on block search" do
102
+ results = ActiveRecordArticle.search :load => true do
103
+ query { string '"Test 1"' }
104
+ end
105
+
106
+ assert_equal ActiveRecordArticle.find(1), results.first
107
+ end
108
+
109
+ should "load records with options on query search" do
110
+ assert_equal ActiveRecordArticle.find(['1'], :include => 'comments').first,
111
+ ActiveRecordArticle.search('"Test 1"',
112
+ :load => { :include => 'comments' }).results.first
113
+ end
114
+
115
+ should "return empty collection for nonmatching query" do
116
+ assert_nothing_raised do
117
+ results = ActiveRecordArticle.search :load => true do
118
+ query { string '"Hic Sunt Leones"' }
119
+ end
120
+ assert_equal 0, results.size
121
+ assert ! results.any?
122
+ end
123
+ end
124
+ end
125
+
126
+ should "remove document from index on destroy" do
127
+ a = ActiveRecordArticle.new :title => 'Test remove...'
128
+ a.save!
129
+ assert_equal 1, ActiveRecordArticle.count
130
+
131
+ a.destroy
132
+ assert_equal 0, ActiveRecordArticle.all.size
133
+
134
+ a.index.refresh
135
+ results = ActiveRecordArticle.search 'test'
136
+ assert_equal 0, results.count
137
+ end
138
+
139
+ should "return documents with scores" do
140
+ ActiveRecordArticle.create! :title => 'foo'
141
+ ActiveRecordArticle.create! :title => 'bar'
142
+
143
+ ActiveRecordArticle.index.refresh
144
+ results = ActiveRecordArticle.search 'foo OR bar^100'
145
+ assert_equal 2, results.count
146
+
147
+ assert_equal 'bar', results.first.title
148
+ end
149
+
150
+ context "with pagination" do
151
+ setup do
152
+ 1.upto(9) { |number| ActiveRecordArticle.create :title => "Test#{number}" }
153
+ ActiveRecordArticle.index.refresh
154
+ end
155
+
156
+ context "and parameter searches" do
157
+
158
+ should "find first page with five results" do
159
+ results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 1
160
+ assert_equal 5, results.size
161
+
162
+ # WillPaginate
163
+ #
164
+ assert_equal 2, results.total_pages
165
+ assert_equal 1, results.current_page
166
+ assert_equal nil, results.previous_page
167
+ assert_equal 2, results.next_page
168
+
169
+ # Kaminari
170
+ #
171
+ assert_equal 5, results.limit_value
172
+ assert_equal 9, results.total_count
173
+ assert_equal 2, results.num_pages
174
+ assert_equal 0, results.offset_value
175
+
176
+ assert_equal 'Test1', results.first.title
177
+ end
178
+
179
+ should "find next page with five results" do
180
+ results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 2
181
+ assert_equal 4, results.size
182
+
183
+ assert_equal 2, results.total_pages
184
+ assert_equal 2, results.current_page
185
+ assert_equal 1, results.previous_page
186
+ assert_equal nil, results.next_page
187
+
188
+ #kaminari
189
+ assert_equal 5, results.limit_value
190
+ assert_equal 9, results.total_count
191
+ assert_equal 2, results.num_pages
192
+ assert_equal 5, results.offset_value
193
+
194
+ assert_equal 'Test6', results.first.title
195
+ end
196
+
197
+ should "find not find missing page" do
198
+ results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 3
199
+ assert_equal 0, results.size
200
+
201
+ assert_equal 2, results.total_pages
202
+ assert_equal 3, results.current_page
203
+ assert_equal 2, results.previous_page
204
+ assert_equal nil, results.next_page
205
+
206
+ #kaminari
207
+ assert_equal 5, results.limit_value
208
+ assert_equal 9, results.total_count
209
+ assert_equal 2, results.num_pages
210
+ assert_equal 10, results.offset_value
211
+
212
+ assert_nil results.first
213
+ end
214
+
215
+ end
216
+
217
+ context "and block searches" do
218
+ setup { @q = 'test*' }
219
+
220
+ should "find first page with five results" do
221
+ results = ActiveRecordArticle.search do |search|
222
+ search.query { |query| query.string @q }
223
+ search.sort { by :title }
224
+ search.from 0
225
+ search.size 5
226
+ end
227
+ assert_equal 5, results.size
228
+
229
+ assert_equal 2, results.total_pages
230
+ assert_equal 1, results.current_page
231
+ assert_equal nil, results.previous_page
232
+ assert_equal 2, results.next_page
233
+
234
+ assert_equal 'Test1', results.first.title
235
+ end
236
+
237
+ should "find next page with five results" do
238
+ results = ActiveRecordArticle.search do |search|
239
+ search.query { |query| query.string @q }
240
+ search.sort { by :title }
241
+ search.from 5
242
+ search.size 5
243
+ end
244
+ assert_equal 4, results.size
245
+
246
+ assert_equal 2, results.total_pages
247
+ assert_equal 2, results.current_page
248
+ assert_equal 1, results.previous_page
249
+ assert_equal nil, results.next_page
250
+
251
+ assert_equal 'Test6', results.first.title
252
+ end
253
+
254
+ should "not find a missing page" do
255
+ results = ActiveRecordArticle.search do |search|
256
+ search.query { |query| query.string @q }
257
+ search.sort { by :title }
258
+ search.from 10
259
+ search.size 5
260
+ end
261
+ assert_equal 0, results.size
262
+
263
+ assert_equal 2, results.total_pages
264
+ assert_equal 3, results.current_page
265
+ assert_equal 2, results.previous_page
266
+ assert_equal nil, results.next_page
267
+
268
+ assert_nil results.first
269
+ end
270
+
271
+ end
272
+
273
+ end
274
+
275
+ context "with proxy" do
276
+
277
+ should "allow access to Tire instance methods" do
278
+ a = ActiveRecordClassWithTireMethods.create :title => 'One'
279
+ assert_equal "THIS IS MY INDEX!", a.index
280
+ assert_instance_of Tire::Index, a.tire.index
281
+ assert a.tire.index.exists?, "Index should exist"
282
+ end
283
+
284
+ should "allow access to Tire class methods" do
285
+ class ::ActiveRecordClassWithTireMethods < ActiveRecord::Base
286
+ def self.search(*)
287
+ "THIS IS MY SEARCH!"
288
+ end
289
+ end
290
+
291
+ ActiveRecordClassWithTireMethods.create :title => 'One'
292
+ ActiveRecordClassWithTireMethods.tire.index.refresh
293
+
294
+ assert_equal "THIS IS MY SEARCH!", ActiveRecordClassWithTireMethods.search
295
+
296
+ results = ActiveRecordClassWithTireMethods.tire.search 'one'
297
+
298
+ assert_equal 'One', results.first.title
299
+ end
300
+
301
+ end
302
+
303
+ context "with dynamic index name" do
304
+ setup do
305
+ @a = ActiveRecordClassWithDynamicIndexName.create! :title => 'Test'
306
+ @a.index.refresh
307
+ end
308
+
309
+ should "search in proper index" do
310
+ assert_equal 'dynamic_index', ActiveRecordClassWithDynamicIndexName.index.name
311
+ assert_equal 'dynamic_index', @a.index.name
312
+
313
+ results = ActiveRecordClassWithDynamicIndexName.search 'test'
314
+ assert_equal 'dynamic_index', results.first._index
315
+ end
316
+ end
317
+
318
+ context "within Rails" do
319
+
320
+ setup do
321
+ module ::Rails; end
322
+
323
+ a = ActiveRecordArticle.new :title => 'Test'
324
+ a.comments.build :author => 'fool', :body => 'Works!'
325
+ a.stats.build :pageviews => 12, :period => '2011-08'
326
+ a.save!
327
+ @id = a.id.to_s
328
+
329
+ a.index.refresh
330
+ @item = ActiveRecordArticle.search('test').first
331
+ end
332
+
333
+ should "have access to indexed properties" do
334
+ assert_equal 'Test', @item.title
335
+ assert_equal 'fool', @item.comments.first.author
336
+ assert_equal 12, @item.stats.first.pageviews
337
+ end
338
+
339
+ should "load the underlying models" do
340
+ assert_instance_of Results::Item, @item
341
+ assert_instance_of ActiveRecordArticle, @item.load
342
+ assert_equal 'Test', @item.load.title
343
+
344
+ assert_instance_of Results::Item, @item.comments.first
345
+ assert_instance_of ActiveRecordComment, @item.comments.first.load
346
+ assert_equal 'fool', @item.comments.first.load.author
347
+ end
348
+
349
+ should "load the underlying model with options" do
350
+ ActiveRecordArticle.expects(:find).with(@id, :include => 'comments')
351
+ @item.load(:include => 'comments')
352
+ end
353
+
354
+ end
355
+
356
+ context "with multiple class instances in one index" do
357
+ setup do
358
+ ActiveRecord::Schema.define do
359
+ create_table(:active_record_assets) { |t| t.string :title, :timestamp }
360
+ create_table(:active_record_model_one) { |t| t.string :title, :timestamp }
361
+ create_table(:active_record_model_two) { |t| t.string :title, :timestamp }
362
+ end
363
+
364
+ ActiveRecordModelOne.create :title => 'Title One', timestamp: Time.now.to_i
365
+ ActiveRecordModelTwo.create :title => 'Title Two', timestamp: Time.now.to_i
366
+ ActiveRecordModelOne.tire.index.refresh
367
+ ActiveRecordModelTwo.tire.index.refresh
368
+
369
+
370
+ ActiveRecordVideo.create! :title => 'Title One', timestamp: Time.now.to_i
371
+ ActiveRecordPhoto.create! :title => 'Title Two', timestamp: Time.now.to_i
372
+ ActiveRecordAsset.tire.index.refresh
373
+ end
374
+
375
+ teardown do
376
+ ActiveRecordModelOne.destroy_all
377
+ ActiveRecordModelTwo.destroy_all
378
+ ActiveRecordModelOne.tire.index.delete
379
+ ActiveRecordModelTwo.tire.index.delete
380
+
381
+ ActiveRecordAsset.destroy_all
382
+ ActiveRecordAsset.tire.index.delete
383
+ ActiveRecordModelOne.destroy_all
384
+ end
385
+
386
+ should "eagerly load instances of multiple classes, from multiple indices" do
387
+ s = Tire.search ['active_record_model_one', 'active_record_model_two'], :load => true do
388
+ query { string 'title' }
389
+ sort { by :timestamp }
390
+ end
391
+
392
+ # puts s.results[0].inspect
393
+
394
+ assert_equal 2, s.results.length
395
+ assert_instance_of ActiveRecordModelOne, s.results[0]
396
+ assert_instance_of ActiveRecordModelTwo, s.results[1]
397
+ end
398
+
399
+ should "eagerly load all STI descendant records" do
400
+ s = Tire.search('active_record_assets', :load => true) do
401
+ query { string 'title' }
402
+ sort { by :timestamp }
403
+ end
404
+
405
+ assert_equal 2, s.results.length
406
+ assert_instance_of ActiveRecordVideo, s.results[0]
407
+ assert_instance_of ActiveRecordPhoto, s.results[1]
408
+ end
409
+ end
410
+
411
+ context "with namespaced models" do
412
+ setup do
413
+ ActiveRecord::Schema.define { create_table(:active_record_namespace_my_models) { |t| t.string :title, :timestamp } }
414
+
415
+ ActiveRecordNamespace::MyModel.create :title => 'Test'
416
+ ActiveRecordNamespace::MyModel.tire.index.refresh
417
+ end
418
+
419
+ teardown do
420
+ ActiveRecordNamespace::MyModel.destroy_all
421
+ ActiveRecordNamespace::MyModel.tire.index.delete
422
+ end
423
+
424
+ should "save document into index on save and find it" do
425
+ results = ActiveRecordNamespace::MyModel.search 'test'
426
+
427
+ assert results.any?, "No results returned: #{results.inspect}"
428
+ assert_equal 1, results.count
429
+
430
+ assert_instance_of Results::Item, results.first
431
+ end
432
+
433
+ should "eagerly load the records from returned hits" do
434
+ results = ActiveRecordNamespace::MyModel.search 'test', :load => true
435
+
436
+ assert results.any?, "No results returned: #{results.inspect}"
437
+ assert_instance_of ActiveRecordNamespace::MyModel, results.first
438
+ assert_equal ActiveRecordNamespace::MyModel.find(1), results.first
439
+ end
440
+
441
+ end
442
+ end
443
+
444
+ end
445
+
446
+ end