elasticsearch-persistence 5.1.0 → 6.0.0.pre

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/Gemfile +9 -0
  4. data/README.md +164 -323
  5. data/Rakefile +8 -8
  6. data/elasticsearch-persistence.gemspec +4 -5
  7. data/lib/elasticsearch/persistence.rb +2 -110
  8. data/lib/elasticsearch/persistence/repository.rb +212 -53
  9. data/lib/elasticsearch/persistence/repository/dsl.rb +94 -0
  10. data/lib/elasticsearch/persistence/repository/find.rb +27 -10
  11. data/lib/elasticsearch/persistence/repository/response/results.rb +17 -5
  12. data/lib/elasticsearch/persistence/repository/search.rb +15 -4
  13. data/lib/elasticsearch/persistence/repository/serialize.rb +65 -7
  14. data/lib/elasticsearch/persistence/repository/store.rb +38 -44
  15. data/lib/elasticsearch/persistence/version.rb +1 -1
  16. data/spec/repository/find_spec.rb +179 -0
  17. data/spec/repository/response/results_spec.rb +105 -0
  18. data/spec/repository/search_spec.rb +181 -0
  19. data/spec/repository/serialize_spec.rb +53 -0
  20. data/spec/repository/store_spec.rb +327 -0
  21. data/spec/repository_spec.rb +716 -0
  22. data/spec/spec_helper.rb +28 -0
  23. metadata +25 -80
  24. data/lib/elasticsearch/persistence/client.rb +0 -51
  25. data/lib/elasticsearch/persistence/model.rb +0 -153
  26. data/lib/elasticsearch/persistence/model/base.rb +0 -87
  27. data/lib/elasticsearch/persistence/model/errors.rb +0 -8
  28. data/lib/elasticsearch/persistence/model/find.rb +0 -180
  29. data/lib/elasticsearch/persistence/model/rails.rb +0 -47
  30. data/lib/elasticsearch/persistence/model/store.rb +0 -254
  31. data/lib/elasticsearch/persistence/model/utils.rb +0 -0
  32. data/lib/elasticsearch/persistence/repository/class.rb +0 -71
  33. data/lib/elasticsearch/persistence/repository/naming.rb +0 -115
  34. data/lib/rails/generators/elasticsearch/model/model_generator.rb +0 -21
  35. data/lib/rails/generators/elasticsearch/model/templates/model.rb.tt +0 -9
  36. data/lib/rails/generators/elasticsearch_generator.rb +0 -2
  37. data/test/integration/model/model_basic_test.rb +0 -238
  38. data/test/integration/repository/custom_class_test.rb +0 -85
  39. data/test/integration/repository/customized_class_test.rb +0 -82
  40. data/test/integration/repository/default_class_test.rb +0 -116
  41. data/test/integration/repository/virtus_model_test.rb +0 -118
  42. data/test/test_helper.rb +0 -55
  43. data/test/unit/model_base_test.rb +0 -72
  44. data/test/unit/model_find_test.rb +0 -153
  45. data/test/unit/model_gateway_test.rb +0 -101
  46. data/test/unit/model_rails_test.rb +0 -112
  47. data/test/unit/model_store_test.rb +0 -576
  48. data/test/unit/persistence_test.rb +0 -32
  49. data/test/unit/repository_class_test.rb +0 -51
  50. data/test/unit/repository_client_test.rb +0 -32
  51. data/test/unit/repository_find_test.rb +0 -388
  52. data/test/unit/repository_indexing_test.rb +0 -37
  53. data/test/unit/repository_module_test.rb +0 -146
  54. data/test/unit/repository_naming_test.rb +0 -146
  55. data/test/unit/repository_response_results_test.rb +0 -98
  56. data/test/unit/repository_search_test.rb +0 -117
  57. data/test/unit/repository_serialize_test.rb +0 -57
  58. data/test/unit/repository_store_test.rb +0 -303
data/Rakefile CHANGED
@@ -7,24 +7,24 @@ task :test => 'test:unit'
7
7
  # ----- Test tasks ------------------------------------------------------------
8
8
 
9
9
  require 'rake/testtask'
10
+ require 'rspec/core/rake_task'
11
+
10
12
  namespace :test do
13
+
14
+ RSpec::Core::RakeTask.new(:spec)
11
15
  Rake::TestTask.new(:unit) do |test|
