lifesaver 0.0.1 → 0.1.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.
Files changed (43) hide show
  1. data/.rspec +1 -0
  2. data/.travis.yml +3 -1
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +4 -0
  5. data/README.md +33 -13
  6. data/lib/lifesaver.rb +34 -11
  7. data/lib/lifesaver/config.rb +16 -0
  8. data/lib/lifesaver/index_worker.rb +10 -10
  9. data/lib/lifesaver/indexing/enqueuer.rb +37 -0
  10. data/lib/lifesaver/indexing/indexer.rb +40 -0
  11. data/lib/lifesaver/indexing/model_additions.rb +55 -0
  12. data/lib/lifesaver/notification/eager_loader.rb +48 -0
  13. data/lib/lifesaver/notification/enqueuer.rb +24 -0
  14. data/lib/lifesaver/notification/indexing_graph.rb +91 -0
  15. data/lib/lifesaver/notification/model_additions.rb +104 -0
  16. data/lib/lifesaver/notification/notifiable_associations.rb +49 -0
  17. data/lib/lifesaver/notification/traversal_queue.rb +48 -0
  18. data/lib/lifesaver/railtie.rb +4 -3
  19. data/lib/lifesaver/serialized_model.rb +3 -0
  20. data/lib/lifesaver/version.rb +1 -1
  21. data/lib/lifesaver/visitor_worker.rb +8 -4
  22. data/spec/integration/lifesaver_spec.rb +78 -0
  23. data/spec/spec_helper.rb +12 -10
  24. data/spec/support/active_record.rb +3 -3
  25. data/spec/support/test_models.rb +8 -6
  26. data/spec/support/tire_helper.rb +37 -0
  27. data/spec/unit/config_spec.rb +17 -0
  28. data/spec/unit/indexing/indexer_spec.rb +51 -0
  29. data/spec/unit/indexing/model_addtions_spec.rb +53 -0
  30. data/spec/unit/notification/eager_loader_spec.rb +64 -0
  31. data/spec/unit/notification/enqueuer_spec.rb +39 -0
  32. data/spec/unit/notification/indexing_graph_spec.rb +73 -0
  33. data/spec/unit/notification/model_additions_spec.rb +69 -0
  34. data/spec/unit/notification/notifiable_associations_spec.rb +75 -0
  35. data/spec/unit/notification/traversal_queue_spec.rb +67 -0
  36. metadata +40 -14
  37. data/lib/lifesaver/index_graph.rb +0 -53
  38. data/lib/lifesaver/marshal.rb +0 -43
  39. data/lib/lifesaver/model_additions.rb +0 -133
  40. data/spec/lifesaver/index_graph_spec.rb +0 -64
  41. data/spec/lifesaver/marshal_spec.rb +0 -80
  42. data/spec/lifesaver/model_additions_spec.rb +0 -91
  43. data/spec/lifesaver_spec.rb +0 -111
