tire 0.1.14 → 0.1.15

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.
data/README.markdown CHANGED
@@ -557,4 +557,4 @@ You can send feedback via [e-mail](mailto:karmi@karmi.cz) or via [Github Issues]
557
557
 
558
558
  -----
559
559
 
560
- [Karel Minarik](http://karmi.cz)
560
+ [Karel Minarik](http://karmi.cz) and [contributors](http://github.com/karmi/tire/contributors)
data/examples/tire-dsl.rb CHANGED
@@ -61,14 +61,56 @@ Tire.index 'articles' do
61
61
  create
62
62
 
63
63
  # We want to store and index some articles with `title`, `tags` and `published_on` properties.
64
- # Simple Hashes are OK.
64
+ # Simple Hashes are OK. The default type is „document”.
65
65
  #
66
66
  store :title => 'One', :tags => ['ruby'], :published_on => '2011-01-01'
67
67
  store :title => 'Two', :tags => ['ruby', 'python'], :published_on => '2011-01-02'
68
- store :title => 'Three', :tags => ['java'], :published_on => '2011-01-02'
69
- store :title => 'Four', :tags => ['ruby', 'php'], :published_on => '2011-01-03'
70
68
 
71
- # We force refreshing the index, so we can query it immediately.
69
+ # We usually want to set a specific _type_ for the document in _ElasticSearch_.
70
+ # Simply setting a `type` property is OK.
71
+ #
72
+ store :type => 'article',
73
+ :title => 'Three',
74
+ :tags => ['java'],
75
+ :published_on => '2011-01-02'
76
+
77
+ # We may want to wrap your data in a Ruby class, and use it when storing data.
78
+ # The contract required of such a class is very simple.
79
+ #
80
+ class Article
81
+
82
+ #
83
+ attr_reader :title, :tags, :published_on
84
+ def initialize(attributes={})
85
+ @attributes = attributes
86
+ @attributes.each_pair { |name,value| instance_variable_set :"@#{name}", value }
87
+ end
88
+
89
+ # It must provide a `type`, `_type` or `document_type` method for propper mapping.
90
+ #
91
+ def type
92
+ 'article'
93
+ end
94
+
95
+ # And it must provide a `to_indexed_json` method for conversion to JSON.
96
+ #
97
+ def to_indexed_json
98
+ @attributes.to_json
99
+ end
100
+ end
101
+
102
+ # Note: Since our class takes a Hash of attributes on initialization, we may even
103
+ # wrap the results in instances of this class; we'll see how to do that further below.
104
+ #
105
+ article = Article.new :title => 'Four',
106
+ :tags => ['ruby', 'php'],
107
+ :published_on => '2011-01-03'
108
+
109
+ # Let's store the `article`, now.
110
+ #
111
+ store article
112
+
113
+ # And let's „force refresh“ the index, so we can query it immediately.
72
114
  #
73
115
  refresh
74
116
  end
@@ -77,22 +119,23 @@ end
77
119
  # for the index.
78
120
 
79
121
  Tire.index 'articles' do
80
- # To do so, just pass a Hash containing the specified mapping to the `Index#create` method.
122
+ # To do so, let's just pass a Hash containing the specified mapping to the `Index#create` method.
81
123
  #
82
124
  create :mappings => {
83
125
 
84
- # Specify for which type of documents this mapping should be used.
126
+ # Let's specify for which _type_ of documents this mapping should be used:
127
+ # „article”, in our case.
85
128
  #
86
129
  :article => {
87
130
  :properties => {
88
131
 
89
- # Specify the type of the field, whether it should be analyzed, etc.
132
+ # Let's specify the type of the field, whether it should be analyzed, ...
90
133
  #
91
134
  :id => { :type => 'string', :index => 'not_analyzed', :include_in_all => false },
92
135
 
93
- # Set the boost or analyzer settings for the field, ... The _ElasticSearch_ guide
94
- # has [more information](http://elasticsearch.org/guide/reference/mapping/index.html)
95
- # about this. Proper mapping is key to efficient and effective search.
136
+ # ... set the boost or analyzer settings for the field, etc. The _ElasticSearch_ guide
137
+ # has [more information](http://elasticsearch.org/guide/reference/mapping/index.html).
138
+ # Don't forget, that proper mapping is key to efficient and effective search.
96
139
  # But don't fret about getting the mapping right the first time, you won't.
97
140
  # In most cases, the default, dynamic mapping is just fine for prototyping.
98
141
  #
@@ -116,10 +159,13 @@ articles = [
116
159
 
117
160
  # Notice that such objects must have an `id` property!
118
161
  #
119
- { :id => '1', :title => 'one', :tags => ['ruby'], :published_on => '2011-01-01' },
120
- { :id => '2', :title => 'two', :tags => ['ruby', 'python'], :published_on => '2011-01-02' },
121
- { :id => '3', :title => 'three', :tags => ['java'], :published_on => '2011-01-02' },
122
- { :id => '4', :title => 'four', :tags => ['ruby', 'php'], :published_on => '2011-01-03' }
162
+ { :id => '1', :type => 'article', :title => 'one', :tags => ['ruby'], :published_on => '2011-01-01' },
163
+
164
+ # And, of course, they should contain the `type` property for the mapping to work!
165
+ #
166
+ { :id => '2', :type => 'article', :title => 'two', :tags => ['ruby', 'python'], :published_on => '2011-01-02' },
167
+ { :id => '3', :type => 'article', :title => 'three', :tags => ['java'], :published_on => '2011-01-02' },
168
+ { :id => '4', :type => 'article', :title => 'four', :tags => ['ruby', 'php'], :published_on => '2011-01-03' }
123
169
  ]
124
170
 
125
171
  # We can just push them into the index in one go.
@@ -209,7 +255,7 @@ end
209
255
  # To do that, we have to use a slight variation of the DSL.
210
256
  #
211
257
 
212
- # Let's assume we have a plain Article class.
258
+ # Let's assume we have a plain Ruby class, named `Article`.
213
259
  #
214
260
  class Article
215
261
 
@@ -219,7 +265,7 @@ class Article
219
265
  "title:T*"
220
266
  end
221
267
 
222
- # ... and wrap the _Tire_ search method.
268
+ # ... and wrap the _Tire_ search method in another one.
223
269
  def self.search
224
270
 
225
271
  # Notice how we pass the `search` object around as a block argument.
@@ -298,13 +344,14 @@ Tire.configure do
298
344
  #
299
345
  url "http://search.example.com"
300
346
 
301
- # Second, we may want to wrap the result items in our own class.
347
+ # Second, we may want to wrap the result items in our own class, for instance
348
+ # the `Article` class set above.
302
349
  #
303
- class MySpecialWrapper; end
304
- wrapper MySpecialWrapper
350
+ wrapper Article
305
351
 
306
352
  # Finally, we can reset one or all configuration settings to their defaults.
307
353
  #
354
+ reset :url
308
355
  reset
309
356
 
310
357
  end
data/lib/tire/client.rb CHANGED
@@ -2,25 +2,7 @@ module Tire
2
2
 
3
3
  module Client
4
4
 
5
- class Base
6
- def get(url, data=nil)
7
- raise_no_method_error
8
- end
9
- def post(url, data)
10
- raise_no_method_error
11
- end
12
- def put(url, data)
13
- raise NoMethodError, "Implement this method in your client class"
14
- end
15
- def delete(url)
16
- raise_no_method_error
17
- end
18
- def raise_no_method_error
19
- raise NoMethodError, "Implement this method in your client class"
20
- end
21
- end
22
-
23
- class RestClient < Base
5
+ class RestClient
24
6
  def self.get(url, data=nil)
25
7
  ::RestClient::Request.new(:method => :get, :url => url, :payload => data).execute
26
8
  end
@@ -3,7 +3,7 @@ module Tire
3
3
  class Configuration
4
4
 
5
5
  def self.url(value=nil)
6
- @url = value || @url || "http://localhost:9200"
6
+ @url = (value ? value.to_s.gsub(%r|/*$|, '') : nil) || @url || "http://localhost:9200"
7
7
  end
8
8
 
9
9
  def self.client(klass=nil)
data/lib/tire/index.rb CHANGED
@@ -41,13 +41,20 @@ module Tire
41
41
  end
42
42
 
43
43
  def store(*args)
44
- # TODO: Infer type from the document (hash property, method)
45
- # TODO: Refactor common logic for getting id, JSON, into private methods
46
-
47
- case args.size
48
- when 3 then (type, document, options = args)
49
- when 2 then (type, document = args)
50
- else (document = args.pop; type = :document)
44
+ case
45
+ when ( args.size === 3 && (args.first.is_a?(String) || args.first.is_a?(Symbol)) )
46
+ Tire.warn "Passing the document type as argument in Index#store has been deprecated, " +
47
+ "please pass a Hash with _type/type property, or " +
48
+ "an object with _type/type/document_type method."
49
+ type, document, options = args
50
+ when ( args.size === 2 && (args.first.is_a?(String) || args.first.is_a?(Symbol)) )
51
+ Tire.warn "Passing the document type as argument in Index#store has been deprecated" +
52
+ "please pass a Hash with _type/type property, or " +
53
+ "an object with _type/type/document_type method."
54
+ type, document = args
55
+ else
56
+ document, options = args
57
+ type = get_type_from_document(document)
51
58
  end
52
59
 
53
60
  if options
@@ -55,18 +62,8 @@ module Tire
55
62
  percolate = "*" if percolate === true
56
63
  end
57
64
 
58
- old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings
59
- id = case true
60
- when document.is_a?(Hash) then document[:id] || document['id']
61
- when document.respond_to?(:id) && document.id != document.object_id then document.id
62
- end
63
- $VERBOSE = old_verbose
64
-
65
- document = case true
66
- when document.is_a?(String) then document
67
- when document.respond_to?(:to_indexed_json) then document.to_indexed_json
68
- else raise ArgumentError, "Please pass a JSON string or object with a 'to_indexed_json' method"
69
- end
65
+ id = get_id_from_document(document)
66
+ document = convert_document_to_json(document)
70
67
 
71
68
  url = id ? "#{Configuration.url}/#{@name}/#{type}/#{id}" : "#{Configuration.url}/#{@name}/#{type}/"
72
69
  url += "?percolate=#{percolate}" if percolate
@@ -83,22 +80,14 @@ module Tire
83
80
 
84
81
  def bulk_store documents
85
82
  payload = documents.map do |document|
86
- old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings
87
- id = case
88
- when document.is_a?(Hash) then document[:id] || document['id']
89
- when document.respond_to?(:id) && document.id != document.object_id then document.id
90
- # TODO: Raise error when no id present
91
- end
92
- $VERBOSE = old_verbose
83
+ id = get_id_from_document(document)
84
+ type = get_type_from_document(document)
93
85
 
94
- type = case
95
- when document.is_a?(Hash) then document[:type] || document['type']
96
- when document.respond_to?(:document_type) then document.document_type
97
- end || 'document'
86
+ STDERR.puts "[ERROR] Document #{document.inspect} does not have ID" unless id
98
87
 
99
88
  output = []
100
89
  output << %Q|{"index":{"_index":"#{@name}","_type":"#{type}","_id":"#{id}"}}|
101
- output << document.to_indexed_json
90
+ output << convert_document_to_json(document)
102
91
  output.join("\n")
103
92
  end
104
93
  payload << ""
@@ -107,8 +96,6 @@ module Tire
107
96
  count = 0
108
97
 
109
98
  begin
110
- # STDERR.puts "Posting payload..."
111
- # STDERR.puts payload.join("\n")
112
99
  Configuration.client.post("#{Configuration.url}/_bulk", payload.join("\n"))
113
100
  rescue Exception => error
114
101
  if count < tries
@@ -127,14 +114,11 @@ module Tire
127
114
  end
128
115
 
129
116
  def import(klass_or_collection, method=nil, options={})
130
- # p [klass_or_collection, method, options]
131
-
132
117
  case
133
-
134
118
  when method
135
119
  options = {:page => 1, :per_page => 1000}.merge options
136
120
  while documents = klass_or_collection.send(method.to_sym, options.merge(:page => options[:page])) \
137
- and not documents.empty?
121
+ and documents.size > 0
138
122
  documents = yield documents if block_given?
139
123
 
140
124
  bulk_store documents
@@ -144,34 +128,31 @@ module Tire
144
128
  when klass_or_collection.respond_to?(:map)
145
129
  documents = block_given? ? yield(klass_or_collection) : klass_or_collection
146
130
  bulk_store documents
131
+
147
132
  else
148
- raise ArgumentError, "Please pass either a collection of objects, "+
133
+ raise ArgumentError, "Please pass either a collection of objects, " +
149
134
  "or method for fetching records, or Enumerable compatible class"
150
135
  end
151
136
  end
152
137
 
153
138
  def remove(*args)
154
- # TODO: Infer type from the document (hash property, method)
155
-
156
139
  if args.size > 1
157
- (type, document = args)
140
+ type, document = args
141
+ id = get_id_from_document(document) || document
158
142
  else
159
- (document = args.pop; type = :document)
143
+ document = args.pop
144
+ type = get_type_from_document(document)
145
+ id = get_id_from_document(document) || document
160
146
  end
161
-
162
- old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings
163
- id = case true
164
- when document.is_a?(Hash) then document[:id] || document['id']
165
- when document.respond_to?(:id) && document.id != document.object_id then document.id
166
- else document
167
- end
168
- $VERBOSE = old_verbose
147
+ raise ArgumentError, "Please pass a document ID" unless id
169
148
 
170
149
  result = Configuration.client.delete "#{Configuration.url}/#{@name}/#{type}/#{id}"
171
150
  MultiJson.decode(result) if result
172
151
  end
173
152
 
174
153
  def retrieve(type, id)
154
+ raise ArgumentError, "Please pass a document ID" unless id
155
+
175
156
  @response = Configuration.client.get "#{Configuration.url}/#{@name}/#{type}/#{id}"
176
157
  h = MultiJson.decode(@response.body)
177
158
  if Configuration.wrapper == Hash then h
@@ -237,19 +218,17 @@ module Tire
237
218
  end
238
219
 
239
220
  def percolate(*args, &block)
240
- # TODO: Infer type from the document (hash property, method)
241
-
242
221
  if args.size > 1
243
- (type, document = args)
222
+ Tire.warn "Passing the document type as argument in Index#percolate has been deprecated, " +
223
+ "please pass a Hash with _type/type property, or " +
224
+ "an object with _type/type/document_type method."
225
+ type, document = args
244
226
  else
245
- (document = args.pop; type = :document)
227
+ document = args.pop
228
+ type = get_type_from_document(document)
246
229
  end
247
230
 
248
- document = case true
249
- when document.is_a?(String) then document
250
- when document.respond_to?(:to_hash) then document.to_hash
251
- else raise ArgumentError, "Please pass a JSON string or object with a 'to_hash' method"
252
- end
231
+ document = MultiJson.decode convert_document_to_json(document)
253
232
 
254
233
  query = Search::Query.new(&block).to_hash if block_given?
255
234
 
@@ -288,5 +267,41 @@ module Tire
288
267
  end
289
268
  end
290
269
 
270
+ def get_type_from_document(document)
271
+ old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#type deprecation warnings
272
+ type = case
273
+ when document.respond_to?(:document_type)
274
+ document.document_type
275
+ when document.is_a?(Hash)
276
+ document.delete(:_type) || document.delete('_type') || document.delete(:type) || document.delete('type')
277
+ when document.respond_to?(:_type)
278
+ document._type
279
+ when document.respond_to?(:type) && document.type != document.class
280
+ document.type
281
+ end
282
+ $VERBOSE = old_verbose
283
+ type ||= :document
284
+ end
285
+
286
+ def get_id_from_document(document)
287
+ old_verbose, $VERBOSE = $VERBOSE, nil # Silence Object#id deprecation warnings
288
+ id = case
289
+ when document.is_a?(Hash)
290
+ document[:_id] || document['_id'] || document[:id] || document['id']
291
+ when document.respond_to?(:id) && document.id != document.object_id
292
+ document.id
293
+ end
294
+ $VERBOSE = old_verbose
295
+ id
296
+ end
297
+
298
+ def convert_document_to_json(document)
299
+ document = case
300
+ when document.is_a?(String) then document
301
+ when document.respond_to?(:to_indexed_json) then document.to_indexed_json
302
+ else raise ArgumentError, "Please pass a JSON string or object with a 'to_indexed_json' method"
303
+ end
304
+ end
305
+
291
306
  end
292
307
  end
@@ -35,15 +35,14 @@ module Tire
35
35
  def search(query=nil, options={}, &block)
36
36
  old_wrapper = Tire::Configuration.wrapper
37
37
  Tire::Configuration.wrapper self
38
- sort = options[:order] || options[:sort]
39
- sort = Array(sort)
38
+ sort = Array( options[:order] || options[:sort] )
40
39
  unless block_given?
41
40
  s = Tire::Search::Search.new(elasticsearch_index.name, options)
42
41
  s.query { string query }
43
42
  s.sort do
44
43
  sort.each do |t|
45
44
  field_name, direction = t.split(' ')
46
- field_name.include?('.') ? field(field_name, direction) : send(field_name, direction)
45
+ by field_name, direction
47
46
  end
48
47
  end unless sort.empty?
49
48
  s.size( options[:per_page].to_i ) if options[:per_page]
@@ -75,7 +74,7 @@ module Tire
75
74
  module InstanceMethods
76
75
 
77
76
  def score
78
- STDERR.puts "DEPRECATED! Please use #{self.class}#_score instead."
77
+ Tire.warn "#{self.class}#score has been deprecated, please use #{self.class}#_score instead."
79
78
  attributes['_score']
80
79
  end
81
80
 
@@ -86,9 +85,9 @@ module Tire
86
85
  def update_elastic_search_index
87
86
  _run_update_elastic_search_index_callbacks do
88
87
  if destroyed?
89
- index.remove document_type, self
88
+ index.remove self
90
89
  else
91
- response = index.store( document_type, self, {:percolate => self.percolator} )
90
+ response = index.store( self, {:percolate => self.percolator} )
92
91
  self.id ||= response['_id'] if self.respond_to?(:id=)
93
92
  self._index = response['_index']
94
93
  self._type = response['_type']
@@ -98,6 +97,7 @@ module Tire
98
97
  end
99
98
  end
100
99
  end
100
+ alias :update_elasticsearch_index :update_elastic_search_index
101
101
 
102
102
  def to_indexed_json
103
103
  if self.class.mapping.empty?
@@ -4,20 +4,19 @@ module Tire
4
4
  class Sort
5
5
  def initialize(&block)
6
6
  @value = []
7
- self.instance_eval(&block) if block_given?
7
+ block.arity < 1 ? self.instance_eval(&block) : block.call(self) if block_given?
8
8
  end
9
9
 
10
- def field(name, direction=nil)
10
+ def by(name, direction=nil)
11
11
  @value << ( direction ? { name => direction } : name )
12
12
  self
13
13
  end
14
14
 
15
15
  def method_missing(id, *args, &block)
16
- case arg = args.shift
17
- when String, Symbol, Hash then @value << { id => arg }
18
- else @value << id
19
- end
20
- self
16
+ Tire.warn "Using methods when sorting has been deprecated, please use the `by` method: " +
17
+ "sort { by :#{id}#{ args.empty? ? '' : ', ' + args.first.inspect } }"
18
+
19
+ by id, args.shift
21
20
  end
22
21
 
23
22
  def to_ary
data/lib/tire/version.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  module Tire
2
- VERSION = "0.1.14"
2
+ VERSION = "0.1.15"
3
3
 
4
4
  CHANGELOG =<<-END
5
5
  IMPORTANT CHANGES LATELY:
6
6
 
7
- # Added `<after/before>_update_elastic_search_index` callbacks for models
8
- # Added percolator support for DSL and ActiveModel
9
- # Lazy loading of ResultsCollection into memory
10
- # Fixes for debug logging
7
+ # Cleanup of code for getting document type, id, JSON serialization
8
+ # Bunch of deprecations: sorting, passing document type to store/remove
9
+ # Displaying a warning when no ID is passed when storing in bulk
10
+ # Correct handling of import for Mongoid/Kaminari combos
11
11
  END
12
12
  end
data/lib/tire.rb CHANGED
@@ -7,7 +7,6 @@ require 'tire/rubyext/symbol'
7
7
  require 'tire/logger'
8
8
  require 'tire/configuration'
9
9
  require 'tire/client'
10
- require 'tire/client'
11
10
  require 'tire/search'
12
11
  require 'tire/search/query'
13
12
  require 'tire/search/sort'
@@ -33,4 +32,10 @@ require 'tire/tasks'
33
32
 
34
33
  module Tire
35
34
  extend DSL
35
+
36
+ def warn(message)
37
+ line = caller.detect { |line| line !~ %r|lib\/tire\/| }.sub(/:in .*/, '')
38
+ STDERR.puts "", "\e[31m[DEPRECATION WARNING] #{message}", "(Called from #{line})", "\e[0m"
39
+ end
40
+ module_function :warn
36
41
  end
@@ -132,7 +132,7 @@ module Tire
132
132
  should "find first page with five results" do
133
133
  results = ActiveRecordArticle.search do |search|
134
134
  search.query { |query| query.string @q }
135
- search.sort { title }
135
+ search.sort { by :title }
136
136
  search.from 0
137
137
  search.size 5
138
138
  end
@@ -149,7 +149,7 @@ module Tire
149
149
  should "find next page with five results" do
150
150
  results = ActiveRecordArticle.search do |search|
151
151
  search.query { |query| query.string @q }
152
- search.sort { title }
152
+ search.sort { by :title }
153
153
  search.from 5
154
154
  search.size 5
155
155
  end
@@ -166,7 +166,7 @@ module Tire
166
166
  should "not find a missing page" do
167
167
  results = ActiveRecordArticle.search do |search|
168
168
  search.query { |query| query.string @q }
169
- search.sort { title }
169
+ search.sort { by :title }
170
170
  search.from 10
171
171
  search.size 5
172
172
  end
@@ -12,7 +12,7 @@ module Tire
12
12
 
13
13
  index = Tire.index 'mapped-index' do
14
14
  create
15
- store :article, :title => 'One'
15
+ store :type => :article, :title => 'One'
16
16
  refresh
17
17
  end
18
18
  sleep 1.5
@@ -83,17 +83,17 @@ module Tire
83
83
  end
84
84
 
85
85
  should "return an empty array when no query matches" do
86
- response = @index.store :document, {:message => 'Situation normal'}, {:percolate => true}
86
+ response = @index.store( {:message => 'Situation normal'}, {:percolate => true} )
87
87
  assert_equal [], response['matches']
88
88
  end
89
89
 
90
90
  should "return an array of matching query names" do
91
- response = @index.store :document, {:message => 'Severe weather warning'}, {:percolate => true}
91
+ response = @index.store( {:message => 'Severe weather warning'}, {:percolate => true} )
92
92
  assert_equal ['alert','weather'], response['matches'].sort
93
93
  end
94
94
 
95
95
  should "return an array of matching query names for specific percolated queries" do
96
- response = @index.store :document, {:message => 'Severe weather warning'}, {:percolate => 'tags:weather'}
96
+ response = @index.store( {:message => 'Severe weather warning'}, {:percolate => 'tags:weather'} )
97
97
  assert_equal ['weather'], response['matches']
98
98
  end
99
99
  end
@@ -11,7 +11,7 @@ module Tire
11
11
  q = '*'
12
12
  s = Tire.search('articles-test') do
13
13
  query { string q }
14
- sort { title }
14
+ sort { by :title }
15
15
  end
16
16
 
17
17
  assert_equal 5, s.results.count
@@ -22,7 +22,7 @@ module Tire
22
22
  q = '*'
23
23
  s = Tire.search('articles-test') do
24
24
  query { string q }
25
- sort { title :desc }
25
+ sort { by :title, :desc }
26
26
  end
27
27
 
28
28
  assert_equal 5, s.results.count
@@ -4,25 +4,6 @@ module Tire
4
4
 
5
5
  class ClientTest < Test::Unit::TestCase
6
6
 
7
- context "Base" do
8
- setup { @http ||= Client::Base.new }
9
-
10
- should "have abstract methods" do
11
- assert_raise(ArgumentError) { @http.get }
12
- assert_raise(NoMethodError) { @http.get 'URL' }
13
-
14
- assert_raise(ArgumentError) { @http.post }
15
- assert_raise(ArgumentError) { @http.post 'URL' }
16
- assert_raise(NoMethodError) { @http.post 'URL', 'DATA' }
17
-
18
- assert_raise(ArgumentError) { @http.put }
19
- assert_raise(ArgumentError) { @http.put 'URL' }
20
-
21
- assert_raise(ArgumentError) { @http.delete }
22
- assert_raise(NoMethodError) { @http.delete 'URL' }
23
- end
24
- end
25
-
26
7
  context "RestClient" do
27
8
 
28
9
  should "be default" do
@@ -27,13 +27,13 @@ module Tire
27
27
  assert_equal 'http://example.com', Configuration.url
28
28
  end
29
29
 
30
- should "return default client" do
31
- assert_equal Client::RestClient, Configuration.client
30
+ should "strip trailing slash from the URL" do
31
+ assert_nothing_raised { Configuration.url 'http://slash.com:9200/' }
32
+ assert_equal 'http://slash.com:9200', Configuration.url
32
33
  end
33
34
 
34
- should "allow setting and retrieving the client" do
35
- assert_nothing_raised { Configuration.client Client::Base }
36
- assert_equal Client::Base, Configuration.client
35
+ should "return default client" do
36
+ assert_equal Client::RestClient, Configuration.client
37
37
  end
38
38
 
39
39
  should "return nil as logger by default" do
@@ -48,21 +48,18 @@ module Tire
48
48
 
49
49
  should "allow to reset the configuration for specific property" do
50
50
  Configuration.url 'http://example.com'
51
- Configuration.client Client::Base
52
- assert_equal 'http://example.com', Configuration.url
51
+ assert_equal 'http://example.com', Configuration.url
53
52
  Configuration.reset :url
54
- assert_equal 'http://localhost:9200', Configuration.url
55
- assert_equal Client::Base, Configuration.client
53
+ assert_equal 'http://localhost:9200', Configuration.url
56
54
  end
57
55
 
58
56
  should "allow to reset the configuration for all properties" do
59
- Configuration.url 'http://example.com'
60
- Configuration.client Client::Base
61
- assert_equal 'http://example.com', Configuration.url
62
- assert_equal Client::Base, Configuration.client
57
+ Configuration.url 'http://example.com'
58
+ Configuration.wrapper Hash
59
+ assert_equal 'http://example.com', Configuration.url
63
60
  Configuration.reset
64
- assert_equal 'http://localhost:9200', Configuration.url
65
- assert_equal Client::RestClient, Configuration.client
61
+ assert_equal 'http://localhost:9200', Configuration.url
62
+ assert_equal Client::RestClient, Configuration.client
66
63
  end
67
64
  end
68
65
 
@@ -101,10 +101,42 @@ module Tire
101
101
 
102
102
  context "when storing" do
103
103
 
104
- should "properly set type from args" do
105
- Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/", '{"title":"Test"}').returns(mock_response('{"ok":true,"_id":"test"}')).twice
106
- @index.store 'article', :title => 'Test'
107
- @index.store :article, :title => 'Test'
104
+ should "set type from Hash :type property" do
105
+ Configuration.client.expects(:post).with do |url,document|
106
+ url == "#{Configuration.url}/dummy/article/"
107
+ end.returns(mock_response('{"ok":true,"_id":"test"}'))
108
+ @index.store :type => 'article', :title => 'Test'
109
+ end
110
+
111
+ should "set type from Hash :_type property" do
112
+ Configuration.client.expects(:post).with do |url,document|
113
+ url == "#{Configuration.url}/dummy/article/"
114
+ end.returns(mock_response('{"ok":true,"_id":"test"}'))
115
+ @index.store :_type => 'article', :title => 'Test'
116
+ end
117
+
118
+ should "set type from Object _type method" do
119
+ Configuration.client.expects(:post).with do |url,document|
120
+ url == "#{Configuration.url}/dummy/article/"
121
+ end.returns(mock_response('{"ok":true,"_id":"test"}'))
122
+
123
+ article = Class.new do
124
+ def _type; 'article'; end
125
+ def to_indexed_json; "{}"; end
126
+ end.new
127
+ @index.store article
128
+ end
129
+
130
+ should "set type from Object type method" do
131
+ Configuration.client.expects(:post).with do |url,document|
132
+ url == "#{Configuration.url}/dummy/article/"
133
+ end.returns(mock_response('{"ok":true,"_id":"test"}'))
134
+
135
+ article = Class.new do
136
+ def type; 'article'; end
137
+ def to_indexed_json; "{}"; end
138
+ end.new
139
+ @index.store article
108
140
  end
109
141
 
110
142
  should "set default type" do
@@ -151,7 +183,7 @@ module Tire
151
183
 
152
184
  Configuration.client.stubs(:post).with("#{Configuration.url}/dummy/article/", '{"title":"Test"}').
153
185
  returns(mock_response('{"ok":true,"_id":"id-1"}'))
154
- @index.store :article, :title => 'Test'
186
+ @index.store :type => 'article', :title => 'Test'
155
187
  end
156
188
 
157
189
  should "return document in default wrapper" do
@@ -182,21 +214,27 @@ module Tire
182
214
  assert_equal 'Test', article.title
183
215
  end
184
216
 
217
+ should "raise error when no ID passed" do
218
+ assert_raise ArgumentError do
219
+ @index.retrieve 'article', nil
220
+ end
221
+ end
222
+
185
223
  end
186
224
 
187
225
  context "when removing" do
188
226
 
189
- should "properly set type from args" do
190
- Configuration.client.expects(:delete).with("#{Configuration.url}/dummy/article/").
191
- returns('{"ok":true,"_id":"test"}').twice
192
- @index.remove 'article', :title => 'Test'
193
- @index.remove :article, :title => 'Test'
227
+ should "get type from document" do
228
+ Configuration.client.expects(:delete).with("#{Configuration.url}/dummy/article/1").
229
+ returns('{"ok":true,"_id":"1"}').twice
230
+ @index.remove :id => 1, :type => 'article', :title => 'Test'
231
+ @index.remove :id => 1, :type => 'article', :title => 'Test'
194
232
  end
195
233
 
196
234
  should "set default type" do
197
- Configuration.client.expects(:delete).with("#{Configuration.url}/dummy/document/").
198
- returns('{"ok":true,"_id":"test"}')
199
- @index.remove :title => 'Test'
235
+ Configuration.client.expects(:delete).with("#{Configuration.url}/dummy/document/1").
236
+ returns('{"ok":true,"_id":"1"}')
237
+ @index.remove :id => 1, :title => 'Test'
200
238
  end
201
239
 
202
240
  should "get ID from hash" do
@@ -212,10 +250,16 @@ module Tire
212
250
  @index.remove document
213
251
  end
214
252
 
215
- should "get ID from arguments" do
216
- Configuration.client.expects(:delete).with("#{Configuration.url}/dummy/document/1").
253
+ should "get type and ID from arguments" do
254
+ Configuration.client.expects(:delete).with("#{Configuration.url}/dummy/article/1").
217
255
  returns('{"ok":true,"_id":"1"}')
218
- @index.remove :document, 1
256
+ @index.remove :article, 1
257
+ end
258
+
259
+ should "raise error when no ID passed" do
260
+ assert_raise ArgumentError do
261
+ @index.remove :document, nil
262
+ end
219
263
  end
220
264
 
221
265
  end
@@ -275,8 +319,12 @@ module Tire
275
319
  end
276
320
  end
277
321
 
278
- should_eventually "raise exception when collection item does not have ID" do
279
- # TODO: raise exception when collection item does not have ID
322
+ should "raise exception when collection item does not have ID" do
323
+ Configuration.client.expects(:post).with { |url, json| url == "#{Configuration.url}/_bulk" }
324
+ STDERR.expects(:puts).once
325
+
326
+ documents = [ { :title => 'Bogus' }, { :title => 'Real', :id => 1 } ]
327
+ ActiveModelArticle.elasticsearch_index.bulk_store documents
280
328
  end
281
329
 
282
330
  end
@@ -479,7 +527,7 @@ module Tire
479
527
  end.
480
528
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
481
529
 
482
- matches = @index.percolate :article, :title => 'Test'
530
+ matches = @index.percolate :type => 'article', :title => 'Test'
483
531
  assert_equal ["alerts"], matches
484
532
  end
485
533
 
@@ -502,13 +550,13 @@ module Tire
502
550
  should "percolate document against all registered queries" do
503
551
  Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/?percolate=*", '{"title":"Test"}').
504
552
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
505
- @index.store :article, {:title => 'Test'}, {:percolate => true}
553
+ @index.store( {:type => 'article', :title => 'Test'}, {:percolate => true} )
506
554
  end
507
555
 
508
556
  should "percolate document against specific queries" do
509
557
  Configuration.client.expects(:post).with("#{Configuration.url}/dummy/article/?percolate=tag:alerts", '{"title":"Test"}').
510
558
  returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
511
- response = @index.store :article, {:title => 'Test'}, {:percolate => 'tag:alerts'}
559
+ response = @index.store( {:type => 'article', :title => 'Test'}, {:percolate => 'tag:alerts'} )
512
560
  assert_equal response['matches'], ['alerts']
513
561
  end
514
562
 
@@ -96,7 +96,7 @@ module Tire
96
96
  document = collection.first
97
97
 
98
98
  assert_instance_of ActiveModelArticle, document
99
- assert_not_nil document.score
99
+ assert_not_nil document._score
100
100
  assert_equal 1, document.id
101
101
  assert_equal 'Article', document.title
102
102
  end
@@ -139,26 +139,26 @@ module Tire
139
139
  end
140
140
 
141
141
  should "allow to pass :order option" do
142
- Tire::Search::Sort.any_instance.expects(:title)
142
+ Tire::Search::Sort.any_instance.expects(:by).with('title', nil)
143
143
 
144
144
  ActiveModelArticle.search @q, :order => 'title'
145
145
  end
146
146
 
147
147
  should "allow to pass :sort option as :order option" do
148
- Tire::Search::Sort.any_instance.expects(:title)
148
+ Tire::Search::Sort.any_instance.expects(:by).with('title', nil)
149
149
 
150
150
  ActiveModelArticle.search @q, :sort => 'title'
151
151
  end
152
152
 
153
153
  should "allow to specify sort direction" do
154
- Tire::Search::Sort.any_instance.expects(:title).with('DESC')
154
+ Tire::Search::Sort.any_instance.expects(:by).with('title', 'DESC')
155
155
 
156
156
  ActiveModelArticle.search @q, :order => 'title DESC'
157
157
  end
158
158
 
159
159
  should "allow to specify more fields to sort on" do
160
- Tire::Search::Sort.any_instance.expects(:title).with('DESC')
161
- Tire::Search::Sort.any_instance.expects(:field).with('author.name', nil)
160
+ Tire::Search::Sort.any_instance.expects(:by).with('title', 'DESC')
161
+ Tire::Search::Sort.any_instance.expects(:by).with('author.name', nil)
162
162
 
163
163
  ActiveModelArticle.search @q, :order => ['title DESC', 'author.name']
164
164
  end
@@ -417,8 +417,8 @@ module Tire
417
417
  should "mark the instance for percolation on index update" do
418
418
  @article.percolate = true
419
419
 
420
- Tire::Index.any_instance.expects(:store).with do |type,doc,options|
421
- # p [type,doc,options]
420
+ Tire::Index.any_instance.expects(:store).with do |doc,options|
421
+ # p [doc,options]
422
422
  options[:percolate] == true
423
423
  end.returns(MultiJson.decode('{"ok":true,"_id":"test","matches":["alerts"]}'))
424
424
 
@@ -426,8 +426,8 @@ module Tire
426
426
  end
427
427
 
428
428
  should "not percolate document on index update when not set for percolation" do
429
- Tire::Index.any_instance.expects(:store).with do |type,doc,options|
430
- # p [type,doc,options]
429
+ Tire::Index.any_instance.expects(:store).with do |doc,options|
430
+ # p [doc,options]
431
431
  options[:percolate] == nil
432
432
  end.returns(MultiJson.decode('{"ok":true,"_id":"test"}'))
433
433
 
@@ -455,8 +455,8 @@ module Tire
455
455
  percolate!
456
456
  end
457
457
 
458
- Tire::Index.any_instance.expects(:store).with do |type,doc,options|
459
- # p [type,doc,options]
458
+ Tire::Index.any_instance.expects(:store).with do |doc,options|
459
+ # p [doc,options]
460
460
  options[:percolate] == true
461
461
  end.returns(MultiJson.decode('{"ok":true,"_id":"test","matches":["alerts"]}'))
462
462
 
@@ -472,7 +472,7 @@ module Tire
472
472
  percolated = ActiveModelArticleWithPercolation.new :title => 'Percolate me!'
473
473
 
474
474
  Tire::Index.any_instance.expects(:store).
475
- with do |type,doc,options|
475
+ with do |doc,options|
476
476
  doc == percolated &&
477
477
  options[:percolate] == true
478
478
  end.
@@ -491,7 +491,7 @@ module Tire
491
491
  percolated = ActiveModelArticleWithPercolation.new :title => 'Percolate me!'
492
492
 
493
493
  Tire::Index.any_instance.expects(:store).
494
- with do |type,doc,options|
494
+ with do |doc,options|
495
495
  doc == percolated &&
496
496
  options[:percolate] == 'tags:alert'
497
497
  end.
@@ -6,41 +6,74 @@ module Tire::Search
6
6
 
7
7
  context "Sort" do
8
8
 
9
- should "be serialized to JSON" do
10
- assert_respond_to Sort.new, :to_json
11
- end
9
+ context "with the old interface" do
12
10
 
13
- should "encode simple strings" do
14
- assert_equal [:foo].to_json, Sort.new.foo.to_json
15
- end
11
+ should "encode simple strings" do
12
+ assert_equal [:foo].to_json, Sort.new.foo.to_json
13
+ end
16
14
 
17
- should "encode method arguments" do
18
- assert_equal [:foo => 'desc'].to_json, Sort.new.foo('desc').to_json
19
- end
15
+ should "encode method arguments" do
16
+ assert_equal [:foo => 'desc'].to_json, Sort.new.foo('desc').to_json
17
+ end
20
18
 
21
- should "encode hash" do
22
- assert_equal [ :foo => { :reverse => true } ].to_json, Sort.new.foo(:reverse => true).to_json
23
- end
19
+ should "encode hash" do
20
+ assert_equal [ :foo => { :reverse => true } ].to_json, Sort.new.foo(:reverse => true).to_json
21
+ end
24
22
 
25
- should "encode multiple sort fields" do
26
- assert_equal [:foo, :bar].to_json, Sort.new.foo.bar.to_json
27
- end
23
+ should "encode multiple sort fields" do
24
+ assert_equal [:foo, :bar].to_json, Sort.new.foo.bar.to_json
25
+ end
28
26
 
29
- should "encode fields when passed as a block to constructor" do
30
- s = Sort.new do
31
- foo
32
- bar 'desc'
33
- _score
27
+ should "encode fields when passed as a block to constructor with deprecated interface" do
28
+ s = Sort.new do
29
+ foo
30
+ bar 'desc'
31
+ _score
32
+ end
33
+ assert_equal [ :foo, {:bar => 'desc'}, :_score ].to_json, s.to_json
34
34
  end
35
- assert_equal [ :foo, {:bar => 'desc'}, :_score ].to_json, s.to_json
35
+
36
36
  end
37
37
 
38
- should "encode fields deeper in json" do
39
- s = Sort.new { field 'author.name' }
40
- assert_equal [ 'author.name' ].to_json, s.to_json
38
+ context "with the new interface" do
39
+
40
+ should "be serialized to JSON" do
41
+ assert_respond_to Sort.new, :to_json
42
+ end
43
+
44
+ should "encode simple strings" do
45
+ assert_equal [:foo].to_json, Sort.new.by(:foo).to_json
46
+ end
47
+
48
+ should "encode method arguments" do
49
+ assert_equal [:foo => 'desc'].to_json, Sort.new.by(:foo, 'desc').to_json
50
+ end
51
+
52
+ should "encode hash" do
53
+ assert_equal [ :foo => { :reverse => true } ].to_json, Sort.new.by(:foo, :reverse => true).to_json
54
+ end
55
+
56
+ should "encode multiple sort fields in chain" do
57
+ assert_equal [:foo, :bar].to_json, Sort.new.by(:foo).by(:bar).to_json
58
+ end
59
+
60
+ should "encode fields when passed as a block to constructor" do
61
+ s = Sort.new do
62
+ by :foo
63
+ by :bar, 'desc'
64
+ by :_score
65
+ end
66
+ assert_equal [ :foo, {:bar => 'desc'}, :_score ].to_json, s.to_json
67
+ end
68
+
69
+ should "encode fields deeper in json" do
70
+ s = Sort.new { by 'author.name' }
71
+ assert_equal [ 'author.name' ].to_json, s.to_json
72
+
73
+ s = Sort.new { by 'author.name', 'desc' }
74
+ assert_equal [ {'author.name' => 'desc'} ].to_json, s.to_json
75
+ end
41
76
 
42
- s = Sort.new { field 'author.name', :desc }
43
- assert_equal [ {'author.name' => 'desc'} ].to_json, s.to_json
44
77
  end
45
78
 
46
79
  end
@@ -67,7 +67,11 @@ module Tire
67
67
 
68
68
  should "allow chaining" do
69
69
  assert_nothing_raised do
70
- Search::Search.new('index').query { }.sort { title 'desc' }.size(5).sort { name 'asc' }.from(1)
70
+ Search::Search.new('index').query { }.
71
+ sort { by :title, 'desc' }.
72
+ size(5).
73
+ sort { by :name, 'asc' }.
74
+ from(1)
71
75
  end
72
76
  end
73
77
 
@@ -108,8 +112,8 @@ module Tire
108
112
  should "allow sorting by multiple fields" do
109
113
  s = Search::Search.new('index') do
110
114
  sort do
111
- title 'desc'
112
- _score
115
+ by :title, 'desc'
116
+ by :_score
113
117
  end
114
118
  end
115
119
  hash = MultiJson.decode( s.to_json )
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tire
3
3
  version: !ruby/object:Gem::Version
4
- hash: 7
4
+ hash: 5
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 14
10
- version: 0.1.14
9
+ - 15
10
+ version: 0.1.15
11
11
  platform: ruby
12
12
  authors:
13
13
  - Karel Minarik
@@ -15,8 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-06-27 00:00:00 +02:00
19
- default_executable:
18
+ date: 2011-07-04 00:00:00 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: rake
@@ -336,7 +335,6 @@ files:
336
335
  - test/unit/search_test.rb
337
336
  - test/unit/tire_test.rb
338
337
  - tire.gemspec
339
- has_rdoc: true
340
338
  homepage: http://github.com/karmi/tire
341
339
  licenses: []
342
340
 
@@ -349,12 +347,12 @@ post_install_message: |
349
347
 
350
348
  IMPORTANT CHANGES LATELY:
351
349
 
352
- # Added `<after/before>_update_elastic_search_index` callbacks for models
353
- # Added percolator support for DSL and ActiveModel
354
- # Lazy loading of ResultsCollection into memory
355
- # Fixes for debug logging
350
+ # Cleanup of code for getting document type, id, JSON serialization
351
+ # Bunch of deprecations: sorting, passing document type to store/remove
352
+ # Displaying a warning when no ID is passed when storing in bulk
353
+ # Correct handling of import for Mongoid/Kaminari combos
356
354
 
357
- See the full changelog at <http://github.com/karmi/tire/commits/v0.1.14>.
355
+ See the full changelog at <http://github.com/karmi/tire/commits/v0.1.15>.
358
356
 
359
357
  --------------------------------------------------------------------------------
360
358
 
@@ -385,7 +383,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
385
383
  requirements: []
386
384
 
387
385
  rubyforge_project: tire
388
- rubygems_version: 1.5.0
386
+ rubygems_version: 1.8.5
389
387
  signing_key:
390
388
  specification_version: 3
391
389
  summary: Ruby client for ElasticSearch