12
- test.libs << 'lib' << 'test'
13
- test.test_files = FileList["test/unit/**/*_test.rb"]
14
- test.verbose = false
15
- test.warning = false
16
16
  end
17
17
 
18
18
  Rake::TestTask.new(:integration) do |test|
19
- test.libs << 'lib' << 'test'
20
- test.test_files = FileList["test/integration/**/*_test.rb"]
21
19
  test.verbose = false
22
20
  test.warning = false
21
+ test.deps = [ :spec ]
23
22
  end
24
23
 
25
24
  Rake::TestTask.new(:all) do |test|
26
- test.libs << 'lib' << 'test'
27
- test.test_files = FileList["test/unit/**/*_test.rb", "test/integration/**/*_test.rb"]
25
+ test.verbose = false
26
+ test.warning = false
27
+ test.deps = [ :spec ]
28
28
  end
29
29
  end
30
30
 
@@ -23,17 +23,16 @@ Gem::Specification.new do |s|
23
23
 
24
24
  s.required_ruby_version = ">= 1.9.3"
25
25
 
26
- s.add_dependency "elasticsearch", '~> 5'
27
- s.add_dependency "elasticsearch-model", '~> 5'
26
+ s.add_dependency "elasticsearch", '~> 6'
27
+ s.add_dependency "elasticsearch-model", '>= 5'
28
28
  s.add_dependency "activesupport", '> 4'
29
29
  s.add_dependency "activemodel", '> 4'
30
30
  s.add_dependency "hashie"
31
- s.add_dependency "virtus"
32
31
 
33
32
  s.add_development_dependency "bundler", "~> 1.5"
34
33
  s.add_development_dependency "rake", "~> 11.1"
35
34
 
36
- s.add_development_dependency "oj"
35
+ s.add_development_dependency "oj" unless defined?(JRUBY_VERSION)
37
36
 
38
37
  s.add_development_dependency "rails", '> 4'
39
38
 
@@ -45,7 +44,7 @@ Gem::Specification.new do |s|
45
44
  s.add_development_dependency "mocha"
46
45
  s.add_development_dependency "turn"
47
46
  s.add_development_dependency "yard"
48
- s.add_development_dependency "ruby-prof"
47
+ s.add_development_dependency "ruby-prof" unless defined?(JRUBY_VERSION)
49
48
  s.add_development_dependency "pry"
50
49
 
51
50
  s.add_development_dependency "simplecov"
@@ -1,116 +1,8 @@
1
1
  require 'hashie/mash'
2
2
 
3
3
  require 'elasticsearch'
4
-
5
- require 'elasticsearch/model/hash_wrapper'
6
- require 'elasticsearch/model/indexing'
7
- require 'elasticsearch/model/searching'
8
-
9
- require 'active_support/inflector'
4
+ require 'elasticsearch/model'
10
5
 
11
6
  require 'elasticsearch/persistence/version'
12
-
13
- require 'elasticsearch/persistence/client'
14
- require 'elasticsearch/persistence/repository/response/results'
15
- require 'elasticsearch/persistence/repository/naming'
16
- require 'elasticsearch/persistence/repository/serialize'
17
- require 'elasticsearch/persistence/repository/store'
18
- require 'elasticsearch/persistence/repository/find'
19
- require 'elasticsearch/persistence/repository/search'
20
- require 'elasticsearch/persistence/repository/class'
21
7
  require 'elasticsearch/persistence/repository'
