elastictastic 0.5.0 → 0.10.2
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/LICENSE +1 -1
- data/README.md +161 -10
- data/lib/elastictastic/adapter.rb +84 -0
- data/lib/elastictastic/association.rb +6 -0
- data/lib/elastictastic/basic_document.rb +213 -0
- data/lib/elastictastic/bulk_persistence_strategy.rb +64 -19
- data/lib/elastictastic/callbacks.rb +18 -12
- data/lib/elastictastic/child_collection_proxy.rb +15 -11
- data/lib/elastictastic/client.rb +47 -24
- data/lib/elastictastic/configuration.rb +59 -4
- data/lib/elastictastic/dirty.rb +43 -28
- data/lib/elastictastic/discrete_persistence_strategy.rb +48 -23
- data/lib/elastictastic/document.rb +1 -85
- data/lib/elastictastic/embedded_document.rb +34 -0
- data/lib/elastictastic/errors.rb +17 -5
- data/lib/elastictastic/field.rb +3 -0
- data/lib/elastictastic/mass_assignment_security.rb +2 -4
- data/lib/elastictastic/middleware.rb +66 -84
- data/lib/elastictastic/multi_get.rb +30 -0
- data/lib/elastictastic/multi_search.rb +70 -0
- data/lib/elastictastic/nested_document.rb +3 -27
- data/lib/elastictastic/new_relic_instrumentation.rb +8 -8
- data/lib/elastictastic/observing.rb +8 -6
- data/lib/elastictastic/optimistic_locking.rb +57 -0
- data/lib/elastictastic/parent_child.rb +56 -54
- data/lib/elastictastic/persistence.rb +16 -16
- data/lib/elastictastic/properties.rb +136 -96
- data/lib/elastictastic/railtie.rb +1 -1
- data/lib/elastictastic/rotor.rb +105 -0
- data/lib/elastictastic/scope.rb +186 -56
- data/lib/elastictastic/server_error.rb +20 -1
- data/lib/elastictastic/test_helpers.rb +152 -97
- data/lib/elastictastic/thrift/constants.rb +12 -0
- data/lib/elastictastic/thrift/rest.rb +83 -0
- data/lib/elastictastic/thrift/types.rb +124 -0
- data/lib/elastictastic/thrift_adapter.rb +61 -0
- data/lib/elastictastic/transport_methods.rb +27 -0
- data/lib/elastictastic/validations.rb +11 -13
- data/lib/elastictastic/version.rb +1 -1
- data/lib/elastictastic.rb +148 -27
- data/spec/environment.rb +1 -1
- data/spec/examples/bulk_persistence_strategy_spec.rb +151 -23
- data/spec/examples/callbacks_spec.rb +65 -34
- data/spec/examples/dirty_spec.rb +160 -1
- data/spec/examples/document_spec.rb +168 -106
- data/spec/examples/middleware_spec.rb +1 -61
- data/spec/examples/multi_get_spec.rb +127 -0
- data/spec/examples/multi_search_spec.rb +113 -0
- data/spec/examples/observing_spec.rb +24 -3
- data/spec/examples/optimistic_locking_spec.rb +417 -0
- data/spec/examples/parent_child_spec.rb +73 -33
- data/spec/examples/properties_spec.rb +53 -0
- data/spec/examples/rotor_spec.rb +132 -0
- data/spec/examples/scope_spec.rb +78 -18
- data/spec/examples/search_spec.rb +26 -0
- data/spec/examples/validation_spec.rb +7 -1
- data/spec/models/author.rb +1 -1
- data/spec/models/blog.rb +2 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/photo.rb +9 -0
- data/spec/models/post.rb +3 -0
- metadata +97 -78
- data/lib/elastictastic/resource.rb +0 -4
- data/spec/examples/active_model_lint_spec.rb +0 -20
@@ -1,37 +1,70 @@
|
|
1
|
-
require '
|
1
|
+
require 'elastictastic/transport_methods'
|
2
2
|
|
3
3
|
module Elastictastic
|
4
|
+
|
4
5
|
module Middleware
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
class Base
|
8
|
+
|
9
|
+
include TransportMethods
|
10
|
+
|
11
|
+
def initialize(connection)
|
12
|
+
@connection = connection
|
13
|
+
end
|
14
|
+
|
15
|
+
def request(method, path, body = nil)
|
16
|
+
@connection.request(method, path, body)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class JsonEncodeBody < Base
|
22
|
+
|
23
|
+
def request(method, path, body = nil)
|
24
|
+
case body
|
8
25
|
when String, nil
|
9
|
-
|
10
|
-
else
|
26
|
+
super
|
27
|
+
else
|
28
|
+
@connection.request(
|
29
|
+
method, path,
|
30
|
+
Elastictastic.json_encode(body)
|
31
|
+
)
|
11
32
|
end
|
12
|
-
@app.call(env)
|
13
33
|
end
|
34
|
+
|
14
35
|
end
|
15
36
|
|
16
|
-
class JsonDecodeResponse <
|
17
|
-
|
18
|
-
|
19
|
-
|
37
|
+
class JsonDecodeResponse < Base
|
38
|
+
|
39
|
+
def request(method, path, body = nil)
|
40
|
+
response = super
|
41
|
+
if response.body.present?
|
42
|
+
Adapter::Response.new(
|
43
|
+
response.status,
|
44
|
+
response.headers,
|
45
|
+
Elastictastic.json_decode(response.body)
|
46
|
+
)
|
47
|
+
else
|
48
|
+
response
|
20
49
|
end
|
21
50
|
end
|
51
|
+
|
22
52
|
end
|
23
53
|
|
24
|
-
class RaiseServerErrors <
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
54
|
+
class RaiseServerErrors < Base
|
55
|
+
|
56
|
+
def request(method, path, body = nil)
|
57
|
+
super.tap do |response|
|
58
|
+
if method != :head
|
59
|
+
if response.body.nil?
|
60
|
+
raise Elastictastic::ServerError::ServerError,
|
61
|
+
"No body in ElasticSearch response with status #{env[:status]}"
|
62
|
+
elsif response.body['error']
|
63
|
+
raise_error(response.body['error'], response.body['status'])
|
64
|
+
elsif response.body['_shards'] && response.body['_shards']['failures']
|
65
|
+
raise_error(
|
66
|
+
response.body['_shards']['failures'].first['reason'], response.body['status'])
|
67
|
+
end
|
35
68
|
end
|
36
69
|
end
|
37
70
|
end
|
@@ -39,81 +72,30 @@ module Elastictastic
|
|
39
72
|
private
|
40
73
|
|
41
74
|
def raise_error(server_message, status)
|
42
|
-
|
43
|
-
if match
|
44
|
-
clazz = Elastictastic::ServerError.const_get(match[1])
|
45
|
-
error = clazz.new(match[2])
|
46
|
-
error.status = status
|
47
|
-
Kernel.raise error
|
48
|
-
else
|
49
|
-
Kernel.raise Elastictastic::ServerError::ServerError, server_message
|
50
|
-
end
|
75
|
+
::Kernel.raise(Elastictastic::ServerError[server_message, status])
|
51
76
|
end
|
77
|
+
|
52
78
|
end
|
53
79
|
|
54
|
-
class LogRequests <
|
55
|
-
|
56
|
-
|
80
|
+
class LogRequests < Base
|
81
|
+
|
82
|
+
def initialize(connection, logger)
|
83
|
+
super(connection)
|
57
84
|
@logger = logger
|
58
85
|
end
|
59
86
|
|
60
|
-
def
|
87
|
+
def request(method, path, body = nil)
|
61
88
|
now = Time.now
|
62
|
-
|
63
|
-
@app.call(env).on_complete do
|
64
|
-
method = env[:method].to_s.upcase
|
89
|
+
super.tap do
|
65
90
|
time = ((Time.now - now) * 1000).to_i
|
66
|
-
message = "ElasticSearch #{method} (#{time}ms) #{
|
91
|
+
message = "ElasticSearch #{method.to_s.upcase} (#{time}ms) #{path}"
|
67
92
|
message << ' ' << body if body
|
68
93
|
@logger.debug(message)
|
69
94
|
end
|
70
95
|
end
|
71
|
-
end
|
72
|
-
|
73
|
-
class Rotor < Faraday::Middleware
|
74
|
-
def initialize(app, *hosts)
|
75
|
-
first = nil
|
76
|
-
hosts.each do |host|
|
77
|
-
node = Node.new(app, host)
|
78
|
-
first ||= node
|
79
|
-
@head.next = node if @head
|
80
|
-
@head = node
|
81
|
-
end
|
82
|
-
@head.next = first
|
83
|
-
end
|
84
|
-
|
85
|
-
def call(env)
|
86
|
-
last = @head
|
87
|
-
begin
|
88
|
-
@head = @head.next
|
89
|
-
@head.call(env)
|
90
|
-
rescue Faraday::Error::ConnectionFailed => e
|
91
|
-
raise NoServerAvailable if @head == last
|
92
|
-
retry
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
class Node < Faraday::Middleware
|
97
|
-
attr_accessor :next
|
98
96
|
|
99
|
-
def initialize(app, url)
|
100
|
-
super(app)
|
101
|
-
# Create a connection instance so we can use its #build_url method.
|
102
|
-
# Kinda lame -- seems like it would make more sense for Faraday to
|
103
|
-
# just implement a middleware for injecting a host/path prefix.
|
104
|
-
@connection = Faraday::Connection.new(:url => url)
|
105
|
-
end
|
106
|
-
|
107
|
-
def call(env)
|
108
|
-
original_url = env[:url]
|
109
|
-
begin
|
110
|
-
env[:url] = @connection.build_url(original_url)
|
111
|
-
@app.call(env)
|
112
|
-
ensure
|
113
|
-
env[:url] = original_url
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
97
|
end
|
98
|
+
|
118
99
|
end
|
100
|
+
|
119
101
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Elastictastic
|
2
|
+
|
3
|
+
class MultiGet
|
4
|
+
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@docspecs = []
|
9
|
+
@scopes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(scope, *ids)
|
13
|
+
scope = scope.all
|
14
|
+
params = scope.multi_get_params
|
15
|
+
ids.flatten.each do |id|
|
16
|
+
@docspecs << params.merge('_id' => id.to_s)
|
17
|
+
@scopes << scope
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
return if @docspecs.empty?
|
23
|
+
Elastictastic.client.mget(@docspecs)['docs'].zip(@scopes) do |hit, scope|
|
24
|
+
yield scope.materialize_hit(hit) if hit['exists']
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Elastictastic
|
4
|
+
class MultiSearch
|
5
|
+
Component = Struct.new(:scope, :search_type)
|
6
|
+
|
7
|
+
def self.query(*scopes)
|
8
|
+
new.query(*scopes).run
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.count(*scopes)
|
12
|
+
new.count(*scopes).run
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@components = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def query(*scopes)
|
20
|
+
components = validate_scopes_for_query(scopes.flatten).map do |scope|
|
21
|
+
Component.new(scope, 'query_then_fetch')
|
22
|
+
end
|
23
|
+
@components.concat(components)
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def count(*scopes)
|
28
|
+
components = scopes.flatten.map { |scope| Component.new(scope, 'count') }
|
29
|
+
@components.concat(components)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def run
|
34
|
+
if @components.any?
|
35
|
+
responses = Elastictastic.client.msearch(search_bodies)['responses']
|
36
|
+
responses.zip(@components) do |response, component|
|
37
|
+
raise ServerError[response['error']] if response['error']
|
38
|
+
scope, search_type = component.scope, component.search_type
|
39
|
+
case search_type
|
40
|
+
when 'query_then_fetch' then scope.response = response
|
41
|
+
when 'count' then scope.counts = response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def search_bodies
|
51
|
+
StringIO.new.tap do |io|
|
52
|
+
@components.each do |component|
|
53
|
+
scope, search_type = component.scope, component.search_type
|
54
|
+
headers = scope.multi_search_headers.
|
55
|
+
merge('search_type' => search_type)
|
56
|
+
io.puts(Elastictastic.json_encode(headers))
|
57
|
+
io.puts(Elastictastic.json_encode(scope.params))
|
58
|
+
end
|
59
|
+
end.string
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate_scopes_for_query(scopes)
|
63
|
+
scopes.each do |scope|
|
64
|
+
if scope.params['size'].blank?
|
65
|
+
raise ArgumentError, "Multi-search scopes must have an explicit size"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -1,29 +1,5 @@
|
|
1
|
-
|
2
|
-
module NestedDocument
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
include Properties
|
7
|
-
include Dirty
|
8
|
-
include Dirty::NestedDocumentMethods
|
9
|
-
include MassAssignmentSecurity
|
10
|
-
include Validations
|
11
|
-
end
|
1
|
+
warn 'Elastictastic::NestedDocument is deprecated. Use Elastictastic::EmbeddedDocument instead.'
|
12
2
|
|
13
|
-
|
14
|
-
|
15
|
-
self.write_attributes(original.read_attributes.dup)
|
16
|
-
end
|
17
|
-
|
18
|
-
def inspect
|
19
|
-
inspected = "#<#{self.class.name}"
|
20
|
-
if attributes.any?
|
21
|
-
inspected << ' ' << attributes.each_pair.map do |attr, value|
|
22
|
-
"#{attr}: #{value.inspect}"
|
23
|
-
end.join(', ')
|
24
|
-
end
|
25
|
-
inspected << '>'
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
3
|
+
module Elastictastic
|
4
|
+
NestedDocument = EmbeddedDocument
|
29
5
|
end
|
@@ -11,14 +11,14 @@ module Elastictastic
|
|
11
11
|
included do
|
12
12
|
include NewRelic::Agent::MethodTracer
|
13
13
|
|
14
|
-
add_method_tracer :create, 'ElasticSearch
|
15
|
-
add_method_tracer :delete, 'ElasticSearch
|
16
|
-
add_method_tracer :get, 'ElasticSearch
|
17
|
-
add_method_tracer :mget, 'ElasticSearch/mget'
|
18
|
-
add_method_tracer :put_mapping, 'ElasticSearch
|
19
|
-
add_method_tracer :scroll, 'ElasticSearch/scroll'
|
20
|
-
add_method_tracer :search, 'ElasticSearch
|
21
|
-
add_method_tracer :update, 'ElasticSearch
|
14
|
+
add_method_tracer :create, 'Database/ElasticSearch/create'
|
15
|
+
add_method_tracer :delete, 'Database/ElasticSearch/delete'
|
16
|
+
add_method_tracer :get, 'Database/ElasticSearch/get'
|
17
|
+
add_method_tracer :mget, 'Database/ElasticSearch/mget'
|
18
|
+
add_method_tracer :put_mapping, 'Database/ElasticSearch/put_mapping'
|
19
|
+
add_method_tracer :scroll, 'Database/ElasticSearch/scroll'
|
20
|
+
add_method_tracer :search, 'Database/ElasticSearch/search'
|
21
|
+
add_method_tracer :update, 'Database/ElasticSearch/update'
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -7,15 +7,17 @@ module Elastictastic
|
|
7
7
|
include ActiveModel::Observing
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
Callbacks::HOOKS.each do |method|
|
11
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
12
|
+
def #{method}(options = {})
|
13
|
+
if options[:observers] == false
|
14
|
+
super
|
15
|
+
else
|
14
16
|
notify_observers(:before_#{method})
|
15
17
|
super.tap { notify_observers(:after_#{method}) }
|
16
18
|
end
|
17
|
-
|
18
|
-
|
19
|
+
end
|
20
|
+
RUBY
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Elastictastic
|
2
|
+
|
3
|
+
module OptimisticLocking
|
4
|
+
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def create_or_update(id, &block)
|
10
|
+
scope = current_scope
|
11
|
+
new.tap do |instance|
|
12
|
+
instance.id = id
|
13
|
+
yield instance
|
14
|
+
end.create do |e|
|
15
|
+
case e
|
16
|
+
when nil # chill
|
17
|
+
when Elastictastic::ServerError::DocumentAlreadyExistsEngineException,
|
18
|
+
Elastictastic::ServerError::DocumentAlreadyExistsException # 0.19+
|
19
|
+
scope.update(id, &block)
|
20
|
+
else
|
21
|
+
raise e
|
22
|
+
end
|
23
|
+
end
|
24
|
+
rescue Elastictastic::CancelSave
|
25
|
+
# Do Nothing
|
26
|
+
end
|
27
|
+
|
28
|
+
def update(id, &block)
|
29
|
+
instance = scoped({}).find_one(id, :preference => '_primary_first')
|
30
|
+
instance.try_update(current_scope, &block) if instance
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_each(&block)
|
34
|
+
all.each { |instance| instance.try_update(current_scope, &block) }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
def try_update(scope, &block) #:nodoc:
|
40
|
+
yield self
|
41
|
+
update do |e|
|
42
|
+
case e
|
43
|
+
when nil # chill
|
44
|
+
when Elastictastic::ServerError::VersionConflictEngineException,
|
45
|
+
Elastictastic::ServerError::VersionConflictException # 0.19
|
46
|
+
scope.update(id, &block)
|
47
|
+
else
|
48
|
+
raise e
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rescue Elastictastic::CancelSave
|
52
|
+
# Do Nothing
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -31,7 +31,7 @@ module Elastictastic
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def child_associations
|
34
|
-
@
|
34
|
+
@_child_associations ||= {}
|
35
35
|
end
|
36
36
|
|
37
37
|
def mapping
|
@@ -41,75 +41,77 @@ module Elastictastic
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
module InstanceMethods
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def elasticsearch_doc=(doc)
|
57
|
-
@parent_id = doc.delete('_parent')
|
58
|
-
super
|
45
|
+
def initialize(attributes = {})
|
46
|
+
super
|
47
|
+
@_children = Hash.new do |hash, child_association_name|
|
48
|
+
hash[child_association_name] = Elastictastic::ChildCollectionProxy.new(
|
49
|
+
self.class.child_association(child_association_name.to_s),
|
50
|
+
self
|
51
|
+
)
|
59
52
|
end
|
53
|
+
end
|
60
54
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
self.class.parent_association.clazz.find(@parent_id)
|
66
|
-
end
|
67
|
-
end
|
55
|
+
def elasticsearch_doc=(doc)
|
56
|
+
@_parent_id = doc.delete('_parent')
|
57
|
+
super
|
58
|
+
end
|
68
59
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
@
|
60
|
+
def _parent #:nodoc:
|
61
|
+
return @_parent if defined? @_parent
|
62
|
+
@_parent =
|
63
|
+
if @_parent_id
|
64
|
+
self.class.parent_association.clazz.in_index(index).find(@_parent_id)
|
74
65
|
end
|
66
|
+
#TODO - here's a piece of debugging to fix a problem where we get weird parents. remove after fixing
|
67
|
+
if @_parent && !@_parent.respond_to?(:id)
|
68
|
+
raise ArgumentError.new("Bad parent loaded from id #{@_parent_id} is a #{@_parent.class.name}.")
|
75
69
|
end
|
70
|
+
@_parent
|
71
|
+
end
|
76
72
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
73
|
+
def _parent_id #:nodoc:
|
74
|
+
if @_parent_id
|
75
|
+
@_parent_id
|
76
|
+
elsif @_parent
|
77
|
+
unless @_parent.respond_to?(:id)
|
78
|
+
raise ArgumentError,
|
79
|
+
"@_parent is incorrectly set to #{Object.instance_method(:inspect).bind(@_parent).call}"
|
81
80
|
end
|
82
|
-
|
83
|
-
raise Elastictastic::IllegalModificationError,
|
84
|
-
"Can't change parent of persisted object"
|
85
|
-
end
|
86
|
-
@parent_collection = parent_collection
|
87
|
-
@parent = parent_collection.parent
|
81
|
+
@_parent_id = @_parent.id
|
88
82
|
end
|
83
|
+
end
|
89
84
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
85
|
+
def parent=(parent)
|
86
|
+
if @_parent
|
87
|
+
raise Elastictastic::IllegalModificationError,
|
88
|
+
"Document is already a child of #{_parent}"
|
89
|
+
end
|
90
|
+
if persisted?
|
91
|
+
raise Elastictastic::IllegalModificationError,
|
92
|
+
"Can't change parent of persisted object"
|
97
93
|
end
|
94
|
+
#TODO - here's a piece of debugging to fix a problem where we get weird parents. remove after fixing
|
95
|
+
if parent && !parent.respond_to?(:id)
|
96
|
+
raise ArgumentError.new("Bad parent loaded from id #{parent_id} is a #{parent.class.name}.")
|
97
|
+
end
|
98
|
+
@_parent = parent
|
99
|
+
end
|
98
100
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
def save(options = {})
|
102
|
+
super
|
103
|
+
self.class.child_associations.each_pair do |name, association|
|
104
|
+
association.extract(self).transient_children.each do |child|
|
105
|
+
child.save unless child.pending_save?
|
104
106
|
end
|
105
107
|
end
|
108
|
+
end
|
106
109
|
|
107
|
-
|
108
|
-
|
109
|
-
def read_child(field_name)
|
110
|
-
@children[field_name.to_s]
|
111
|
-
end
|
110
|
+
protected
|
112
111
|
|
112
|
+
def read_child(field_name)
|
113
|
+
@_children[field_name.to_s]
|
113
114
|
end
|
115
|
+
|
114
116
|
end
|
115
117
|
end
|
@@ -1,19 +1,19 @@
|
|
1
1
|
module Elastictastic
|
2
2
|
module Persistence
|
3
|
-
def save
|
4
|
-
persisted? ? update : create
|
3
|
+
def save(options = {}, &block)
|
4
|
+
persisted? ? update(options, &block) : create(options, &block)
|
5
5
|
end
|
6
6
|
|
7
|
-
def destroy
|
7
|
+
def destroy(options = {}, &block)
|
8
8
|
if persisted?
|
9
|
-
Elastictastic.persister.destroy(self)
|
9
|
+
Elastictastic.persister.destroy(self, &block)
|
10
10
|
else
|
11
11
|
raise OperationNotAllowed, "Cannot destroy transient document: #{inspect}"
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
def persisted?
|
16
|
-
!!@
|
16
|
+
!!@_persisted
|
17
17
|
end
|
18
18
|
|
19
19
|
def transient?
|
@@ -21,38 +21,38 @@ module Elastictastic
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def pending_save?
|
24
|
-
!!@
|
24
|
+
!!@_pending_save
|
25
25
|
end
|
26
26
|
|
27
27
|
def pending_destroy?
|
28
|
-
!!@
|
28
|
+
!!@_pending_destroy
|
29
29
|
end
|
30
30
|
|
31
31
|
def persisted!
|
32
|
-
@
|
33
|
-
@
|
32
|
+
@_persisted = true
|
33
|
+
@_pending_save = false
|
34
34
|
end
|
35
35
|
|
36
36
|
def transient!
|
37
|
-
@
|
37
|
+
@_persisted = @_pending_destroy = false
|
38
38
|
end
|
39
39
|
|
40
40
|
def pending_save!
|
41
|
-
@
|
41
|
+
@_pending_save = true
|
42
42
|
end
|
43
43
|
|
44
44
|
def pending_destroy!
|
45
|
-
@
|
45
|
+
@_pending_destroy = true
|
46
46
|
end
|
47
47
|
|
48
48
|
protected
|
49
49
|
|
50
|
-
def create
|
51
|
-
Elastictastic.persister.create(self)
|
50
|
+
def create(options = {}, &block)
|
51
|
+
Elastictastic.persister.create(self, &block)
|
52
52
|
end
|
53
53
|
|
54
|
-
def update
|
55
|
-
Elastictastic.persister.update(self)
|
54
|
+
def update(options = {}, &block)
|
55
|
+
Elastictastic.persister.update(self, &block)
|
56
56
|
end
|
57
57
|
|
58
58
|
private
|