elasticsearch-persistence-queryable 0.1.8

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 (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CHANGELOG.md +16 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +13 -0
  6. data/README.md +678 -0
  7. data/Rakefile +57 -0
  8. data/elasticsearch-persistence.gemspec +57 -0
  9. data/examples/music/album.rb +34 -0
  10. data/examples/music/artist.rb +50 -0
  11. data/examples/music/artists/_form.html.erb +8 -0
  12. data/examples/music/artists/artists_controller.rb +67 -0
  13. data/examples/music/artists/artists_controller_test.rb +53 -0
  14. data/examples/music/artists/index.html.erb +57 -0
  15. data/examples/music/artists/show.html.erb +51 -0
  16. data/examples/music/assets/application.css +226 -0
  17. data/examples/music/assets/autocomplete.css +48 -0
  18. data/examples/music/assets/blank_cover.png +0 -0
  19. data/examples/music/assets/form.css +113 -0
  20. data/examples/music/index_manager.rb +60 -0
  21. data/examples/music/search/index.html.erb +93 -0
  22. data/examples/music/search/search_controller.rb +41 -0
  23. data/examples/music/search/search_controller_test.rb +9 -0
  24. data/examples/music/search/search_helper.rb +15 -0
  25. data/examples/music/suggester.rb +45 -0
  26. data/examples/music/template.rb +392 -0
  27. data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.css +7 -0
  28. data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.js +6 -0
  29. data/examples/notes/.gitignore +7 -0
  30. data/examples/notes/Gemfile +28 -0
  31. data/examples/notes/README.markdown +36 -0
  32. data/examples/notes/application.rb +238 -0
  33. data/examples/notes/config.ru +7 -0
  34. data/examples/notes/test.rb +118 -0
  35. data/lib/elasticsearch/per_thread_registry.rb +53 -0
  36. data/lib/elasticsearch/persistence/client.rb +51 -0
  37. data/lib/elasticsearch/persistence/inheritence.rb +9 -0
  38. data/lib/elasticsearch/persistence/model/base.rb +95 -0
  39. data/lib/elasticsearch/persistence/model/callbacks.rb +37 -0
  40. data/lib/elasticsearch/persistence/model/errors.rb +9 -0
  41. data/lib/elasticsearch/persistence/model/find.rb +155 -0
  42. data/lib/elasticsearch/persistence/model/gateway_delegation.rb +23 -0
  43. data/lib/elasticsearch/persistence/model/hash_wrapper.rb +17 -0
  44. data/lib/elasticsearch/persistence/model/rails.rb +39 -0
  45. data/lib/elasticsearch/persistence/model/store.rb +271 -0
  46. data/lib/elasticsearch/persistence/model.rb +148 -0
  47. data/lib/elasticsearch/persistence/null_relation.rb +56 -0
  48. data/lib/elasticsearch/persistence/query_cache.rb +68 -0
  49. data/lib/elasticsearch/persistence/querying.rb +21 -0
  50. data/lib/elasticsearch/persistence/relation/delegation.rb +130 -0
  51. data/lib/elasticsearch/persistence/relation/finder_methods.rb +39 -0
  52. data/lib/elasticsearch/persistence/relation/merger.rb +179 -0
  53. data/lib/elasticsearch/persistence/relation/query_builder.rb +279 -0
  54. data/lib/elasticsearch/persistence/relation/query_methods.rb +362 -0
  55. data/lib/elasticsearch/persistence/relation/search_option_methods.rb +44 -0
  56. data/lib/elasticsearch/persistence/relation/spawn_methods.rb +61 -0
  57. data/lib/elasticsearch/persistence/relation.rb +110 -0
  58. data/lib/elasticsearch/persistence/repository/class.rb +71 -0
  59. data/lib/elasticsearch/persistence/repository/find.rb +73 -0
  60. data/lib/elasticsearch/persistence/repository/naming.rb +115 -0
  61. data/lib/elasticsearch/persistence/repository/response/results.rb +105 -0
  62. data/lib/elasticsearch/persistence/repository/search.rb +156 -0
  63. data/lib/elasticsearch/persistence/repository/serialize.rb +31 -0
  64. data/lib/elasticsearch/persistence/repository/store.rb +94 -0
  65. data/lib/elasticsearch/persistence/repository.rb +77 -0
  66. data/lib/elasticsearch/persistence/scoping/default.rb +137 -0
  67. data/lib/elasticsearch/persistence/scoping/named.rb +70 -0
  68. data/lib/elasticsearch/persistence/scoping.rb +52 -0
  69. data/lib/elasticsearch/persistence/version.rb +5 -0
  70. data/lib/elasticsearch/persistence.rb +157 -0
  71. data/lib/elasticsearch/rails_compatibility.rb +17 -0
  72. data/lib/rails/generators/elasticsearch/model/model_generator.rb +21 -0
  73. data/lib/rails/generators/elasticsearch/model/templates/model.rb.tt +9 -0
  74. data/lib/rails/generators/elasticsearch_generator.rb +2 -0
  75. data/lib/rails/instrumentation/railtie.rb +31 -0
  76. data/lib/rails/instrumentation.rb +10 -0
  77. data/test/integration/model/model_basic_test.rb +157 -0
  78. data/test/integration/repository/custom_class_test.rb +85 -0
  79. data/test/integration/repository/customized_class_test.rb +82 -0
  80. data/test/integration/repository/default_class_test.rb +114 -0
  81. data/test/integration/repository/virtus_model_test.rb +114 -0
  82. data/test/test_helper.rb +53 -0
  83. data/test/unit/model_base_test.rb +48 -0
  84. data/test/unit/model_find_test.rb +148 -0
  85. data/test/unit/model_gateway_test.rb +99 -0
  86. data/test/unit/model_rails_test.rb +88 -0
  87. data/test/unit/model_store_test.rb +514 -0
  88. data/test/unit/persistence_test.rb +32 -0
  89. data/test/unit/repository_class_test.rb +51 -0
  90. data/test/unit/repository_client_test.rb +32 -0
  91. data/test/unit/repository_find_test.rb +388 -0
  92. data/test/unit/repository_indexing_test.rb +37 -0
  93. data/test/unit/repository_module_test.rb +146 -0
  94. data/test/unit/repository_naming_test.rb +146 -0
  95. data/test/unit/repository_response_results_test.rb +98 -0
  96. data/test/unit/repository_search_test.rb +117 -0
  97. data/test/unit/repository_serialize_test.rb +57 -0
  98. data/test/unit/repository_store_test.rb +303 -0
  99. metadata +487 -0
@@ -0,0 +1,146 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Persistence::RepositoryNamingTest < Test::Unit::TestCase
4
+ context "The repository naming" do
5
+ # Fake class for the naming tests
6
+ class ::Foobar; end
7
+ class ::FooBar; end
8
+ module ::Foo; class Bar; end; end
9
+
10
+ setup do
11
+ @shoulda_subject = Class.new() { include Elasticsearch::Persistence::Repository::Naming }.new
12
+ end
13
+
14
+ context "get Ruby class from the Elasticsearch type" do
15
+ should "get a simple class" do
16
+ assert_equal Foobar, subject.__get_klass_from_type('foobar')
17
+ end
18
+ should "get a camelcased class" do
19
+ assert_equal FooBar, subject.__get_klass_from_type('foo_bar')
20
+ end
21
+ should "get a namespaced class" do
22
+ assert_equal Foo::Bar, subject.__get_klass_from_type('foo/bar')
23
+ end
24
+ should "re-raise a NameError exception" do
25
+ assert_raise NameError do
26
+ subject.__get_klass_from_type('foobarbazbam')
27
+ end
28
+ end
29
+ end
30
+
31
+ context "get Elasticsearch type from the Ruby class" do
32
+ should "encode a simple class" do
33
+ assert_equal 'foobar', subject.__get_type_from_class(Foobar)
34
+ end
35
+ should "encode a camelcased class" do
36
+ assert_equal 'foo_bar', subject.__get_type_from_class(FooBar)
37
+ end
38
+ should "encode a namespaced class" do
39
+ assert_equal 'foo/bar', subject.__get_type_from_class(Foo::Bar)
40
+ end
41
+ end
42
+
43
+ context "get an ID from the document" do
44
+ should "get an ID from Hash" do
45
+ assert_equal 1, subject.__get_id_from_document(id: 1)
46
+ assert_equal 1, subject.__get_id_from_document(_id: 1)
47
+ assert_equal 1, subject.__get_id_from_document('id' => 1)
48
+ assert_equal 1, subject.__get_id_from_document('_id' => 1)
49
+ end
50
+ end
51
+
52
+ context "extract an ID from the document" do
53
+ should "delete the key from theHash" do
54
+ d1 = { :id => 1 }
55
+ d2 = { :_id => 1 }
56
+ d3 = { 'id' => 1 }
57
+ d4 = { '_id' => 1 }
58
+
59
+ assert_equal 1, subject.__extract_id_from_document(d1)
60
+ assert_nil d1[:id]
61
+
62
+ assert_equal 1, subject.__extract_id_from_document(d2)
63
+ assert_nil d1[:_id]
64
+
65
+ assert_equal 1, subject.__extract_id_from_document(d3)
66
+ assert_nil d1['id']
67
+
68
+ assert_equal 1, subject.__extract_id_from_document(d4)
69
+ assert_nil d1['_id']
70
+ end
71
+ end
72
+
73
+ context "document class name" do
74
+ should "be nil by default" do
75
+ assert_nil subject.klass
76
+ end
77
+
78
+ should "be settable" do
79
+ subject.klass = Foobar
80
+ assert_equal Foobar, subject.klass
81
+ end
82
+
83
+ should "be settable by DSL" do
84
+ subject.klass Foobar
85
+ assert_equal Foobar, subject.klass
86
+ end
87
+ end
88
+
89
+ context "index_name" do
90
+ should "default to the class name" do
91
+ subject.instance_eval do
92
+ def self.class
93
+ 'FakeRepository'
94
+ end
95
+ end
96
+
97
+ assert_equal 'fake_repository', subject.index_name
98
+ end
99
+
100
+ should "be settable" do
101
+ subject.index_name = 'foobar1'
102
+ assert_equal 'foobar1', subject.index_name
103
+
104
+ subject.index_name 'foobar2'
105
+ assert_equal 'foobar2', subject.index_name
106
+ end
107
+
108
+ should "be aliased as `index`" do
109
+ subject.index_name = 'foobar1'
110
+ assert_equal 'foobar1', subject.index
111
+ end
112
+
113
+ should "be inferred from the host class" do
114
+ class ::MySpecialRepository; end
115
+ subject.define_singleton_method(:host) { MySpecialRepository }
116
+ assert_equal 'my_special_repository', subject.index_name
117
+ end
118
+ end
119
+
120
+ context "document_type" do
121
+ should "be nil when no klass is set" do
122
+ assert_equal nil, subject.document_type
123
+ end
124
+
125
+ should "default to klass" do
126
+ subject.klass Foobar
127
+ assert_equal 'foobar', subject.document_type
128
+ end
129
+
130
+ should "be aliased as `type`" do
131
+ subject.klass Foobar
132
+ assert_equal 'foobar', subject.type
133
+ end
134
+
135
+ should "be settable" do
136
+ subject.document_type = 'foobar'
137
+ assert_equal 'foobar', subject.document_type
138
+ end
139
+
140
+ should "be settable by DSL" do
141
+ subject.document_type 'foobar'
142
+ assert_equal 'foobar', subject.document_type
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,98 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Persistence::RepositoryResponseResultsTest < Test::Unit::TestCase
4
+ include Elasticsearch::Persistence
5
+ class MyDocument; end
6
+
7
+ context "Response results" do
8
+ setup do
9
+ @repository = Repository.new
10
+
11
+ @response = { "took" => 2,
12
+ "timed_out" => false,
13
+ "_shards" => {"total" => 5, "successful" => 5, "failed" => 0},
14
+ "hits" =>
15
+ { "total" => 2,
16
+ "max_score" => 0.19,
17
+ "hits" =>
18
+ [{"_index" => "my_index",
19
+ "_type" => "note",
20
+ "_id" => "1",
21
+ "_score" => 0.19,
22
+ "_source" => {"id" => 1, "title" => "Test 1"}},
23
+
24
+ {"_index" => "my_index",
25
+ "_type" => "note",
26
+ "_id" => "2",
27
+ "_score" => 0.19,
28
+ "_source" => {"id" => 2, "title" => "Test 2"}}
29
+ ]
30
+ }
31
+ }
32
+
33
+ @shoulda_subject = Repository::Response::Results.new @repository, @response
34
+ end
35
+
36
+ should "provide the access to the repository" do
37
+ assert_instance_of Repository::Class, subject.repository
38
+ end
39
+
40
+ should "provide the access to the response" do
41
+ assert_equal 5, subject.response['_shards']['total']
42
+ end
43
+
44
+ should "wrap the response in Hashie::Mash" do
45
+ assert_equal 5, subject.response._shards.total
46
+ end
47
+
48
+ should "return the total" do
49
+ assert_equal 2, subject.total
50
+ end
51
+
52
+ should "return the max_score" do
53
+ assert_equal 0.19, subject.max_score
54
+ end
55
+
56
+ should "delegate methods to results" do
57
+ subject.repository
58
+ .expects(:deserialize)
59
+ .twice
60
+ .returns(MyDocument.new)
61
+
62
+ assert_equal 2, subject.size
63
+ assert_respond_to subject, :each
64
+ end
65
+
66
+ should "respond to missing" do
67
+ assert_instance_of Method, subject.method(:to_a)
68
+ end
69
+
70
+ should "yield each object with hit" do
71
+ @shoulda_subject = Repository::Response::Results.new \
72
+ @repository,
73
+ { 'hits' => { 'hits' => [{'_id' => '1', 'foo' => 'bar'}] } }
74
+
75
+ subject.repository
76
+ .expects(:deserialize)
77
+ .returns('FOO')
78
+
79
+ subject.each_with_hit do |object, hit|
80
+ assert_equal 'FOO', object
81
+ assert_equal 'bar', hit.foo
82
+ end
83
+ end
84
+
85
+ should "map objects and hits" do
86
+ @shoulda_subject = Repository::Response::Results.new \
87
+ @repository,
88
+ { 'hits' => { 'hits' => [{'_id' => '1', 'foo' => 'bar'}] } }
89
+
90
+ subject.repository
91
+ .expects(:deserialize)
92
+ .returns('FOO')
93
+
94
+ assert_equal ['FOO---bar'], subject.map_with_hit { |object, hit| "#{object}---#{hit.foo}" }
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,117 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Persistence::RepositorySearchTest < Test::Unit::TestCase
4
+ class MyDocument; end
5
+
6
+ context "The repository search" do
7
+ setup do
8
+ @shoulda_subject = Class.new() { include Elasticsearch::Persistence::Repository::Search }.new
9
+
10
+ @client = mock
11
+ @shoulda_subject.stubs(:document_type).returns(nil)
12
+ @shoulda_subject.stubs(:klass).returns(nil)
13
+ @shoulda_subject.stubs(:index_name).returns('test')
14
+ @shoulda_subject.stubs(:client).returns(@client)
15
+ end
16
+
17
+ should "search in type based on klass" do
18
+ subject.expects(:klass).returns(MyDocument).at_least_once
19
+ subject.expects(:__get_type_from_class).with(MyDocument).returns('my_document')
20
+
21
+ @client.expects(:search).with do |arguments|
22
+ assert_equal 'test', arguments[:index]
23
+ assert_equal 'my_document', arguments[:type]
24
+ assert_equal({foo: 'bar'}, arguments[:body])
25
+ true
26
+ end
27
+
28
+ subject.search foo: 'bar'
29
+ end
30
+
31
+ should "search in type based on document_type" do
32
+ subject.expects(:document_type).returns('my_special_document').at_least_once
33
+ subject.expects(:__get_type_from_class).never
34
+
35
+ @client.expects(:search).with do |arguments|
36
+ assert_equal 'test', arguments[:index]
37
+ assert_equal 'my_special_document', arguments[:type]
38
+ assert_equal({foo: 'bar'}, arguments[:body])
39
+ true
40
+ end
41
+
42
+ subject.search foo: 'bar'
43
+ end
44
+
45
+ should "search across all types" do
46
+ subject.expects(:document_type).returns(nil).at_least_once
47
+ subject.expects(:klass).returns(nil).at_least_once
48
+ subject.expects(:__get_type_from_class).never
49
+
50
+ @client.expects(:search).with do |arguments|
51
+ assert_equal 'test', arguments[:index]
52
+ assert_equal nil, arguments[:type]
53
+ assert_equal({foo: 'bar'}, arguments[:body])
54
+ true
55
+ end
56
+
57
+ assert_instance_of Elasticsearch::Persistence::Repository::Response::Results,
58
+ subject.search(foo: 'bar')
59
+ end
60
+
61
+ should "pass options to the client" do
62
+ subject.expects(:klass).returns(nil).at_least_once
63
+ subject.expects(:__get_type_from_class).never
64
+
65
+ @client.expects(:search).twice.with do |arguments|
66
+ assert_equal 'bambam', arguments[:routing]
67
+ true
68
+ end
69
+
70
+ assert_instance_of Elasticsearch::Persistence::Repository::Response::Results,
71
+ subject.search( {foo: 'bar'}, { routing: 'bambam' } )
72
+ assert_instance_of Elasticsearch::Persistence::Repository::Response::Results,
73
+ subject.search( 'foobar', { routing: 'bambam' } )
74
+ end
75
+
76
+ should "search with simple search" do
77
+ subject.expects(:klass).returns(nil).at_least_once
78
+ subject.expects(:__get_type_from_class).never
79
+
80
+ @client.expects(:search).with do |arguments|
81
+ assert_equal 'foobar', arguments[:q]
82
+ true
83
+ end
84
+
85
+ assert_instance_of Elasticsearch::Persistence::Repository::Response::Results,
86
+ subject.search('foobar')
87
+ end
88
+
89
+ should "raise error for incorrect search definitions" do
90
+ subject.expects(:klass).returns(nil).at_least_once
91
+ subject.expects(:__get_type_from_class).never
92
+
93
+ assert_raise ArgumentError do
94
+ subject.search 123
95
+ end
96
+ end
97
+
98
+ should "return the number of domain objects" do
99
+ subject.expects(:search)
100
+ .returns(Elasticsearch::Persistence::Repository::Response::Results.new( subject, {'hits' => { 'total' => 1 }}))
101
+ assert_equal 1, subject.count
102
+ end
103
+
104
+ should "pass arguments from count to search" do
105
+ subject.expects(:search)
106
+ .with do |query_or_definition, options|
107
+ assert_equal 'bar', query_or_definition[:query][:match][:foo]
108
+ assert_equal true, options[:ignore_unavailable]
109
+ true
110
+ end
111
+ .returns(Elasticsearch::Persistence::Repository::Response::Results.new( subject, {'hits' => { 'total' => 1 }}))
112
+
113
+ subject.count( { query: { match: { foo: 'bar' } } }, { ignore_unavailable: true } )
114
+ end
115
+ end
116
+
117
+ end
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Persistence::RepositorySerializeTest < Test::Unit::TestCase
4
+ context "The repository serialization" do
5
+ class DummyDocument
6
+ def to_hash
7
+ { foo: 'bar' }
8
+ end
9
+ end
10
+
11
+ class MyDocument; end
12
+
13
+ setup do
14
+ @shoulda_subject = Class.new() { include Elasticsearch::Persistence::Repository::Serialize }.new
15
+ end
16
+
17
+ context "serialize" do
18
+ should "call #to_hash on passed object" do
19
+ document = DummyDocument.new
20
+ assert_equal( { foo: 'bar' }, subject.serialize(document))
21
+ end
22
+ end
23
+
24
+ context "deserialize" do
25
+ should "get the class name from #klass" do
26
+ subject.expects(:klass)
27
+ .returns(MyDocument)
28
+
29
+ MyDocument.expects(:new)
30
+
31
+ subject.deserialize( {} )
32
+ end
33
+
34
+ should "get the class name from Elasticsearch _type" do
35
+ subject.expects(:klass)
36
+ .returns(nil)
37
+
38
+ subject.expects(:__get_klass_from_type)
39
+ .returns(MyDocument)
40
+
41
+ MyDocument.expects(:new)
42
+
43
+ subject.deserialize( {} )
44
+ end
45
+
46
+ should "create the class instance with _source attributes" do
47
+ subject.expects(:klass).returns(nil)
48
+
49
+ subject.expects(:__get_klass_from_type).returns(MyDocument)
50
+
51
+ MyDocument.expects(:new).with({ 'foo' => 'bar' })
52
+
53
+ subject.deserialize( {'_source' => { 'foo' => 'bar' } } )
54
+ end
55
+ end
56
+ end
57
+ end