22
-
23
- module Elasticsearch
24
-
25
- # Persistence for Ruby domain objects and models in Elasticsearch
26
- # ===============================================================
27
- #
28
- # `Elasticsearch::Persistence` contains modules for storing and retrieving Ruby domain objects and models
29
- # in Elasticsearch.
30
- #
31
- # == Repository
32
- #
33
- # The repository patterns allows to store and retrieve Ruby objects in Elasticsearch.
34
- #
35
- # require 'elasticsearch/persistence'
36
- #
37
- # class Note
38
- # def to_hash; {foo: 'bar'}; end
39
- # end
40
- #
41
- # repository = Elasticsearch::Persistence::Repository.new
42
- #
43
- # repository.save Note.new
44
- # # => {"_index"=>"repository", "_type"=>"note", "_id"=>"mY108X9mSHajxIy2rzH2CA", ...}
45
- #
46
- # Customize your repository by including the main module in a Ruby class
47
- # class MyRepository
48
- # include Elasticsearch::Persistence::Repository
49
- #
50
- # index 'my_notes'
51
- # klass Note
52
- #
53
- # client Elasticsearch::Client.new log: true
54
- # end
55
- #
56
- # repository = MyRepository.new
57
- #
58
- # repository.save Note.new
59
- # # 2014-04-04 22:15:25 +0200: POST http://localhost:9200/my_notes/note [status:201, request:0.009s, query:n/a]
60
- # # 2014-04-04 22:15:25 +0200: > {"foo":"bar"}
61
- # # 2014-04-04 22:15:25 +0200: < {"_index":"my_notes","_type":"note","_id":"-d28yXLFSlusnTxb13WIZQ", ...}
62
- #
63
- # == Model
64
- #
65
- # The active record pattern allows to use the interface familiar from ActiveRecord models:
66
- #
67
- # require 'elasticsearch/persistence'
68
- #
69
- # class Article
70
- # attribute :title, String, mapping: { analyzer: 'snowball' }
71
- # end
72
- #
73
- # article = Article.new id: 1, title: 'Test'
74
- # article.save
75
- #
76
- # Article.find(1)
77
- #
78
- # article.update_attributes title: 'Update'
79
- #
80
- # article.destroy
81
- #
82
- module Persistence
83
-
84
- # :nodoc:
85
- module ClassMethods
86
-
87
- # Get or set the default client for all repositories and models
88
- #
89
- # @example Set and configure the default client
90
- #
91
- # Elasticsearch::Persistence.client Elasticsearch::Client.new host: 'http://localhost:9200', tracer: true
92
- #
93
- # @example Perform an API request through the client
94
- #
95
- # Elasticsearch::Persistence.client.cluster.health
96
- # # => { "cluster_name" => "elasticsearch" ... }
97
- #
98
- def client client=nil
99
- @client = client || @client || Elasticsearch::Client.new
100
- end
101
-
102
- # Set the default client for all repositories and models
103
- #
104
- # @example Set and configure the default client
105
- #
106
- # Elasticsearch::Persistence.client = Elasticsearch::Client.new host: 'http://localhost:9200', tracer: true
107
- # => #<Elasticsearch::Transport::Client:0x007f96a6dd0d80 @transport=... >
108
- #
109
- def client=(client)
110
- @client = client
111
- end
112
- end
113
-
114
- extend ClassMethods
115
- end
116
- end
8
+ require 'elasticsearch/persistence/repository/response/results'
@@ -1,77 +1,236 @@
1
+ require 'elasticsearch/persistence/repository/dsl'
2
+ require 'elasticsearch/persistence/repository/find'
3
+ require 'elasticsearch/persistence/repository/store'
4
+ require 'elasticsearch/persistence/repository/serialize'
5
+ require 'elasticsearch/persistence/repository/search'
6
+
1
7
  module Elasticsearch
2
8
  module Persistence
3
9
 
4
- # Delegate methods to the repository (acting as a gateway)
10
+ # The base Repository mixin. This module should be included in classes that
11
+ # represent an Elasticsearch repository.
5
12
  #
6
- module GatewayDelegation
7
- def method_missing(method_name, *arguments, &block)
8
- gateway.respond_to?(method_name) ? gateway.__send__(method_name, *arguments, &block) : super
13
+ # @since 6.0.0
14
+ module Repository
15
+ include Store
16
+ include Serialize
17
+ include Find
18
+ include Search
19
+ include Elasticsearch::Model::Indexing::ClassMethods
20
+
21
+ def self.included(base)
22
+ base.send(:extend, ClassMethods)
9
23
  end
10
24
 
11
- def respond_to?(method_name, include_private=false)
12
- gateway.respond_to?(method_name) || super
25
+ module ClassMethods
26
+
27
+ # Initialize a repository instance. Optionally provide a block to define index mappings or
28
+ # settings on the repository instance.
29
+ #
30
+ # @example Create a new repository.
31
+ # MyRepository.create(index_name: 'notes', klass: Note)
32
+ #
33
+ # @example Create a new repository and evaluate a block on it.
34
+ # MyRepository.create(index_name: 'notes', klass: Note) do
35
+ # mapping dynamic: 'strict' do
36
+ # indexes :title
37
+ # end
38
+ # end
39
+ #
40
+ # @param [ Hash ] options The options to use.
41
+ # @param [ Proc ] block A block to evaluate on the new repository instance.
42
+ #
43
+ # @option options [ Symbol, String ] :index_name The name of the index.
44
+ # @option options [ Symbol, String ] :document_type The type of documents persisted in this repository.
45
+ # @option options [ Symbol, String ] :client The client used to handle requests to and from Elasticsearch.
46
+ # @option options [ Symbol, String ] :klass The class used to instantiate an object when documents are
47
+ # deserialized. The default is nil, in which case the raw document will be returned as a Hash.
48
+ # @option options [ Elasticsearch::Model::Indexing::Mappings, Hash ] :mapping The mapping for this index.
49
+ # @option options [ Elasticsearch::Model::Indexing::Settings, Hash ] :settings The settings for this index.
50
+ #
51
+ # @since 6.0.0
52
+ def create(options = {}, &block)
53
+ new(options).tap do |obj|
54
+ obj.instance_eval(&block) if block_given?
55
+ end
56
+ end
13
57
  end
