tire 0.3.3 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -134,6 +134,17 @@ We can easily manipulate the documents before storing them in the index, by pass
134
134
  end
135
135
  ```
136
136
 
137
+ If this _declarative_ notation does not fit well in your context,
138
+ you can use _Tire's_ classes directly, in a more imperative manner:
139
+
140
+ ```ruby
141
+ index = Tire::Index.new('oldskool')
142
+ index.delete
143
+ index.create
144
+ index.store :title => "Let's do it the old way!"
145
+ index.refresh
146
+ ```
147
+
137
148
  OK. Now, let's go search all the data.
138
149
 
139
150
  We will be searching for articles whose `title` begins with letter “T”, sorted by `title` in `descending` order,
@@ -281,7 +292,21 @@ a plain old Ruby `Hash` (or JSON string) with the query declaration to the `sear
281
292
  If this sounds like a great idea to you, you are probably able to write your application
282
293
  using just `curl`, `sed` and `awk`.
283
294
 
284
- For debugging purposes, we can display the full query JSON for close inspection:
295
+ Do note again, however, that you're not tied to the declarative block-style DSL _Tire_ offers to you.
296
+ If it makes more sense in your context, you can use its classes directly, in a more imperative style:
297
+
298
+ ```ruby
299
+ search = Tire::Search::Search.new('articles')
300
+ search.query { string('title:T*') }
301
+ search.filter :terms, :tags => ['ruby']
302
+ search.sort { by :title, 'desc' }
303
+ search.facet('global-tags') { terms :tags, :global => true }
304
+ # ...
305
+ p search.perform.results
306
+ ```
307
+
308
+ To debug the query we have laboriously set up like this,
309
+ we can display the full query JSON for close inspection:
285
310
 