@@ -1,80 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Lifesaver::Marshal do
4
- describe ".is_serialized?" do
5
- it "returns true if Hash has :id and :class" do
6
- obj = { class: :post, id: 1 }
7
- expect(Lifesaver::Marshal.is_serialized?(obj)).to eql(true)
8
- end
9
-
10
- it "returns true if Hash has 'id' and 'class'" do
11
- obj = { "class" => "post", "id" => "1" }
12
- expect(Lifesaver::Marshal.is_serialized?(obj)).to eql(true)
13
- end
14
-
15
- it "returns false if Hash does not have id and class keys" do
16
- obj = { some_key: 1, id: 4 }
17
- expect(Lifesaver::Marshal.is_serialized?(obj)).to eql(false)
18
- end
19
-
20
- it "returns false if passed an non-Hash object" do
21
- obj = Post.new
22
- expect(Lifesaver::Marshal.is_serialized?(obj)).to eql(false)
23
- end
24
- end
25
-
26
- describe ".sanitize" do
27
- it "returns symbolized version of the Hash'" do
28
- obj = { "class" => "post", "id" => "1", "status" => "updated" }
29
- out = { class: :post, id: 1, status: :updated }
30
- expect(Lifesaver::Marshal.sanitize(obj)).to eql(out)
31
- end
32
-
33
- it "rejects non-Hashes" do
34
- obj = Post.new
35
- expect { Lifesaver::Marshal.sanitize(obj) }.to raise_error
36
- end
37
- end
38
-
39
- describe ".load" do
40
- before(:all) do
41
- Post.create(title: "Test Post")
42
- end
43
-
44
- it "loads a serialized object from ActiveRecord" do
45
- obj = { class: :post, id: 1 }
46
- expect(Lifesaver::Marshal.load(obj)).to eql([Post.find(1), {}])
47
- end
48
-
49
- it "returns nil if model not found" do
50
- obj = { class: :post, id: 12 }
51
- expect(Lifesaver::Marshal.load(obj)).to eql(nil)
52
- end
53
-
54
- it "returns options if they were passed" do
55
- obj = { class: :post, id: 1, status: :notified }
56
- m = Lifesaver::Marshal.load(obj)
57
- expect(m).to eql([Post.find(1), {status: :notified}])
58
- end
59
-
60
-
61
- it "rejects bad input" do
62
- obj = Post.new
63
- expect { Lifesaver::Marshal.load(obj) }.to raise_error
64
- end
65
- end
66
-
67
- describe ".dump" do
68
- it "decomposes an object to a Hash" do
69
- obj = Post.create(title: "Test Post")
70
- out = { class: :post, id: 2 }
71
- expect(Lifesaver::Marshal.dump(obj)).to eql(out)
72
- end
73
-
74
- it "adds additional key, value pairs if passed" do
75
- obj = Post.create(title: "Test Post")
76
- out = { status: :updated, class: :post, id: 2 }
77
- expect(Lifesaver::Marshal.dump(obj, {status: :updated})).to eql(out)
78
- end
79
- end
80
- end
@@ -1,91 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Lifesaver::ModelAdditions do
4
- Lifesaver.suppress_indexing
5
-
6
- describe ".notifies_for_indexing" do
7
- after(:each) do
8
- Post.send(:notifies_for_indexing, only_on_change: :authorships)
9
- end
10
- it "should return a Hash of models for notification on save and notify" do
11
- Post.send(:notifies_for_indexing, :comments,
12
- only_on_change: :authorships,
13
- only_on_notify: :authors
14
- )
15
- exp_hash = { on_change: [], on_notify: [] }
16
- exp_hash[:on_change] << :comments
17
- exp_hash[:on_change] << :authorships
18
- exp_hash[:on_notify] << :comments
19
- exp_hash[:on_notify] << :authors
20
- expect(Post.notifiable_associations).to eql(exp_hash)
21
- end
22
- end
23
-
24
- describe "#association_models" do
25
- before(:each) do
26
- @post = Post.create(title: "Some post")
27
- affiliate = Affiliate.create(name: "Some place")
28
- @author = Author.create(name: "Some guy", affiliate_id: affiliate.id)
29
- Authorship.create(post: @post, author: @author)
30
- end
31
- after(:all) do
32
- Post.destroy_all
33
- Author.destroy_all
34
- Authorship.destroy_all
35
- Affiliate.destroy_all
36
- end
37
-
38
- it "should return an array of models for multiple association" do
39
- association = @post.association_models(:authorships)
40
- expect(association[0]).to be_a_kind_of(Authorship)
41
- end
42
-
43
- it "should return an array of one model for a singular association" do
44
- association = @author.association_models(:affiliate)
45
- expect(association[0]).to be_a_kind_of(Affiliate)
46
- end
47
- end
48
-
49
- Lifesaver.unsuppress_indexing
50
-
51
- describe "#indexing_suppressed?" do
52
- let(:post) { Post.new(title: "Test Post") }
53
-
54
- it "should be false by default" do
55
- expect(post.send(:suppress_indexing?)).to eql(false)
56
- end
57
-
58
- it "should be true if overridden locally" do
59
- Lifesaver.suppress_indexing
60
- expect(post.send(:suppress_indexing?)).to eql(true)
61
- end
62
-
63
- it "should be false if override is cancelled" do
64
- Lifesaver.unsuppress_indexing
65
- expect(post.send(:suppress_indexing?)).to eql(false)
66
- end
67
-
68
- it "should be true if set individually" do
69
- post.suppress_indexing
70
- expect(post.send(:suppress_indexing?)).to eql(true)
71
- end
72
-
73
- it "should be false if unset individually" do
74
- post.unsuppress_indexing
75
- expect(post.send(:suppress_indexing?)).to eql(false)
76
- end
77
- end
78
-
79
- describe "#dependent_association_map" do
80
- it "returns an empty Hash when there are no dependent associations" do
81
- expect(Comment.new.send(:dependent_association_map)).to eql({})
82
- end
83
-
84
- it "returns a Hash with the keys of dependent_association" do
85
- dependent_map = Author.new.send(:dependent_association_map)
86
- expect(dependent_map).to eql({authorships: true})
87
- end
88
-
89
- end
90
-
91
- end
@@ -1,111 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Lifesaver do
4
- before(:all) do
5
- Lifesaver.suppress_indexing
6
- Post.destroy_all
7
- Author.destroy_all
8
- Authorship.destroy_all
9
- Affiliate.destroy_all
10
- Comment.destroy_all
11
- Lifesaver.unsuppress_indexing
12
- end
13
- before(:each) do
14
- [Author, Post].each do |klass|
15
- klass.tire.index.delete
16
- klass.tire.create_elasticsearch_index
17
- end
18
-
19
- Lifesaver.suppress_indexing
20
-
21
- @posts = []
22
- @posts << Post.create(
23
- title: "Lifesavers are my favorite candy",
24
- content: "Lorem ipsum",
25
- tags: %w(candy stuff opinions)
26
- )
27
- @posts << Post.create(
28
- title: "Birds are the best animal",
29
- content: "Lorem ipsum",
30
- tags: %w(animals stuff facts)
31
- )
32
- @posts << Post.create(
33
- title: "Chicago Cubs have a winning season",
34
- content: "Lorem ipsum",
35
- tags: %w(sports stuff jokes)
36
- )
37
- @comments = []
38
- @comments << Comment.create(
39
- post: @posts.last,
40
- text: "We love this!"
41
- )
42
- @comments << Comment.create(
43
- post: @posts.last,
44
- text: "We lied. Didn't realize it was a joke."
45
- )
46
- @authors = []
47
- @authors << Author.create(name: "Paul Sorensen")
48
- @authors << Author.create(name: "Paul Sorensen's Ghost Writer")
49
- @authors << Author.create(name: "Theo Epstein")
50
- @affiliates = []
51
- @affiliates << Affiliate.create(name: "Prosper Forebearer")
52
- @affiliates << Affiliate.create(name: "Chicago Cubs")
53
- @authors[0].affiliate_id = @affiliates.first.id
54
- @authors[1].affiliate_id = @affiliates.first.id
55
- @authors[2].affiliate_id = @affiliates.last.id
56
- @authors.each { |a| a.save! }
57
- Authorship.create(post: @posts[0], author: @authors[0])
58
- Authorship.create(post: @posts[1], author: @authors[0])
59
- Authorship.create(post: @posts[1], author: @authors[1])
60
- Authorship.create(post: @posts[2], author: @authors[2])
61
-
62
- Lifesaver.unsuppress_indexing
63
-
64
- [Author, Post].each do |klass|
65
- klass.all.each { |k| k.tire.update_index }
66
- klass.tire.index.refresh
67
- end
68
- end
69
-
70
- after(:each) do
71
- Post.destroy_all
72
- Author.destroy_all
73
- Authorship.destroy_all
74
- Affiliate.destroy_all
75
- Comment.destroy_all
76
- end
77
-
78
- it "should traverse the provided graph" do
79
- models = Lifesaver::IndexGraph.generate([{"class"=>"author", "id"=>1, "status"=>"changed"}])
80
- expect(models.size).to eql(2)
81
- end
82
-
83
- it "should reindex on destroy" do
84
- @authors[2].destroy
85
- sleep(1.seconds)
86
- expect(Author.search(query: "Theo Epstein").to_a.size).to eql(0)
87
- end
88
-
89
- it "should reindex on update" do
90
- @authors[2].name = "Harry Carry"
91
- @authors[2].save!
92
- sleep(1.seconds) # need to wait for elasticsearch to update
93
- expect(Author.search(query: "Harry Carry").to_a.size).to eql(1)
94
- end
95
-
96
- it "should update distant related indexes" do
97
- @posts[0].tags << 'werd'
98
- @posts[0].save!
99
- sleep(1.seconds)
100
- expect(Author.search(query: "werd").to_a.size).to eql(1)
101
- end
102
-
103
- it "should update related indexes if saved model doesn't have index" do
104
- @comments[0].text = "We hate this!"
105
- @comments[0].save!
106
- sleep(1.seconds)
107
- result = Post.search(query: "Chicago").to_a.first
108
- comment_text = result.comments.first.text
109
- expect(comment_text).to eql("We hate this!")
110
- end
111
- end