14
58
 
15
- def respond_to_missing?(method_name, *)
16
- gateway.respond_to?(method_name) || super
59
+ # The default index name.
60
+ #
61
+ # @return [ String ] The default index name.
62
+ #
63
+ # @since 6.0.0
64
+ DEFAULT_INDEX_NAME = 'repository'.freeze
65
+
66
+ # The default document type.
67
+ #
68
+ # @return [ String ] The default document type.
69
+ #
70
+ # @note the document type will no longer be configurable in future versions
71
+ # of Elasticsearch.
72
+ #
73
+ # @since 6.0.0
74
+ DEFAULT_DOC_TYPE = '_doc'.freeze
75
+
76
+ # The repository options.
77
+ #
78
+ # @return [ Hash ]
79
+ #
80
+ # @since 6.0.0
81
+ attr_reader :options
82
+
83
+ # Initialize a repository instance.
84
+ #
85
+ # @example Initialize the repository.
86
+ # MyRepository.new(index_name: 'notes', klass: Note)
87
+ #
88
+ # @param [ Hash ] options The options to use.
89
+ #
90
+ # @option options [ Symbol, String ] :index_name The name of the index.
91
+ # @option options [ Symbol, String ] :document_type The type of documents persisted in this repository.
92
+ # @option options [ Symbol, String ] :client The client used to handle requests to and from Elasticsearch.
93
+ # @option options [ Symbol, String ] :klass The class used to instantiate an object when documents are
94
+ # deserialized. The default is nil, in which case the raw document will be returned as a Hash.
95
+ # @option options [ Elasticsearch::Model::Indexing::Mappings, Hash ] :mapping The mapping for this index.
96
+ # @option options [ Elasticsearch::Model::Indexing::Settings, Hash ] :settings The settings for this index.
97
+ #
98
+ # @since 6.0.0
99
+ def initialize(options = {})
100
+ @options = options
17
101
  end
18
- end
19
102
 
20
- # When included, creates an instance of the {Repository::Class Repository} class as a "gateway"
21
- #
22
- # @example Include the repository in a custom class
23
- #
24
- # require 'elasticsearch/persistence'
25
- #
26
- # class MyRepository
27
- # include Elasticsearch::Persistence::Repository
28
- # end
29
- #
30
- module Repository
31
- def self.included(base)
32
- gateway = Elasticsearch::Persistence::Repository::Class.new host: base
103
+ # Get the client used by the repository.
104
+ #
105
+ # @example
106
+ # repository.client
107
+ #
108
+ # @return [ Elasticsearch::Client ] The repository's client.
109
+ #
110
+ # @since 6.0.0
111
+ def client
112
+ @client ||= @options[:client] ||
113
+ __get_class_value(:client) ||
114
+ Elasticsearch::Client.new
115
+ end
33
116
 
34
- # Define the instance level gateway
35
- #
36
- base.class_eval do
37
- define_method :gateway do
38
- @gateway ||= gateway
39
- end
117
+ # Get the document type used by the repository object.
118
+ #
119
+ # @example
120
+ # repository.document_type
121
+ #
122
+ # @return [ String, Symbol ] The repository's document type.
123
+ #
124
+ # @since 6.0.0
125
+ def document_type
126
+ @document_type ||= @options[:document_type] ||
127
+ __get_class_value(:document_type) ||
128
+ DEFAULT_DOC_TYPE
129
+ end
40
130
 
