slingshot-rb 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +1 -0
  2. data/README.markdown +276 -50
  3. data/examples/rails-application-template.rb +144 -0
  4. data/examples/slingshot-dsl.rb +272 -102
  5. data/lib/slingshot.rb +13 -0
  6. data/lib/slingshot/client.rb +10 -1
  7. data/lib/slingshot/dsl.rb +17 -1
  8. data/lib/slingshot/index.rb +109 -7
  9. data/lib/slingshot/model/callbacks.rb +23 -0
  10. data/lib/slingshot/model/import.rb +18 -0
  11. data/lib/slingshot/model/indexing.rb +50 -0
  12. data/lib/slingshot/model/naming.rb +30 -0
  13. data/lib/slingshot/model/persistence.rb +34 -0
  14. data/lib/slingshot/model/persistence/attributes.rb +60 -0
  15. data/lib/slingshot/model/persistence/finders.rb +61 -0
  16. data/lib/slingshot/model/persistence/storage.rb +75 -0
  17. data/lib/slingshot/model/search.rb +97 -0
  18. data/lib/slingshot/results/collection.rb +35 -10
  19. data/lib/slingshot/results/item.rb +10 -7
  20. data/lib/slingshot/results/pagination.rb +30 -0
  21. data/lib/slingshot/rubyext/symbol.rb +11 -0
  22. data/lib/slingshot/search.rb +3 -2
  23. data/lib/slingshot/search/facet.rb +8 -6
  24. data/lib/slingshot/search/filter.rb +7 -8
  25. data/lib/slingshot/search/highlight.rb +1 -3
  26. data/lib/slingshot/search/query.rb +4 -0
  27. data/lib/slingshot/search/sort.rb +5 -0
  28. data/lib/slingshot/tasks.rb +88 -0
  29. data/lib/slingshot/version.rb +1 -1
  30. data/slingshot.gemspec +17 -4
  31. data/test/integration/active_model_searchable_test.rb +80 -0
  32. data/test/integration/active_record_searchable_test.rb +193 -0
  33. data/test/integration/highlight_test.rb +1 -1
  34. data/test/integration/index_mapping_test.rb +1 -1
  35. data/test/integration/index_store_test.rb +27 -0
  36. data/test/integration/persistent_model_test.rb +35 -0
  37. data/test/integration/query_string_test.rb +3 -3
  38. data/test/integration/sort_test.rb +2 -2
  39. data/test/models/active_model_article.rb +31 -0
  40. data/test/models/active_model_article_with_callbacks.rb +49 -0
  41. data/test/models/active_model_article_with_custom_index_name.rb +5 -0
  42. data/test/models/active_record_article.rb +12 -0
  43. data/test/models/persistent_article.rb +11 -0
  44. data/test/models/persistent_articles_with_custom_index_name.rb +10 -0
  45. data/test/models/supermodel_article.rb +22 -0
  46. data/test/models/validated_model.rb +11 -0
  47. data/test/test_helper.rb +4 -0
  48. data/test/unit/active_model_lint_test.rb +17 -0
  49. data/test/unit/client_test.rb +4 -0
  50. data/test/unit/configuration_test.rb +4 -0
  51. data/test/unit/index_test.rb +240 -17
  52. data/test/unit/model_callbacks_test.rb +90 -0
  53. data/test/unit/model_import_test.rb +71 -0
  54. data/test/unit/model_persistence_test.rb +400 -0
  55. data/test/unit/model_search_test.rb +289 -0
  56. data/test/unit/results_collection_test.rb +69 -7
  57. data/test/unit/results_item_test.rb +8 -14
  58. data/test/unit/rubyext_hash_test.rb +19 -0
  59. data/test/unit/search_facet_test.rb +25 -7
  60. data/test/unit/search_filter_test.rb +3 -0
  61. data/test/unit/search_query_test.rb +11 -0
  62. data/test/unit/search_sort_test.rb +8 -0
  63. data/test/unit/search_test.rb +14 -0
  64. data/test/unit/slingshot_test.rb +38 -0
  65. metadata +133 -26
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ rdoc/
6
6
  coverage/
7
7
  scratch/
8
8
  examples/*.html
9
+ *.log
data/README.markdown CHANGED
@@ -4,26 +4,32 @@ Slingshot
4
4
  ![Slingshot](https://github.com/karmi/slingshot/raw/master/slingshot.png)
5
5
 
6
6
  _Slingshot_ is a Ruby client for the [ElasticSearch](http://www.elasticsearch.org/) search engine/database.
7
- It aims to provide rich and comfortable Ruby API in the form of a simple domain-specific language.
8
7
 
9
8
  _ElasticSearch_ is a scalable, distributed, cloud-ready, highly-available,
10
- RESTful database communicating by JSON over HTTP, based on [Lucene](http://lucene.apache.org/),
11
- written in Java. It manages to be very simple to use and very powerful at the same time.
9
+ full-text search engine and database, communicating by JSON over RESTful HTTP,
10
+ based on [Lucene](http://lucene.apache.org/), written in Java.
11
+
12
+ This document provides just a brief overview of _Slingshot's_ features. Be sure to check out also
13
+ the extensive documentation at <http://karmi.github.com/slingshot/> if you're interested.
12
14
 
13
15
  Installation
14
16
  ------------
15
17
 
16
18
  First, you need a running _ElasticSearch_ server. Thankfully, it's easy. Let's define easy:
17
19
 
18
- $ curl -k -L -o elasticsearch-0.15.2.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.15.2.tar.gz
19
- $ tar -zxvf elasticsearch-0.15.2.tar.gz
20
- $ ./elasticsearch-0.15.2/bin/elasticsearch -f
20
+ $ curl -k -L -o elasticsearch-0.16.0.tar.gz http://github.com/downloads/elasticsearch/elasticsearch/elasticsearch-0.16.0.tar.gz
21
+ $ tar -zxvf elasticsearch-0.16.0.tar.gz
22
+ $ ./elasticsearch-0.16.0/bin/elasticsearch -f
23
+
24
+ OK. Easy. On a Mac, you can also use _Homebrew_:
25
+
26
+ $ brew install elasticsearch
21
27
 
22
- OK, easy. Now, install the gem via Rubygems:
28
+ OK. Let's install the gem via Rubygems:
23
29
 
24
30
  $ gem install slingshot-rb
25
31
 
26
- or from source:
32
+ Of course, you can install it from the source as well:
27
33
 
28
34
  $ git clone git://github.com/karmi/slingshot.git
29
35
  $ cd slingshot
@@ -33,17 +39,20 @@ or from source:
33
39
  Usage
34
40
  -----
35
41
 
36
- Currently, you can use _Slingshot_ via the DSL (eg. by extending your class with it).
37
- Plans for full ActiveModel integration (and other convenience layers) are in progress
38
- (see the [`activemodel`](https://github.com/karmi/slingshot/compare/activemodel) branch).
42
+ _Slingshot_ exposes easy-to-use domain specific language for fluent communication with _ElasticSearch_.
39
43
 
40
- To kick the tires, require the gem in an IRB session or a Ruby script
41
- (note that you can just run the full example from [`examples/dsl.rb`](https://github.com/karmi/slingshot/blob/master/examples/dsl.rb)):
44
+ It also blends with your [ActiveModel](https://github.com/rails/rails/tree/master/activemodel)
45
+ classes for convenient usage in Rails applications.
46
+
47
+ To test-drive the core _ElasticSearch_ functionality, let's require the gem:
42
48
 
43
49
  require 'rubygems'
44
50
  require 'slingshot'
45
51
 
46
- First, let's create an index named `articles` and store/index some documents:
52
+ Please note that you can copy these snippets from the much more extensive and heavily annotated file
53
+ in [examples/slingshot-dsl.rb](http://karmi.github.com/slingshot/).
54
+
55
+ OK. Let's create an index named `articles` and store/index some documents:
47
56
 
48
57
  Slingshot.index 'articles' do
49
58
  delete
@@ -57,8 +66,9 @@ First, let's create an index named `articles` and store/index some documents:
57
66
  refresh
58
67
  end
59
68
 
60
- We can also create the
61
- index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html):
69
+ We can also create the index with custom
70
+ [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
71
+ for a specific document type:
62
72
 
63
73
  Slingshot.index 'articles' do
64
74
  create :mappings => {
@@ -73,9 +83,32 @@ index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/a
73
83
  }
74
84
  end
75
85
 
76
- Now, let's query the database.
86
+ Of course, we may have large amounts of data, and it may be impossible or impractical to add them to the index
87
+ one by one. We can use _ElasticSearch's_ [bulk storage](http://www.elasticsearch.org/guide/reference/api/bulk.html):
88
+
89
+ articles = [
90
+ { :id => '1', :title => 'one' },
91
+ { :id => '2', :title => 'two' },
92
+ { :id => '3', :title => 'three' }
93
+ ]
94
+
95
+ Slingshot.index 'bulk' do
96
+ import articles
97
+ end
98
+
99
+ We can also easily manipulate the documents before storing them in the index, by passing a block to the
100
+ `import` method:
101
+
102
+ Slingshot.index 'bulk' do
103
+ import articles do |documents|
104
+
105
+ documents.each { |document| document[:title].capitalize! }
106
+ end
107
+ end
108
+
109
+ OK. Now, let's go search all the data.
77
110
 
78
- We are searching for articles whose `title` begins with letter “T”, sorted by `title` in `descending` order,
111
+ We will be searching for articles whose `title` begins with letter “T”, sorted by `title` in `descending` order,
79
112
  filtering them for ones tagged “ruby”, and also retrieving some [_facets_](http://www.elasticsearch.org/guide/reference/api/search/facets/)
80
113
  from the database:
81
114
 
@@ -97,6 +130,9 @@ from the database:
97
130
  end
98
131
  end
99
132
 
133
+ (Of course, we may also page the results with `from` and `size` query options, retrieve only specific fields
134
+ or highlight content matching our query, etc.)
135
+
100
136
  Let's display the results:
101
137
 
102
138
  s.results.each do |document|
@@ -128,70 +164,260 @@ count for articles tagged 'php' is excluded, since they don't match the current
128
164
  # python 1
129
165
  # java 1
130
166
 
131
- We can display the full query JSON:
167
+ If configuring the search payload with a block somehow feels too weak for you, you can simply pass
168
+ a Ruby `Hash` (or JSON string) with the query declaration to the `search` method:
169
+
170
+ Slingshot.search 'articles', :query => { :fuzzy => { :title => 'Sour' } }
171
+
172
+ If this sounds like a great idea to you, you are probably able to write your application
173
+ using just `curl`, `sed` and `awk`.
174
+
175
+ We can display the full query JSON for close inspection:
132
176
 
133
177
  puts s.to_json
134
178
  # {"facets":{"current-tags":{"terms":{"field":"tags"}},"global-tags":{"global":true,"terms":{"field":"tags"}}},"query":{"query_string":{"query":"title:T*"}},"filter":{"terms":{"tags":["ruby"]}},"sort":[{"title":"desc"}]}
135
179
 
136
- Or, better, we can display the corresponding `curl` command for easy debugging:
180
+ Or, better, we can display the corresponding `curl` command to recreate and debug the request in the terminal:
137
181
 
138
182
  puts s.to_curl
139
183
  # curl -X POST "http://localhost:9200/articles/_search?pretty=true" -d '{"facets":{"current-tags":{"terms":{"field":"tags"}},"global-tags":{"global":true,"terms":{"field":"tags"}}},"query":{"query_string":{"query":"title:T*"}},"filter":{"terms":{"tags":["ruby"]}},"sort":[{"title":"desc"}]}'
140
184
 
141
- Since `curl` is the crucial debugging tool in _ElasticSearch_ land, we can log every search query in `curl` format:
185
+ However, we can simply log every search query (and other requests) in this `curl`-friendly format:
142
186
 
143
187
  Slingshot.configure { logger 'elasticsearch.log' }
144
188
 
189
+ When you set the log level to _debug_:
145
190
 
146
- Features
147
- --------
191
+ Slingshot.configure { logger 'elasticsearch.log', :level => 'debug' }
192
+
193
+ the JSON responses are logged as well. This is not a great idea for production environment,
194
+ but it's priceless when you want to paste a complicated transaction to the mailing list or IRC channel.
195
+
196
+ The _Slingshot_ DSL tries hard to provide a strong Ruby-like API for the main _ElasticSearch_ features.
197
+
198
+ By default, _Slingshot_ wraps the results collection in a enumerable `Results::Collection` class,
199
+ and result items in a `Results::Item` class, which looks like a child of `Hash` and `Openstruct`,
200
+ for smooth iterating and displaying the results.
201
+
202
+ You may wrap the result items in your own class by setting the `Slingshot.configuration.wrapper`
203
+ property. Your class must take a `Hash` of attributes on initialization.
204
+
205
+ If that seems like a great idea to you, there's a big chance you already have such class, and one would bet
206
+ it's an `ActiveRecord` or `ActiveModel` class, containing model of your Rails application.
207
+
208
+ Fortunately, _Slingshot_ makes blending _ElasticSearch_ features into your models trivially possible.
209
+
210
+
211
+ ActiveModel Integration
212
+ -----------------------
213
+
214
+ Let's suppose you have an `Article` class in your Rails application. To make it searchable with
215
+ _Slingshot_, you just `include` it:
216
+
217
+ class Article < ActiveRecord::Base
218
+ include Slingshot::Model::Search
219
+ include Slingshot::Model::Callbacks
220
+ end
221
+
222
+ When you now save a record:
223
+
224
+ Article.create :title => "I Love ElasticSearch",
225
+ :content => "...",
226
+ :author => "Captain Nemo",
227
+ :published_on => Time.now
228
+
229
+ it is automatically added into the index, because of the included callbacks. The document attributes
230
+ are indexed exactly as when you call the `Article#to_json` method.
231
+
232
+ Now you can search the records:
233
+
234
+ Article.search 'love'
235
+
236
+ OK. Often, this is where the game stops. Not here.
237
+
238
+ First of all, you may use the full query DSL, as explained above, with filters, sorting,
239
+ advanced facet aggregation, highlighting, etc:
240
+
241
+ q = 'love'
242
+ Article.search do
243
+ query { string q }
244
+ facet('timeline') { date :published_on, :interval => 'month' }
245
+ sort { published_on 'desc' }
246
+ end
247
+
248
+ Dynamic mapping is a godsend when you're prototyping.
249
+ For serious usage, though, you'll definitely want to define a custom mapping for your model:
250
+
251
+ class Article < ActiveRecord::Base
252
+ include Slingshot::Model::Search
253
+ include Slingshot::Model::Callbacks
254
+
255
+ mapping do
256
+ indexes :id, :type => 'string', :analyzed => false
257
+ indexes :title, :type => 'string', :analyzer => 'snowball', :boost => 100
258
+ indexes :content, :type => 'string', :analyzer => 'snowball'
259
+ indexes :author, :type => 'string', :analyzer => 'keyword'
260
+ indexes :published_on, :type => 'date', :include_in_all => false
261
+ end
262
+ end
148
263
 
149
- Currently, _Slingshot_ supports main features of the _ElasticSearch_ [Search API](http://www.elasticsearch.org/guide/reference/api/search/request-body.html) and it's [Query DSL](http://www.elasticsearch.org/guide/reference/query-dsl/). In present, it allows you to:
264
+ In this case, _only_ the defined model attributes are indexed when adding to the index.
150
265
 
151
- * Create, delete and refresh the index
152
- * Create the index with specific [mapping](http://www.elasticsearch.org/guide/reference/api/admin-indices-create-index.html)
153
- * Store a document in the index
154
- * [Query](https://github.com/karmi/slingshot/blob/master/examples/dsl.rb) the index with the `query_string`, `term`, `terms` and `match_all` types of queries
155
- * [Sort](http://elasticsearch.org/guide/reference/api/search/sort.html) the results by `fields`
156
- * [Filter](http://elasticsearch.org/guide/reference/query-dsl/) the results
157
- * Retrieve the _terms_ and _date histogram_ types of [facets](http://www.elasticsearch.org/guide/reference/api/search/facets/index.html) (other types are high priority)
158
- * [Highlight](http://www.elasticsearch.org/guide/reference/api/search/highlighting.html) matching fields
159
- * Return just specific `fields` from documents
160
- * Page the results with `from` and `size` query options
161
- * Log the `curl`-equivalent of requests and response JSON
266
+ When you want tight grip on how your model attributes are added to the index, just
267
+ provide the `to_indexed_json` method yourself:
162
268
 
163
- See the [`examples/slingshot-dsl.rb`](blob/master/examples/slingshot-dsl.rb) file for the full, working examples.
269
+ class Article < ActiveRecord::Base
270
+ include Slingshot::Model::Search
271
+ include Slingshot::Model::Callbacks
164
272
 
165
- _Slingshot_ wraps the results in a enumerable `Results::Collection` class, and every result in a `Results::Item` class,
166
- which looks like a child of `Hash` and `Openstruct`, for smooth iterating and displaying the results.
273
+ def to_indexed_json
274
+ names = author.split(/\W/)
275
+ last_name = names.pop
276
+ first_name = names.join
167
277
 
168
- You may wrap the result items in your own class just by setting the `Configuration.wrapper` property,
169
- supposed your class takes a hash of attributes upon initialization, in ActiveModel/ActiveRecord manner.
170
- Please see the files `test/models/article.rb` and `test/unit/results_collection_test.rb` for details.
278
+ {
279
+ :title => title,
280
+ :content => content,
281
+ :author => {
282
+ :first_name => first_name,
283
+ :last_name => last_name
284
+ }
285
+ }.to_json
286
+ end
287
+
288
+ end
289
+
290
+ Note that _Slingshot_-enhanced models are fully compatible with [`will_paginate`](https://github.com/mislav/will_paginate),
291
+ so you can pass any parameters to the `search` method in the controller, as usual:
292
+
293
+ @articles = Article.search params[:q], :page => (params[:page] || 1)
294
+
295
+ OK. Chances are, you have lots of records stored in the underlying database. How will you get them to _ElasticSearch_? Easy:
296
+
297
+ Article.index.import Article.all
298
+
299
+ However, this way, all your records are loaded into memory, serialized into JSON,
300
+ and sent down the wire to _ElasticSearch_. Not practical, you say? You're right.
301
+
302
+ Provided your model implements some sort of _pagination_ — and it probably does, for so much data —,
303
+ you can just run:
304
+
305
+ Article.import
306
+
307
+ In this case, the `Article.paginate` method is called, and your records are sent to the index
308
+ in chunks of 1000. If that number doesn't suit you, just provide a better one:
309
+
310
+ Article.import :per_page => 100
311
+
312
+ Any other parameters you provide to the `import` method are passed down to the `paginate` method.
313
+
314
+ Are we saying you have to fiddle with this thing in a `rails console` or silly Ruby scripts? No.
315
+ Just call the included _Rake_ task on the commandline:
316
+
317
+ $ rake environment slingshot:import CLASS='Article'
318
+
319
+ You can also force-import the data by deleting the index first (and creating it with mapping
320
+ provided by the `mapping` block in your model):
321
+
322
+ $ rake environment slingshot:import CLASS='Article' FORCE=true
323
+
324
+ When you'll spend more time with _ElasticSearch_, you'll notice how
325
+ [index aliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases.html)
326
+ are the best idea since the invention of inverted index.
327
+ You can index your data into a fresh index (and possibly update an alias if everything's fine):
328
+
329
+ $ rake environment slingshot:import CLASS='Article' INDEX='articles-2011-05'
330
+
331
+ If you're the type who has no time for long introductions, you can generate a fully working
332
+ example Rails application, with an `ActiveRecord` model and a search form, to play with:
333
+
334
+ $ rails new searchapp -m https://github.com/karmi/slingshot/raw/master/examples/rails-application-template.rb
335
+
336
+ OK. All this time we have been talking about `ActiveRecord` models, since
337
+ it is a reasonable Rails' default for the storage layer.
338
+
339
+ But what if you use another database such as [MongoDB](http://www.mongodb.org/),
340
+ another object mapping library, such as [Mongoid](http://mongoid.org/)?
341
+
342
+ Well, things stay mostly the same:
343
+
344
+ class Article
345
+ include Mongoid::Document
346
+ field :title, :type => String
347
+ field :content, :type => String
348
+
349
+ include Slingshot::Model::Search
350
+ include Slingshot::Model::Callbacks
351
+
352
+ # Let's use a different index name so stuff doesn't get mixed up
353
+ #
354
+ index_name 'mongo-articles'
355
+
356
+ # These Mongo guys sure do some funky stuff with their IDs
357
+ # in +serializable_hash+, let's fix it.
358
+ #
359
+ def to_indexed_json
360
+ self.to_json
361
+ end
362
+
363
+ end
364
+
365
+ Article.create :title => 'I Love ElasticSearch'
366
+
367
+ Article.search 'love'
368
+
369
+ That's kinda nice. But there's more.
370
+
371
+ _Slingshot_ implements not only _searchable_ features, but also _persistence_ features.
372
+
373
+ This means that you can use a _Slingshot_ model **instead of** your database, not just
374
+ for searching your database. Why would you like to do that?
375
+
376
+ Well, because you're tired of database migrations and lots of hand-holding with your
377
+ database to store stuff like `{ :name => 'Slingshot', :tags => [ 'ruby', 'search' ] }`.
378
+ Because what you need is to just dump a JSON-representation of your data into a database and
379
+ load it back when needed.
380
+ Because you've noticed that _searching_ your data is a much more effective way of retrieval
381
+ then constructing elaborate database query conditions.
382
+ Because you have _lots_ of data and want to use _ElasticSearch's_
383
+ advanced distributed features.
384
+
385
+ To use the persistence features, you have to include the `Slingshot::Persistence` module
386
+ in your class and define the properties (analogous to the way you do with CouchDB- or MongoDB-based models):
387
+
388
+ class Article
389
+ include Slingshot::Model::Persistence
390
+ include Slingshot::Model::Search
391
+ include Slingshot::Model::Callbacks
392
+
393
+ validates_presence_of :title, :author
394
+
395
+ property :title
396
+ property :author
397
+ property :content
398
+ property :published_on
399
+
400
+ end
171
401
 
402
+ Of course, not all validations or `ActionPack` helpers will be available to your models,
403
+ but if you can live with that, you've just got a schema-free, highly-scalable storage
404
+ and retrieval engine for your data.
172
405
 
173
406
  Todo, Plans & Ideas
174
407
  -------------------
175
408
 
176
- _Slingshot_ is already used in production by its authors. Nevertheless, it's not finished yet.
409
+ _Slingshot_ is already used in production by its authors. Nevertheless, it's not considered finished yet.
177
410
 
178
- The todos and plans are vast, and the most important are listed below, in the order of importance:
411
+ There are todos, plans and ideas, some of which are listed below, in the order of importance:
179
412
 
180
- * Seamless _ActiveModel_ compatibility for easy usage in _Rails_ applications (this also means nearly full _ActiveRecord_ compatibility). See the ongoing work in the [`activemodel`](https://github.com/karmi/slingshot/compare/activemodel) branch
181
- * Seamless [will_paginate](https://github.com/mislav/will_paginate) compatibility for easy pagination. Already [implemented](https://github.com/karmi/slingshot/commit/e1351f6) on the `activemodel` branch
182
- * [Mapping](http://www.elasticsearch.org/guide/reference/mapping/) definition for models
183
413
  * Proper RDoc annotations for the source code
184
- * Dual interface: allow to simply pass queries/options for _ElasticSearch_ as a Hash in any method
185
414
  * [Histogram](http://www.elasticsearch.org/guide/reference/api/search/facets/histogram-facet.html) facets
186
415
  * [Statistical](http://www.elasticsearch.org/guide/reference/api/search/facets/statistical-facet.html) facets
187
416
  * [Geo Distance](http://www.elasticsearch.org/guide/reference/api/search/facets/geo-distance-facet.html) facets
188
417
  * [Index aliases](http://www.elasticsearch.org/guide/reference/api/admin-indices-aliases.html) management
189
418
  * [Analyze](http://www.elasticsearch.org/guide/reference/api/admin-indices-analyze.html) API support
190
- * [Bulk](http://www.elasticsearch.org/guide/reference/api/bulk.html) API
191
419
  * Embedded webserver to display statistics and to allow easy searches
192
- * Seamless support for [auto-updating _river_ index](http://www.elasticsearch.org/guide/reference/river/couchdb.html) for _CouchDB_ `_changes` feed
193
420
 
194
- The full ActiveModel integration is planned for the 1.0 release.
195
421
 
196
422
  Other Clients
197
423
  -------------
@@ -0,0 +1,144 @@
1
+ # ===================================================================================================================
2
+ # Template for generating a no-frills Rails application with support for ElasticSearch full-text search via Slingshot
3
+ # ===================================================================================================================
4
+ #
5
+ # This file creates a basic Rails application with support for ElasticSearch full-text via the Slingshot gem
6
+ #
7
+ # Run it like this:
8
+ #
9
+ # rails new searchapp -m https://github.com/karmi/slingshot/raw/master/examples/rails-application-template.rb
10
+ #
11
+
12
+ run "rm public/index.html"
13
+ run "rm public/images/rails.png"
14
+ run "touch tmp/.gitignore log/.gitignore vendor/.gitignore"
15
+
16
+ git :init
17
+ git :add => '.'
18
+ git :commit => "-m 'Initial commit: Clean application'"
19
+
20
+ puts
21
+ say_status "Rubygems", "Adding Rubygems into Gemfile...\n", :yellow
22
+ puts '-'*80, ''
23
+
24
+ gem 'slingshot-rb', :git => 'https://github.com/karmi/slingshot.git', :branch => 'activemodel'
25
+ gem 'will_paginate', '~>3.0.pre'
26
+ git :add => '.'
27
+ git :commit => "-m 'Added gems'"
28
+
29
+ puts
30
+ say_status "Rubygems", "Installing Rubygems...", :yellow
31
+
32
+ puts
33
+ puts "********************************************************************************"
34
+ puts " Running `bundle install`. Let's watch a movie!"
35
+ puts "********************************************************************************", ""
36
+
37
+ run "bundle install"
38
+
39
+ puts
40
+ say_status "Model", "Adding search support into the Article model...", :yellow
41
+ puts '-'*80, ''
42
+
43
+ generate :scaffold, "Article title:string content:text published_on:date"
44
+ route "root :to => 'articles#index'"
45
+ rake "db:migrate"
46
+
47
+ git :add => '.'
48
+ git :commit => "-m 'Added the Article resource'"
49
+
50
+ run "rm -f app/models/article.rb"
51
+ file 'app/models/article.rb', <<-CODE
52
+ class Article < ActiveRecord::Base
53
+ include Slingshot::Model::Search
54
+ include Slingshot::Model::Callbacks
55
+ end
56
+ CODE
57
+
58
+ initializer 'slingshot.rb', <<-CODE
59
+ Slingshot.configure do
60
+ logger STDERR
61
+ end
62
+ CODE
63
+
64
+ git :commit => "-a -m 'Added Slingshot support into the Article class and an initializer'"
65
+
66
+ puts
67
+ say_status "Controller", "Adding controller action, route, and neccessary HTML for search...", :yellow
68
+ puts '-'*80, ''
69
+
70
+ gsub_file 'app/controllers/articles_controller.rb', %r{# GET /articles/1$}, <<-CODE
71
+ # GET /articles/search
72
+ def search
73
+ @articles = Article.search params[:q]
74
+
75
+ render :action => "index"
76
+ end
77
+
78
+ # GET /articles/1
79
+ CODE
80
+
81
+ gsub_file 'app/views/articles/index.html.erb', %r{<h1>Listing articles</h1>}, <<-CODE
82
+ <h1>Listing articles</h1>
83
+
84
+ <%= form_tag search_articles_path, :method => 'get' do %>
85
+ <%= label_tag :query %>
86
+ <%= text_field_tag :q, params[:q] %>
87
+ <%= submit_tag :search %>
88
+ <% end %>
89
+
90
+ <hr>
91
+ CODE
92
+
93
+ gsub_file 'app/views/articles/index.html.erb', %r{<%= link_to 'New Article', new_article_path %>}, <<-CODE
94
+ <%= link_to 'New Article', new_article_path %>
95
+ <%= link_to 'Back', articles_path if params[:q] %>
96
+ CODE
97
+
98
+ gsub_file 'config/routes.rb', %r{resources :articles}, <<-CODE
99
+ resources :articles do
100
+ collection { get :search }
101
+ end
102
+ CODE
103
+
104
+ git :commit => "-a -m 'Added Slingshot support into the frontend of application'"
105
+
106
+ puts
107
+ say_status "Database", "Seeding the database with data...", :yellow
108
+ puts '-'*80, ''
109
+
110
+ run "rm -f db/seeds.rb"
111
+ file 'db/seeds.rb', <<-CODE
112
+ contents = [
113
+ 'Lorem ipsum dolor sit amet.',
114
+ 'Consectetur adipisicing elit, sed do eiusmod tempor incididunt.',
115
+ 'Labore et dolore magna aliqua.',
116
+ 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
117
+ 'Excepteur sint occaecat cupidatat non proident.'
118
+ ]
119
+
120
+ puts "Deleting all articles..."
121
+ Article.delete_all
122
+
123
+ puts "Creating articles:"
124
+ %w[ One Two Three Four Five ].each_with_index do |title, i|
125
+ Article.create :title => title, :content => contents[i], :published_on => i.days.ago.utc
126
+ end
127
+ CODE
128
+
129
+ rake "db:seed"
130
+
131
+ git :add => "db/seeds.rb"
132
+ git :commit => "-m 'Added database seeding script'"
133
+
134
+ puts
135
+ say_status "Index", "Indexing database...", :yellow
136
+ puts '-'*80, ''
137
+
138
+ rake "environment slingshot:import CLASS='Article' FORCE=true"
139
+
140
+ puts "", "="*80
141
+ say_status "DONE", "\e[1mStarting the application. Open http://localhost:3000 and search for something...\e[0m", :yellow
142
+ puts "="*80, ""
143
+
144
+ run "rails server"