asari 0.6.0 → 0.7.0

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.md CHANGED
@@ -16,6 +16,20 @@ for easy integration with your Rails apps.
16
16
  asari = Asari.new("my-search-domain-asdfkljwe4") # CloudSearch search domain
17
17
  asari.add_item("1", { :name => "Tommy Morgan", :email => "tommy@wellbredgrapefruit.com"})
18
18
  asari.search("tommy") #=> ["1"] - a list of document IDs
19
+
20
+ #### Sandbox Mode
21
+
22
+ Because there is no "local" version of CloudSearch, and search instances can be
23
+ kind of expensive, you shouldn't have to have a development version of your
24
+ index set up in order to use Asari. Because of that, Asari has a "sandbox" mode
25
+ where it does nothing with add/update/delete requests and just returns an empty
26
+ collection for any searches. This sandbox mode is enabled by default - any time
27
+ you want to actually connect to the search index, just do the following:
28
+
29
+ Asari.mode = :production
30
+
31
+ You can turn the sandbox back on, if you like, by setting the mode to `:sandbox`
32
+ again.
19
33
 
20
34
  #### Pagination
21
35
 
@@ -64,6 +78,25 @@ with your AR objects as follows:
64
78
  @user.asari_update_in_index
65
79
  @user.asari_remove_from_index
66
80
 
81
+ You can also specify a :when option, like so:
82
+
83
+ asari_index("search-domain-for-users", [:name, :email, :twitter_handle,
84
+ :favorite_sweater], :when => :indexable)
85
+
86
+ or
87
+
88
+ asari_index("search-domain-for-users", [:name, :email, :twitter_handle,
89
+ :favorite_sweater], :when => Proc.new { |user| !user.admin && user.indexable })
90
+
91
+ This provides a way to mark records that shouldn't be in the index. The :when
92
+ option can be either a symbol - indicating a method on the object - or a Proc
93
+ that accepts the object as its first parameter. If the method/Proc returns true
94
+ when the object is created, the object is indexed - otherwise it is left out of
95
+ the index. If the method/Proc returns true when the object is updated, the
96
+ object is indexed - otherwise it is deleted from the index (if it has already
97
+ been added). This lets you be sure that you never have inappropriate data in
98
+ your search index.
99
+
67
100
  Because index updates are done as part of the AR lifecycle by default, you also
68
101
  might want to have control over how Asari handles index update errors - it's
69
102
  kind of problematic, if, say, users can't sign up on your site because
@@ -40,29 +40,49 @@ class Asari
40
40
  # search_domain - the CloudSearch domain to use for indexing this model.
41
41
  # fields - an array of Symbols representing the list of fields that
42
42
  # should be included in this index.
43
+ # options - a hash of extra options to consider when indexing this
44
+ # model. Right now, only one option is available:
45
+ # when - a string or symbol representing a method name, or a Proc to
46
+ # evaluate to determine if this model object should be indexed. On
47
+ # creation, if the method or Proc specified returns false, the
48
+ # model will not be indexed. On update, if the method or Proc
49
+ # specified returns false, the model will be removed from the
50
+ # index (if it exists there).
43
51
  #
44
52
  # Examples:
45
53
  # class User < ActiveRecord::Base
46
54
  # include Asari::ActiveRecord
47
55
  #
48
56
  # asari_index("my-companies-users-asglkj4rsagkjlh34", [:name, :email])
57
+ # # or
58
+ # asari_index("my-companies-users-asglkj4rsagkjlh34", [:name, :email], :when => :should_be_indexed)
59
+ # # or
60
+ # asari_index("my-companies-users-asglkj4rsagkjlh34", [:name, :email], :when => Proc.new({ |user| user.published && !user.admin? }))
49
61
  #
50
- def asari_index(search_domain, fields)
51
- self.class_variable_set(:@@asari, Asari.new(search_domain))
52
- self.class_variable_set(:@@fields, fields)
62
+ def asari_index(search_domain, fields, options = {})
63
+ self.class_variable_set(:@@asari_instance, Asari.new(search_domain))
64
+ self.class_variable_set(:@@asari_fields, fields)
65
+ self.class_variable_set(:@@asari_when, options.delete(:when))
53
66
  end