41
- include GatewayDelegation
42
- end
131
+ # Get the index name used by the repository.
132
+ #
133
+ # @example
134
+ # repository.index_name
135
+ #
136
+ # @return [ String, Symbol ] The repository's index name.
137
+ #
138
+ # @since 6.0.0
139
+ def index_name
140
+ @index_name ||= @options[:index_name] ||
141
+ __get_class_value(:index_name) ||
142
+ DEFAULT_INDEX_NAME
143
+ end
43
144
 
44
- # Define the class level gateway
45
- #
46
- (class << base; self; end).class_eval do
47
- define_method :gateway do |&block|
48
- @gateway ||= gateway
49
- @gateway.instance_eval(&block) if block
50
- @gateway
145
+ # Get the class used by the repository when deserializing.
146
+ #
147
+ # @example
148
+ # repository.klass
149
+ #
150
+ # @return [ Class ] The repository's klass for deserializing.
151
+ #
152
+ # @since 6.0.0
153
+ def klass
154
+ @klass ||= @options[:klass] || __get_class_value(:klass)
155
+ end
156
+
157
+ # Get the index mapping. Optionally pass a block to define the mappings.
158
+ #
159
+ # @example
160
+ # repository.mapping
161
+ #
162
+ # @example Set the mappings with a block.
163
+ # repository.mapping dynamic: 'strict' do
164
+ # indexes :foo
165
+ # end
166
+ # end
167
+ #
168
+ # @note If mappings were set when the repository was created, a block passed to this
169
+ # method will not be evaluated.
170
+ #
171
+ # @return [ Elasticsearch::Model::Indexing::Mappings ] The index mappings.
172
+ #
173
+ # @since 6.0.0
174
+ def mapping(*args)
175
+ @memoized_mapping ||= @options[:mapping] || (begin
176
+ if _mapping = __get_class_value(:mapping)
177
+ _mapping.instance_variable_set(:@type, document_type)
178
+ _mapping
51
179
  end
180
+ end) || (super && @mapping)
181
+ end
182
+ alias :mappings :mapping
52
183
 
53
- include GatewayDelegation
54
- end
184
+ # Get the index settings.
185
+ #
186
+ # @example
187
+ # repository.settings
188
+ #
189
+ # @example Set the settings with a block.
190
+ # repository.settings number_of_shards: 1, number_of_replicas: 0 do
191
+ # mapping dynamic: 'strict' do
192
+ # indexes :foo do
193
+ # indexes :bar
194
+ # end
195
+ # end
196
+ # end
197
+ #
198
+ # @return [ Elasticsearch::Model::Indexing::Settings ] The index settings.
199
+ #
200
+ # @since 6.0.0
201
+ def settings(*args)
202
+ @memoized_settings ||= @options[:settings] || __get_class_value(:settings) || (super && @settings)
203
+ end
55
204
 
56
- # Catch repository methods (such as `serialize` and others) defined in the receiving class,
57
- # and overload the default definition in the gateway
58
- #
59
- def base.method_added(name)
60
- if :gateway != name && respond_to?(:gateway) && (gateway.public_methods - Object.public_methods).include?(name)
61
- gateway.define_singleton_method(name, self.new.method(name).to_proc)
62
- end
63
- end
205
+ # Determine whether the index with this repository's index name exists.
206
+ #
207
+ # @example
208
+ # repository.index_exists?
209
+ #
210
+ # @return [ true, false ] Whether the index exists.
211
+ #
212
+ # @since 6.0.0
213
+ def index_exists?(*args)
214
+ super(index_name: index_name)
64
215
  end
65
216
 
66
- # Shortcut method to allow concise repository initialization
217
+ # Get the nicer formatted string for use in inspection.
67
218
  #
68
- # @example Create a new default repository
219
+ # @example Inspect the repository.
220
+ # repository.inspect
69
221
  #
70
- # repository = Elasticsearch::Persistence::Repository.new
222
+ # @return [ String ] The repository inspection.
71
223
  #
72
- def new(options={}, &block)
73
- Elasticsearch::Persistence::Repository::Class.new( {index: 'repository'}.merge(options), &block )
74
- end; module_function :new
224
+ # @since 6.0.0
225
+ def inspect
226
+ "#<#{self.class}:0x#{object_id} index_name=#{index_name} document_type=#{document_type} klass=#{klass}>"
227
+ end
228
+
229
+ private
230
+
231
+ def __get_class_value(_method_)
232
+ self.class.send(_method_) if self.class.respond_to?(_method_)
233
+ end
75
234
  end
76
235
  end
77
236
  end