opium 1.1.8 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/lib/opium/extensions/pointer.rb +1 -0
- data/lib/opium/model.rb +2 -0
- data/lib/opium/model/attributable.rb +6 -2
- data/lib/opium/model/batchable.rb +83 -0
- data/lib/opium/model/batchable/batch.rb +1 -2
- data/lib/opium/model/connectable.rb +1 -1
- data/lib/opium/model/criteria.rb +4 -4
- data/lib/opium/model/field.rb +12 -2
- data/lib/opium/model/fieldable.rb +12 -2
- data/lib/opium/model/persistable.rb +1 -1
- data/lib/opium/model/reference.rb +50 -0
- data/lib/opium/model/relatable.rb +64 -0
- data/lib/opium/model/relatable/metadata.rb +32 -0
- data/lib/opium/model/relation.rb +156 -0
- data/lib/opium/version.rb +1 -1
- data/spec/opium/model/attributable_spec.rb +0 -9
- data/spec/opium/model/batchable/batch_spec.rb +4 -1
- data/spec/opium/model/batchable_spec.rb +148 -0
- data/spec/opium/model/fieldable_spec.rb +16 -0
- data/spec/opium/model/reference_spec.rb +73 -0
- data/spec/opium/model/relatable/metadata_spec.rb +31 -0
- data/spec/opium/model/relatable_spec.rb +220 -0
- data/spec/opium/model/relation_spec.rb +275 -0
- data/spec/opium/model_spec.rb +1 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bbf318b07ca2a59bb917e4495abda45117aa588
|
4
|
+
data.tar.gz: c85609101238b4c6142df1d421a90e33e9fbf38a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 661189662f2ac5cdeb1b0dcb87d32ca291bdb4e888a108e7669153758160ced3f4aa06e961e625544e87c4e7d66a514e588607f0338f81793ee7029ee5266093
|
7
|
+
data.tar.gz: 76ef297dfd71c71a063140c88f11b48bfb6326b6ca31d333155ac9d6cdbb0917c181d337a7e67adb2038af6261cf4387644ed98211208e932d2e25d9346a3b47
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,7 @@ module Opium
|
|
2
2
|
class Pointer
|
3
3
|
def initialize( attributes = {} )
|
4
4
|
self.class_name = attributes[:class_name] || attributes[:model_name] || (attributes[:class] || attributes[:model]).model_name
|
5
|
+
self.class_name = self.class_name.name if self.class_name.respond_to?(:name)
|
5
6
|
self.id = attributes[:id]
|
6
7
|
end
|
7
8
|
|
data/lib/opium/model.rb
CHANGED
@@ -17,6 +17,7 @@ require 'opium/model/scopable'
|
|
17
17
|
require 'opium/model/findable'
|
18
18
|
require 'opium/model/inheritable'
|
19
19
|
require 'opium/model/batchable'
|
20
|
+
require 'opium/model/relatable'
|
20
21
|
require 'opium/model/kaminari'
|
21
22
|
|
22
23
|
module Opium
|
@@ -38,6 +39,7 @@ module Opium
|
|
38
39
|
include Findable
|
39
40
|
include Inheritable
|
40
41
|
include Batchable
|
42
|
+
include Relatable
|
41
43
|
end
|
42
44
|
|
43
45
|
def initialize( attributes = {} )
|
@@ -7,8 +7,12 @@ module Opium
|
|
7
7
|
include ActiveModel::ForbiddenAttributesProtection
|
8
8
|
end
|
9
9
|
|
10
|
+
def initialize( attributes = {} )
|
11
|
+
super( self.class.default_attributes( self ).merge attributes )
|
12
|
+
end
|
13
|
+
|
10
14
|
def attributes
|
11
|
-
@attributes ||=
|
15
|
+
@attributes ||= {}.with_indifferent_access
|
12
16
|
end
|
13
17
|
|
14
18
|
def attributes=(values)
|
@@ -23,7 +27,7 @@ module Opium
|
|
23
27
|
end
|
24
28
|
|
25
29
|
def attributes_to_parse( options = {} )
|
26
|
-
options[:except] ||= self.class.fields.values.select {|f| f.readonly? }.map {|f| f.name} if options[:not_readonly]
|
30
|
+
options[:except] ||= self.class.fields.values.select {|f| f.readonly? || f.virtual? }.map {|f| f.name} if options[:not_readonly]
|
27
31
|
Hash[*self.as_json( options ).flat_map {|k, v| [self.class.fields[k].name_to_parse, self.class.fields[k].type.to_parse(v)]}]
|
28
32
|
end
|
29
33
|
|
@@ -5,6 +5,89 @@ module Opium
|
|
5
5
|
module Model
|
6
6
|
module Batchable
|
7
7
|
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
require 'fiber'
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def batch( options = {} )
|
13
|
+
raise ArgumentError, 'no block given' unless block_given?
|
14
|
+
create_batch
|
15
|
+
fiber = Fiber.new { yield }
|
16
|
+
subfibers = []
|
17
|
+
subfibers << fiber.resume while fiber.alive?
|
18
|
+
ensure
|
19
|
+
delete_batch
|
20
|
+
end
|
21
|
+
|
22
|
+
def batched?
|
23
|
+
batch_pool[Thread.current].present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_batch
|
27
|
+
batch = current_batch_job
|
28
|
+
if batch
|
29
|
+
batch.dive && batch
|
30
|
+
else
|
31
|
+
self.current_batch_job = Batch.new
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_batch
|
36
|
+
batch = current_batch_job
|
37
|
+
fail 'No current batch job!' unless batch
|
38
|
+
if batch.depth == 0
|
39
|
+
self.current_batch_job = nil
|
40
|
+
else
|
41
|
+
batch.execute
|
42
|
+
batch
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def current_batch_job
|
47
|
+
batch_pool[Thread.current]
|
48
|
+
end
|
49
|
+
|
50
|
+
def current_batch_job=( value )
|
51
|
+
batch_pool[Thread.current] = value
|
52
|
+
end
|
53
|
+
|
54
|
+
def http_post( data, options = {} )
|
55
|
+
if batched?
|
56
|
+
current_batch_job.enqueue( method: :post, path: resource_name, body: data )
|
57
|
+
Fiber.yield
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def http_put( id, data, options = {} )
|
64
|
+
if batched?
|
65
|
+
current_batch_job.enqueue( method: :put, path: resource_name( id ), body: data )
|
66
|
+
Fiber.yield
|
67
|
+
else
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def http_delete( id, options = {} )
|
73
|
+
if batched?
|
74
|
+
current_batch_job.enqueue( method: :delete, path: resource_name( id ) )
|
75
|
+
Fiber.yield
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def batch_pool
|
84
|
+
@batch_pool ||= {}
|
85
|
+
end
|
86
|
+
|
87
|
+
def thread_local_id
|
88
|
+
@thread_local_id ||= :"#{ Module.nesting.first.name.parameterize('_') }_current_batch_job"
|
89
|
+
end
|
90
|
+
end
|
8
91
|
end
|
9
92
|
end
|
10
93
|
end
|
@@ -23,6 +23,7 @@ module Opium
|
|
23
23
|
@@connection ||= Faraday.new( url: 'https://api.parse.com/1/' ) do |faraday|
|
24
24
|
faraday.request :multipart
|
25
25
|
faraday.request :url_encoded
|
26
|
+
faraday.request :json
|
26
27
|
faraday.response :logger if Opium.config.log_network_responses
|
27
28
|
faraday.response :json, content_type: /\bjson$/
|
28
29
|
faraday.headers[:x_parse_application_id] = Opium.config.app_id
|
@@ -125,7 +126,6 @@ module Opium
|
|
125
126
|
def infuse_request_with( data )
|
126
127
|
lambda do |request|
|
127
128
|
request.body = data
|
128
|
-
request.body = request.body.to_json unless request.body.is_a?(String)
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
data/lib/opium/model/criteria.rb
CHANGED
@@ -23,7 +23,7 @@ module Opium
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def chain
|
26
|
-
Marshal.load( Marshal.dump( self ) )
|
26
|
+
Marshal.load( Marshal.dump( self ) ).tap {|m| m.instance_variable_set( :@cache, nil )}
|
27
27
|
end
|
28
28
|
|
29
29
|
def constraints
|
@@ -81,9 +81,9 @@ module Opium
|
|
81
81
|
if response && response['results']
|
82
82
|
variables[:total_count] = response['count']
|
83
83
|
response['results'].each do |attributes|
|
84
|
-
|
85
|
-
@cache <<
|
86
|
-
block.call
|
84
|
+
instance = self.model.new( attributes )
|
85
|
+
@cache << instance if cached?
|
86
|
+
block.call instance
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
data/lib/opium/model/field.rb
CHANGED
@@ -7,9 +7,11 @@ module Opium
|
|
7
7
|
|
8
8
|
attr_reader :name, :type, :readonly, :as
|
9
9
|
|
10
|
-
def default
|
10
|
+
def default( context = nil )
|
11
11
|
if @default.respond_to? :call
|
12
|
-
|
12
|
+
params = []
|
13
|
+
params.push( context ) if @default.arity != 0
|
14
|
+
@default.call( *params )
|
13
15
|
else
|
14
16
|
@default
|
15
17
|
end
|
@@ -19,6 +21,14 @@ module Opium
|
|
19
21
|
self.readonly == true
|
20
22
|
end
|
21
23
|
|
24
|
+
def relation?
|
25
|
+
self.type == Relation
|
26
|
+
end
|
27
|
+
|
28
|
+
def virtual?
|
29
|
+
relation? || self.type == Reference
|
30
|
+
end
|
31
|
+
|
22
32
|
def name_to_parse
|
23
33
|
@name_to_parse ||= (self.as || self.name).to_s.camelize(:lower)
|
24
34
|
end
|
@@ -28,6 +28,10 @@ module Opium
|
|
28
28
|
define_method("#{name}=") do |value|
|
29
29
|
converted = self.class.fields[name].type.to_ruby(value)
|
30
30
|
send( "#{name}_will_change!" ) unless self.attributes[name] == converted
|
31
|
+
if self.class.fields[name].relation?
|
32
|
+
converted.owner ||= self
|
33
|
+
converted.metadata ||= self.class.relations[name]
|
34
|
+
end
|
31
35
|
self.attributes[name] = converted
|
32
36
|
end
|
33
37
|
send(:private, "#{name}=") if options[:readonly]
|
@@ -36,6 +40,12 @@ module Opium
|
|
36
40
|
fields[name]
|
37
41
|
end
|
38
42
|
|
43
|
+
def has_field?( field_name )
|
44
|
+
fields.key? field_name
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_method :field?, :has_field?
|
48
|
+
|
39
49
|
def fields
|
40
50
|
@fields ||= ActiveSupport::HashWithIndifferentAccess.new
|
41
51
|
end
|
@@ -48,8 +58,8 @@ module Opium
|
|
48
58
|
@parse_canonical_field_names ||= ActiveSupport::HashWithIndifferentAccess.new
|
49
59
|
end
|
50
60
|
|
51
|
-
def default_attributes
|
52
|
-
fields.transform_values {|field| field.type.to_ruby field.default}.with_indifferent_access
|
61
|
+
def default_attributes( context = nil )
|
62
|
+
fields.transform_values {|field| field.type.to_ruby field.default( context ) }.with_indifferent_access
|
53
63
|
end
|
54
64
|
end
|
55
65
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
class Reference < SimpleDelegator
|
4
|
+
class << self
|
5
|
+
def to_ruby( value )
|
6
|
+
case value
|
7
|
+
when Hash
|
8
|
+
new( value[:metadata] || value['metadata'], value[:context] || value['context'] )
|
9
|
+
when self
|
10
|
+
value
|
11
|
+
else
|
12
|
+
fail ArgumentError, "could not convert #{ value.inspect } into an Opium::Model::Reference"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize( metadata, context )
|
18
|
+
self.metadata = metadata
|
19
|
+
self.context = context
|
20
|
+
fail ArgumentError, 'did not receive a context object!' unless context
|
21
|
+
super( nil )
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :metadata, :context
|
25
|
+
|
26
|
+
def __getobj__
|
27
|
+
@reference || __setobj__( lookup_reference )
|
28
|
+
end
|
29
|
+
|
30
|
+
def __setobj__( obj )
|
31
|
+
@reference = obj
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
if @reference
|
36
|
+
@reference.inspect
|
37
|
+
else
|
38
|
+
"#<#{ self.class.name }<#{ self.metadata.target_class_name }>>"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def lookup_reference
|
45
|
+
return nil if context.new_record?
|
46
|
+
self.metadata.target_class_name.constantize.where( self.metadata.inverse_relation_name => self.context ).first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'opium/model/relation'
|
2
|
+
require 'opium/model/reference'
|
3
|
+
require 'opium/model/relatable/metadata'
|
4
|
+
|
5
|
+
module Opium
|
6
|
+
module Model
|
7
|
+
module Relatable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
after_create :update_relations
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def relations
|
16
|
+
@relations ||= {}.with_indifferent_access
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_and_belongs_to_many( relation_name, options = {} )
|
20
|
+
create_relation_metadata_from( :has_and_belongs_to_many, relation_name, options )
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_many( relation_name, options = {} )
|
24
|
+
create_relation_metadata_from( :has_many, relation_name, options )
|
25
|
+
field relation_name, type: Relation, default: -> { relations[relation_name].target_class_name }
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_one( relation_name, options = {} )
|
29
|
+
create_relation_metadata_from( :has_one, relation_name, options )
|
30
|
+
end
|
31
|
+
|
32
|
+
def belongs_to( relation_name, options = {} )
|
33
|
+
create_relation_metadata_from( :belongs_to, relation_name, options )
|
34
|
+
field relation_name, type: Reference,
|
35
|
+
default: ->( model ) do
|
36
|
+
{ metadata: relations[relation_name], context: model }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def create_relation_metadata_from( relation_type, relation_name, options )
|
43
|
+
relations[relation_name] = Metadata.new( self, relation_type, relation_name, options )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def save( options = {} )
|
48
|
+
super && relations.all? {|_, relation| relation.save}
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def relations
|
54
|
+
attributes.select {|_, value| value.is_a? Relation}
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_relations
|
58
|
+
send(:relations).each do |_, value|
|
59
|
+
value.owner = self
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
module Relatable
|
4
|
+
class Metadata
|
5
|
+
attr_reader :relation_name, :inverse_relation_name, :target_class_name, :relation_type, :inverse_relation_type, :owning_class_name
|
6
|
+
|
7
|
+
def initialize( klass, relation_type, relation_name, options = {} )
|
8
|
+
self.owning_class_name = klass.model_name
|
9
|
+
self.relation_type = relation_type
|
10
|
+
self.relation_name = relation_name
|
11
|
+
self.target_class_name = (options[:class_name] || relation_name).to_s.classify
|
12
|
+
self.inverse_relation_name = (options[:inverse_of] || determine_inverse_relation_name).to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_writer :relation_name, :inverse_relation_name, :target_class_name, :relation_type, :inverse_relation_type, :owning_class_name
|
18
|
+
|
19
|
+
def determine_inverse_relation_name
|
20
|
+
method =
|
21
|
+
case relation_type
|
22
|
+
when :belongs_to
|
23
|
+
:plural
|
24
|
+
else
|
25
|
+
:singular
|
26
|
+
end
|
27
|
+
owning_class_name.send( method )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
module Opium
|
2
|
+
module Model
|
3
|
+
class Relation < Criteria
|
4
|
+
include ActiveModel::Dirty
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def to_parse( object )
|
8
|
+
class_name =
|
9
|
+
case object
|
10
|
+
when Hash
|
11
|
+
fetch_hash_key_from( object, 'class_name' ) || fetch_hash_key_from( object, 'model_name' )
|
12
|
+
when String, Symbol
|
13
|
+
object
|
14
|
+
when is_descendant.curry[Opium::Model]
|
15
|
+
object.model_name
|
16
|
+
when self
|
17
|
+
object.class_name
|
18
|
+
else
|
19
|
+
fail ArgumentError, "could not convert #{ object.inspect } to a parse Relation hash"
|
20
|
+
end
|
21
|
+
fail ArgumentError, "could not determine class_name from #{ object.inspect }" unless class_name
|
22
|
+
{ __type: 'Relation', className: class_name }.with_indifferent_access
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_ruby( object )
|
26
|
+
return if object.nil?
|
27
|
+
return object if object.is_a? self
|
28
|
+
class_name =
|
29
|
+
case object
|
30
|
+
when Hash
|
31
|
+
fetch_hash_key_from( object, 'class_name' ) || fetch_hash_key_from( object, 'model_name' )
|
32
|
+
when String, Symbol
|
33
|
+
object
|
34
|
+
when is_descendant.curry[Opium::Model]
|
35
|
+
object.model_name
|
36
|
+
else
|
37
|
+
fail ArgumentError, "could not convert #{ object.inspect } to a Opium::Model::Relation"
|
38
|
+
end
|
39
|
+
new( class_name )
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def is_descendant
|
45
|
+
@is_descendant ||= ->( expected_type, object ) { ( object.is_a?( Class ) ? object : object.class ) <= expected_type }
|
46
|
+
end
|
47
|
+
|
48
|
+
def fetch_hash_key_from( hash, key )
|
49
|
+
snake_case_key = key.to_s.underscore
|
50
|
+
lower_camel_key = key.to_s.camelcase(:lower)
|
51
|
+
|
52
|
+
hash[ snake_case_key ] || hash[ snake_case_key.to_sym ] || hash[ lower_camel_key ] || hash[ lower_camel_key.to_sym ]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize( model_name )
|
57
|
+
super
|
58
|
+
update_variable!( :cache, true )
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_parse
|
62
|
+
self.class.to_parse self
|
63
|
+
end
|
64
|
+
|
65
|
+
def empty?
|
66
|
+
owner.nil? || owner.new_record? ? true : super
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_reader :owner, :metadata
|
70
|
+
|
71
|
+
def owner=(value)
|
72
|
+
@owner = value
|
73
|
+
update_constraint!( :where, '$relatedTo' => { 'object' => value.to_parse } )
|
74
|
+
end
|
75
|
+
|
76
|
+
def metadata=(value)
|
77
|
+
@metadata = value
|
78
|
+
update_constraint!( :where, '$relatedTo' => { 'key' => value.relation_name.to_s } )
|
79
|
+
end
|
80
|
+
|
81
|
+
alias_method :class_name, :model_name
|
82
|
+
|
83
|
+
#TODO: likely will need to reimplement .each
|
84
|
+
|
85
|
+
def each(&block)
|
86
|
+
if !block_given?
|
87
|
+
to_enum(:each)
|
88
|
+
else
|
89
|
+
super() {|model| block.call( model ) unless __deletions__.include?( model ) }
|
90
|
+
(__additions__ - __deletions__).each(&block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def push( object )
|
95
|
+
__additions__.push( object )
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
alias_method :<<, :push
|
100
|
+
|
101
|
+
def delete( object )
|
102
|
+
__deletions__.push( object )
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
def build( params = {} )
|
107
|
+
model.new( params || {} ).tap do |instance|
|
108
|
+
push instance
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
alias_method :new, :build
|
113
|
+
|
114
|
+
def save
|
115
|
+
self.reject {|model| model.persisted?}.each(&:save)
|
116
|
+
__apply_additions__
|
117
|
+
__apply_deletions__
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_response
|
122
|
+
@parse_response ||= []
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def __relation_deltas__
|
128
|
+
@__relation_deltas__ ||= {}
|
129
|
+
end
|
130
|
+
|
131
|
+
def __additions__
|
132
|
+
__relation_deltas__[:additions] ||= []
|
133
|
+
end
|
134
|
+
|
135
|
+
def __deletions__
|
136
|
+
__relation_deltas__[:deletions] ||= []
|
137
|
+
end
|
138
|
+
|
139
|
+
def __apply_additions__
|
140
|
+
unless __additions__.empty?
|
141
|
+
parse_response << owner.class.http_put( owner.id, { metadata.relation_name => { __op: 'AddRelation', objects: __additions__.map(&:to_parse) } } )
|
142
|
+
@cache.concat( __additions__ )
|
143
|
+
__additions__.clear
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def __apply_deletions__
|
148
|
+
unless __deletions__.empty?
|
149
|
+
parse_response << owner.class.http_put( owner.id, { metadata.relation_name => { __op: 'RemoveRelation', objects: __deletions__.map(&:to_parse) } } )
|
150
|
+
@cache = @cache - __deletions__
|
151
|
+
__deletions__.clear
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|