286
311
  ```ruby
287
312
  puts s.to_json
@@ -692,6 +717,14 @@ This will result in Article instances being stored in an index called 'test_arti
692
717
  Please be sure to peruse the [integration test suite](https://github.com/karmi/tire/tree/master/test/integration)
693
718
  for examples of the API and _ActiveModel_ integration usage.
694
719
 
720
+
721
+ Extensions and Additions
722
+ ------------------------
723
+
724
+ The [_tire-contrib_](http://github.com/karmi/tire-contrib/) project contains additions
725
+ and extensions to the _Tire_ functionality.
726
+
727
+
695
728
  Todo, Plans & Ideas
696
729
  -------------------
697
730
 
@@ -699,8 +732,6 @@ _Tire_ is already used in production by its authors. Nevertheless, it's not cons
699
732
 
700
733
  There are todos, plans and ideas, some of which are listed below, in the order of importance:
701
734
 
702
- * Wrap all Tire functionality mixed into a model in a "forwardable" object, and proxy everything via this object. (The immediate problem: [Mongoid](http://mongoid.org/docs/indexing.html))
703
- * If we're not stepping on other's toes, bring Tire methods like `index`, `search`, `mapping` also to the class/instance top-level namespace.
704
735
  * Proper RDoc annotations for the source code
705
736
  * [Statistical](http://www.elasticsearch.org/guide/reference/api/search/facets/statistical-facet.html) facets
706
737
  * [Geo Distance](http://www.elasticsearch.org/guide/reference/api/search/facets/geo-distance-facet.html) facets
data/examples/tire-dsl.rb CHANGED
@@ -697,7 +697,7 @@ s = Tire.search 'articles' do
697
697
 
698
698
  # and by their `title` property, in _descending_ order.
699
699
  #
700
- by :title 'desc'
700
+ by :title, 'desc'
701
701
  end
702
702
  end
703
703
 
@@ -8,31 +8,31 @@ module Tire
8
8
 
9
9
  def self.get(url, data=nil)
10
10
  perform ::RestClient::Request.new(:method => :get, :url => url, :payload => data).execute
11
- rescue Exception => e
11
+ rescue ::RestClient::Exception => e
12
12
  Response.new e.http_body, e.http_code
13
13
  end
14
14
 
15
15
  def self.post(url, data)
16
16
  perform ::RestClient.post(url, data)
17
- rescue Exception => e
17
+ rescue ::RestClient::Exception => e
18
18
  Response.new e.http_body, e.http_code
19
19
  end
20
20
 
21
21
  def self.put(url, data)
22
22
  perform ::RestClient.put(url, data)
23
- rescue Exception => e
23
+ rescue ::RestClient::Exception => e
24
24
  Response.new e.http_body, e.http_code
25
25
  end
26
26
 
27
27
  def self.delete(url)
28
28
  perform ::RestClient.delete(url)
29
- rescue Exception => e
29
+ rescue ::RestClient::Exception => e
30
30
  Response.new e.http_body, e.http_code
31
31
  end
32
32
 
33
33
  def self.head(url)
34
34
  perform ::RestClient.head(url)
35
- rescue Exception => e
35
+ rescue ::RestClient::Exception => e
36
36
  Response.new e.http_body, e.http_code
37
37
  end
38
38
 
data/lib/tire/index.rb CHANGED
@@ -9,7 +9,11 @@ module Tire
9
9
  end
10
10
 
11
11
  def exists?
12
- Configuration.client.head("#{Configuration.url}/#{@name}").success?
12
+ @response = Configuration.client.head("#{Configuration.url}/#{@name}")
13
+ @response.success?
14
+ ensure
15
+ curl = %Q|curl -I "#{Configuration.url}/#{@name}"|
16
+ logged(@response.body, 'HEAD', curl) if @response
13
17
  end
14
18
 
15
19
  def delete
@@ -26,7 +30,7 @@ module Tire
26
30
  @response.success? ? @response : false
27
31
  ensure
28
32
  curl = %Q|curl -X POST "#{Configuration.url}/#{@name}" -d '#{MultiJson.encode(options)}'|
29
- logged(@response.body, 'CREATE', curl)
33
+ logged(@response.body, 'CREATE', curl) if @response
30
34
  end
31
35
 
32
36
  def mapping
@@ -230,7 +234,7 @@ module Tire
230
234
  if Configuration.logger.level.to_s == 'debug'
231
235
  # FIXME: Depends on RestClient implementation
232
236
  body = if @response
233
- defined?(Yajl) ? Yajl::Encoder.encode(@json, :pretty => true) : MultiJson.encode(@json)
237
+ defined?(Yajl) ? Yajl::Encoder.encode(@response.body, :pretty => true) : MultiJson.encode(@response.body)
234
238
  else
235
239
  error.http_body rescue ''
236
240
  end
@@ -9,9 +9,6 @@ module Tire
9
9
  # The model must respond to `after_save` and `after_destroy` callbacks
10
10
  # (ActiveModel and ActiveRecord models do so, by default).
11
11
  #
12
- # By including the model, you will also have the `after_update_elasticsearch_index` and
13
- # `before_update_elasticsearch_index` callbacks available.
14
- #
15
12
  module Callbacks
16
13
 
17
14
  # A hook triggered by the `include Tire::Model::Callbacks` statement in the model.
@@ -35,11 +32,6 @@ module Tire
35
32
  end
36
33
  end
37
34
 
38
- # Define _Tire's_ callbacks.
39
- #
40
- base.class_eval do
41
- define_model_callbacks(:update_elasticsearch_index, :only => [:after, :before])
42
- end if base.respond_to?(:define_model_callbacks)
43
35
  end
44
36
 
45
37
  end
@@ -92,6 +92,9 @@ module Tire
92
92
  unless index.exists?
93
93
  index.create :mappings => mapping_to_hash, :settings => settings
94
94
  end
95
+ rescue Errno::ECONNREFUSED => e
96
+ STDERR.puts "Skipping index creation, cannot connect to ElasticSearch",
97
+ "(The original exception was: #{e.inspect})"
95
98
  end
96
99
 
97
100
  def store_mapping?
@@ -20,8 +20,16 @@ module Tire
20
20
  #
21
21
  # Article.index_name 'my-custom-name'
22
22
  #
23
- def index_name name=nil
23
+ # You can also use a block for defining the index name,
24
+ # which is evaluated in the class context:
25
+ #
26
+ # Article.index_name { "articles-#{Time.now.year}" }
27
+ #
28
+ # Article.index_name { "articles-#{Rails.env}" }
29
+ #
30
+ def index_name name=nil, &block
24
31
  @index_name = name if name
32
+ @index_name = block if block_given?
25
33
  @index_name || [index_prefix, klass.model_name.plural].compact.join('_')
26
34
  end
27
35
 
@@ -22,7 +22,7 @@ module Tire
22
22
  else
23
23
  case args = args.pop
24
24
  when Fixnum, String
25
- Index.new(index_name).retrieve document_type, args
25
+ index.retrieve document_type, args
26
26
  when :all, :first
27
27
  send(args)
28
28
  else
@@ -37,7 +37,7 @@ module Tire
37
37
  # TODO: Options like `sort`; Possibly `filters`
38
38
  old_wrapper = Tire::Configuration.wrapper
39
39
  Tire::Configuration.wrapper self
40
- s = Tire::Search::Search.new(index_name).query { all }
40
+ s = Tire::Search::Search.new(index.name).query { all }
41
41
  s.perform.results
42
42
  ensure
43
43
  Tire::Configuration.wrapper old_wrapper
@@ -47,7 +47,7 @@ module Tire
47
47
  # TODO: Options like `sort`; Possibly `filters`
48
48
  old_wrapper = Tire::Configuration.wrapper
49
49
  Tire::Configuration.wrapper self
50
- s = Tire::Search::Search.new(index_name).query { all }.size(1)
50
+ s = Tire::Search::Search.new(index.name).query { all }.size(1)
51
51
  s.perform.results.first
52
52
  ensure
53
53
  Tire::Configuration.wrapper old_wrapper
@@ -25,10 +25,6 @@ module Tire
25
25
  document
26
26
  end
27
27
 
28
- def index
29
- @index = Index.new(index_name)
30
- end
31
-
32
28
  end
33
29
 
34
30
  module InstanceMethods
@@ -99,7 +99,8 @@ module Tire
99
99
  # Example usage: `Article.index.refresh`.
100
100
  #
101
101
  def index
102
- @index = Index.new(index_name)
102
+ name = index_name.respond_to?(:to_proc) ? klass.instance_eval(&index_name) : index_name
103
+ @index = Index.new(name)
103
104
  end
104
105
 
105
106
  end
@@ -120,8 +121,7 @@ module Tire
120
121
  #
121
122
  # On model destroy, it will remove the corresponding document from the index.
122
123
  #
123
- # It will also call the `<after|before>_update_elasticsearch_index` callbacks,
124
- # if defined by the model.
124
+ # It will also execute any `<after|before>_update_elasticsearch_index` callback hooks.
125
125
  #
126
126
  def update_index
127
127
  instance.send :_run_update_elasticsearch_index_callbacks do
@@ -237,6 +237,11 @@ module Tire
237
237
  @__tire__
238
238
  end
239
239
 
240
+ # Define _Tire's_ callbacks (<after|before>_update_elasticsearch_index).
241
+ #
242
+ define_model_callbacks(:update_elasticsearch_index, :only => [:after, :before]) if \
243
+ respond_to?(:define_model_callbacks)
244
+
240
245
  # Serialize the model as a Hash.
241
246
  #
242
247
  # Uses `serializable_hash` representation of the model,
@@ -1,6 +1,8 @@
1
1
  module Tire
2
2
  module Results
3
3
 
4
+ # Adds support for WillPaginate and Kaminari
5
+ #
4
6
  module Pagination
5
7
 
6
8
  def total_entries
@@ -39,6 +41,13 @@ module Tire
39
41
  current_page > total_pages
40
42
  end
41
43
 
44
+ # Kaminari support
45
+ #
46
+ alias :limit_value :per_page
47
+ alias :total_count :total_entries
48
+ alias :num_pages :total_pages
49
+ alias :offset_value :offset
50
+
42
51
  end
43
52
 
44
53
  end
data/lib/tire/search.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module Tire
2
2
  module Search
3
+ class SearchRequestFailed < StandardError; end
3
4
 
4
5
  class Search
5
6
 
@@ -70,7 +71,7 @@ module Tire
70
71
  @response = Configuration.client.get(@url, self.to_json)
71
72
  if @response.failure?
72
73
  STDERR.puts "[REQUEST FAILED] #{self.to_curl}\n"
73
- return false
74
+ raise SearchRequestFailed
74
75
  end
75
76
  @json = MultiJson.decode(@response.body)
76
77
  @results = Results::Collection.new(@json, @options)
data/lib/tire/version.rb CHANGED
@@ -1,34 +1,14 @@
1
1
  module Tire
2
- VERSION = "0.3.3"
2
+ VERSION = "0.3.4"
3
3
 
4
4
  CHANGELOG =<<-END
5
5
  IMPORTANT CHANGES LATELY:
6
6
 
7
- 0.2.0
7
+ 0.3.4
8
8
  ---------------------------------------------------------
9
- # By default, results are wrapped in Item class
10
- # Completely rewritten ActiveModel/ActiveRecord support
11
- # Added infrastructure for loading "real" models from database (eagerly or in runtime)
12
- # Deprecated the dynamic sort methods in favour of the 'sort { by :field_name }' syntax
13
-
14
- 0.2.1
15
- ---------------------------------------------------------
16
- # Lighweight check for index presence
17
- # Added the 'settings' method for models to define index settings
18
- # Fixed errors when importing data with will_paginate vs Kaminari (MongoDB)
19
- # Added support for histogram facets [Paco Guzman]
20
-
21
- 0.3.0
22
- ---------------------------------------------------------
23
- # Isolated Tire ActiveModel integration into `tire` class and instance method.
24
-
25
- When there's no conflict with existing methods, Tire methods are added
26
- to the class namespace, as well, so the change is 100% backwards-compatible.
27
-
28
- 0.3.3
29
- ---------------------------------------------------------
30
- # Added proper will_paginate compatibility
31
- # Added support for plugging in another HTTP library (see lib/http/clients/curb for an example)
9
+ # Added documentation (RDoc annotations and README)
10
+ # Kaminari pagination support
11
+ # Bugfixes (dependency on ES running, callbacks, bogus exceptions)
32
12
 
33
13
  END
34
14
  end
@@ -29,6 +29,9 @@ module Tire
29
29
  create_table :active_record_class_with_tire_methods do |t|
30
30
  t.string :title
31
31
  end
32
+ create_table :active_record_class_with_dynamic_index_names do |t|
33
+ t.string :title
34
+ end
32
35
  end
33
36
  end
34
37
 
@@ -72,6 +75,14 @@ module Tire
72
75
  assert_equal 'Test', results.first.title
73
76
  end
74
77
 
78
+ should "raise exception on invalid query" do
79
+ ActiveRecordArticle.create! :title => 'Test'
80
+
81
+ assert_raise Search::SearchRequestFailed do
82
+ ActiveRecordArticle.search '[x'
83
+ end
84
+ end
85
+
75
86
  context "with eager loading" do
76
87
  setup do
77
88
  ActiveRecordArticle.destroy_all
@@ -147,11 +158,20 @@ module Tire
147
158
  results = ActiveRecordArticle.search 'test*', :sort => 'title', :per_page => 5, :page => 1
148
159
  assert_equal 5, results.size
149
160
 
161
+ # WillPaginate
162
+ #
150
163
  assert_equal 2, results.total_pages
151
164
  assert_equal 1, results.current_page
152
165
  assert_equal nil, results.previous_page
153
166
  assert_equal 2, results.next_page
154
167
 
168
+ # Kaminari
169
+ #
170
+ assert_equal 5, results.limit_value
171
+ assert_equal 9, results.total_count
172
+ assert_equal 2, results.num_pages
173
+ assert_equal 0, results.offset_value
174
+
155
175
  assert_equal 'Test1', results.first.title
156
176
  end
157
177
 
@@ -164,6 +184,12 @@ module Tire
164
184
  assert_equal 1, results.previous_page
165
185
  assert_equal nil, results.next_page
166
186
 
187
+ #kaminari
188
+ assert_equal 5, results.limit_value
189
+ assert_equal 9, results.total_count
190
+ assert_equal 2, results.num_pages
191
+ assert_equal 5, results.offset_value
192
+
167
193
  assert_equal 'Test6', results.first.title
168
194
  end
169
195
 
@@ -176,6 +202,12 @@ module Tire
176
202
  assert_equal 2, results.previous_page
177
203
  assert_equal nil, results.next_page
178
204
 
205
+ #kaminari
206
+ assert_equal 5, results.limit_value
207
+ assert_equal 9, results.total_count
208
+ assert_equal 2, results.num_pages
209
+ assert_equal 10, results.offset_value
210
+
179
211
  assert_nil results.first
180
212
  end
181
213
 
@@ -267,6 +299,21 @@ module Tire
267
299
 
268
300
  end
269
301
 
302
+ context "with dynamic index name" do
303
+ setup do
304
+ @a = ActiveRecordClassWithDynamicIndexName.create! :title => 'Test'
305
+ @a.index.refresh
306
+ end
307
+
308
+ should "search in proper index" do
309
+ assert_equal 'dynamic_index', ActiveRecordClassWithDynamicIndexName.index.name
310
+ assert_equal 'dynamic_index', @a.index.name
311
+
312
+ results = ActiveRecordClassWithDynamicIndexName.search 'test'
313
+ assert_equal 'dynamic_index', results.first._index
314
+ end
315
+ end
316
+
270
317
  context "within Rails" do
271
318
 
272
319
  setup do