54
67
 
55
68
  def asari_instance
56
- self.class_variable_get(:@@asari)
69
+ self.class_variable_get(:@@asari_instance)
57
70
  end
58
71
 
59
72
  def asari_fields
60
- self.class_variable_get(:@@fields)
73
+ self.class_variable_get(:@@asari_fields)
74
+ end
75
+
76
+ def asari_when
77
+ self.class_variable_get(:@@asari_when)
61
78
  end
62
79
 
63
80
  # Internal: method for adding a newly created item to the CloudSearch
64
81
  # index. Should probably only be called from asari_add_to_index above.
65
82
  def asari_add_item(obj)
83
+ if self.asari_when
84
+ return unless asari_should_index?(obj)
85
+ end
66
86
  data = {}
67
87
  self.asari_fields.each do |field|
68
88
  data[field] = obj.send(field) || ""
@@ -75,6 +95,12 @@ class Asari
75
95
  # Internal: method for updating a freshly edited item to the CloudSearch
76
96
  # index. Should probably only be called from asari_update_in_index above.
77
97
  def asari_update_item(obj)
98
+ if self.asari_when
99
+ unless asari_should_index?(obj)
100
+ self.asari_remove_item(obj)
101
+ return
102
+ end
103
+ end
78
104
  data = {}
79
105
  self.asari_fields.each do |field|
80
106
  data[field] = obj.send(field)
@@ -92,6 +118,17 @@ class Asari
92
118
  self.asari_on_error(e)
93
119
  end
94
120
 
121
+ # Internal: method for looking at the when method/Proc (if defined) to
122
+ # determine whether this model should be indexed.
123
+ def asari_should_index?(object)
124
+ when_test = self.asari_when
125
+ if when_test.is_a? Proc
126
+ return Proc.call(object)
127
+ else
128
+ return object.send(when_test)
129
+ end
130
+ end
131
+
95
132
  # Public: method for searching the index for the specified term and
96
133
  # returning all model objects that match.
97
134
  #
@@ -1,3 +1,3 @@
1
1
  class Asari
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -5,7 +5,7 @@ describe Asari do
5
5
  describe "when CloudSearch is responding without error" do
6
6
  before :each do
7
7
  @asari = double()
8
- ActiveRecordFake.class_variable_set(:@@asari, @asari)
8
+ ActiveRecordFake.class_variable_set(:@@asari_instance, @asari)
9
9
  end
10
10
 
11
11
  it "correctly sets up a before_destroy listener" do
@@ -60,7 +60,7 @@ describe Asari do
60
60
 
61
61
  describe "When CloudSearch is being a problem" do
62
62
  before :each do
63
- ActiveRecordFake.class_variable_set(:@@asari, Asari.new("test-domain"))
63
+ ActiveRecordFake.class_variable_set(:@@asari_instance, Asari.new("test-domain"))
64
64
  stub_const("HTTParty", double())
65
65
  HTTParty.stub(:post).and_return(fake_error_response)
66
66
  HTTParty.stub(:get).and_return(fake_error_response)
@@ -0,0 +1,51 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Asari do
4
+ describe Asari::ActiveRecord do
5
+ describe "conditional indexing" do
6
+ describe "when a :when option is provided" do
7
+ before :each do
8
+ @arcs = ARConditionalsSpy.new
9
+ @arcs.be_indexable = false
10
+ @asari = double()
11
+ ARConditionalsSpy.class_variable_set(:@@asari_instance, @asari)
12
+ end
13
+
14
+ it "doesn't add to the index if the :when option returns false" do
15
+ expect(@arcs.was_asked).to eq(false)
16
+ @arcs.asari_add_to_index
17
+ expect(@arcs.was_asked).to eq(true)
18
+ end
19
+
20
+ it "doesn't add to the index if the :when option returns false" do
21
+ expect(@arcs.was_asked).to eq(false)
22
+ @arcs.asari_add_to_index
23
+ expect(@arcs.was_asked).to eq(true)
24
+ end
25
+
26
+ it "does add to the index if the :when option returns true" do
27
+ expect(@arcs.was_asked).to eq(false)
28
+ @arcs.be_indexable = true
29
+ @asari.should_receive(:add_item).with(1, { :name => "Tommy", :email => "some@email.com"})
30
+ @arcs.asari_add_to_index
31
+ expect(@arcs.was_asked).to eq(true)
32
+ end
33
+
34
+ it "deletes the item from the index if the :when option returns false when the item is updated" do
35
+ expect(@arcs.was_asked).to eq(false)
36
+ @asari.should_receive(:remove_item).with(1)
37
+ @arcs.asari_update_in_index
38
+ expect(@arcs.was_asked).to eq(true)
39
+ end
40
+
41
+ it "updates the item in the index if the :when option returns true when the item is updated" do
42
+ expect(@arcs.was_asked).to eq(false)
43
+ @arcs.be_indexable = true
44
+ @asari.should_receive(:update_item).with(1, { :name => "Tommy", :email => "some@email.com"})
45
+ @arcs.asari_update_in_index
46
+ expect(@arcs.was_asked).to eq(true)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -34,6 +34,7 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  class ActiveRecordFake
37
+
37
38
  class << self
38
39
  def before_destroy(sym)
39
40
  @before_destroy = sym
@@ -78,3 +79,55 @@ class ActiveRecordFakeWithErrorOverride < ActiveRecordFake
78
79
  false
79
80
  end
80
81
  end
82
+
83
+ class ARConditionalsSpy
84
+ attr_accessor :be_indexable
85
+ attr_accessor :was_asked
86
+
87
+ class << self
88
+ def before_destroy(sym)
89
+ @before_destroy = sym
90
+ end
91
+
92
+ def after_create(sym)
93
+ @after_create = sym
94
+ end
95
+
96
+ def after_update(sym)
97
+ @after_update = sym
98
+ end
99
+
100
+ def find(*args)
101
+ if args.size > 0
102
+ return [ARConditionalsSpy.new]
103
+ else
104
+ raise ActiveRecord::RecordNotFound
105
+ end
106
+ end
107
+ end
108
+
109
+ include Asari::ActiveRecord
110
+
111
+ asari_index("test-domain", [:name, :email], :when => :indexable)
112
+
113
+ def initialize
114
+ @was_asked = false
115
+ end
116
+
117
+ def id
118
+ 1
119
+ end
120
+
121
+ def name
122
+ "Tommy"
123
+ end
124
+
125
+ def email
126
+ "some@email.com"
127
+ end
128
+
129
+ def indexable
130
+ @was_asked = true
131
+ @be_indexable
132
+ end
133
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: asari
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-12 00:00:00.000000000 Z
12
+ date: 2012-07-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: httparty
16
- requirement: &70109076129460 !ruby/object:Gem::Requirement
16
+ requirement: &70205905685040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70109076129460
24
+ version_requirements: *70205905685040
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70109076128860 !ruby/object:Gem::Requirement
27
+ requirement: &70205905684100 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70109076128860
35
+ version_requirements: *70205905684100
36
36
  description: Asari s a Ruby interface for AWS CloudSearch
37
37
  email:
38
38
  - tommy@wellbredgrapefruit.com
@@ -53,6 +53,7 @@ files:
53
53
  - spec/active_record_spec.rb
54
54
  - spec/asari_spec.rb
55
55
  - spec/collection_spec.rb
56
+ - spec/conditionals_spec.rb
56
57
  - spec/documents_spec.rb
57
58
  - spec/search_spec.rb
58
59
  - spec_helper.rb
@@ -84,6 +85,7 @@ test_files:
84
85
  - spec/active_record_spec.rb
85
86
  - spec/asari_spec.rb
86
87
  - spec/collection_spec.rb
88
+ - spec/conditionals_spec.rb
87
89
  - spec/documents_spec.rb
88
90
  - spec/search_spec.rb
89
91
  has_rdoc: