active_remote 2.0.2 → 2.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8da0d6b9a19c100a7c36a11ef650e1d788f4b493
4
- data.tar.gz: 1da175673f70cbd2eed8686fb27447d19c51b222
3
+ metadata.gz: 0dd96c99847eae8cf17cec819377a356b0c9fe51
4
+ data.tar.gz: 97353c96fb186aaa0c509b882bb860389656c2c0
5
5
  SHA512:
6
- metadata.gz: 864b2fa737f45a1a8988b40aaace637c8ded669f4012f0a49d892fb325cbf42cb1acfbef159d3b09443de7328082b342f950ea37abc7f375d2a83ede46a5b77f
7
- data.tar.gz: 7120238fbe22ca117d738b76f95625eb78c91e1cb52070d6d2c7a41933ce4c952deaaa61c0968352ae3f645807f559e8ff1a97f1087a660b5da1db2c8bff2e7c
6
+ metadata.gz: ad36c87d5dd76cd1663e82f43b92dde4b9780b60033f2cc59ee9d925ad0a87d67b375ed840755e03791a6c7136c47641d94ef5cf611ab6f3e0cfc82725c872c6
7
+ data.tar.gz: 432cd9f0811511f39d4f82e4ffb074dc0511e244efb2d0575cc3f99f8945f3bc1f8a5e5a45869d72fb13704f6d05bcf0f26c4365010b3d2ca34e427f5ca3d1fb
@@ -9,8 +9,10 @@ require 'active_remote/dirty'
9
9
  require 'active_remote/dsl'
10
10
  require 'active_remote/integration'
11
11
  require 'active_remote/persistence'
12
+ require 'active_remote/primary_key'
12
13
  require 'active_remote/publication'
13
14
  require 'active_remote/rpc'
15
+ require 'active_remote/scope_keys'
14
16
  require 'active_remote/search'
15
17
  require 'active_remote/serialization'
16
18
 
@@ -26,8 +28,10 @@ module ActiveRemote
26
28
  include DSL
27
29
  include Integration
28
30
  include Persistence
31
+ include PrimaryKey
29
32
  include Publication
30
33
  include RPC
34
+ include ScopeKeys
31
35
  include Search
32
36
  include Serialization
33
37
 
@@ -32,9 +32,8 @@ module ActiveRemote
32
32
  # Tag.create_all(Generic::Remote::Tags.new(:records => [ Generic::Remote::Tag.new(:name => 'foo') ])
33
33
  #
34
34
  def create_all(*records)
35
- remote = self.new
36
- remote.execute(:create_all, parse_records(records))
37
- remote.serialize_records
35
+ response = rpc.execute(:create_all, parse_records(records))
36
+ serialize_records(response.records) if response.respond_to?(:records)
38
37
  end
39
38
 
40
39
  # Delete multiple records at the same time. Returns a collection of active
@@ -59,9 +58,8 @@ module ActiveRemote
59
58
  # Tag.delete_all(Generic::Remote::Tags.new(:records => [ Generic::Remote::Tag.new(:guid => 'foo') ])
60
59
  #
61
60
  def delete_all(*records)
62
- remote = self.new
63
- remote.execute(:delete_all, parse_records(records))
64
- remote.serialize_records
61
+ response = rpc.execute(:delete_all, parse_records(records))
62
+ serialize_records(response.records) if response.respond_to?(:records)
65
63
  end
66
64
 
67
65
  # Destroy multiple records at the same time. Returns a collection of active
@@ -86,9 +84,8 @@ module ActiveRemote
86
84
  # Tag.destroy_all(Generic::Remote::Tags.new(:records => [ Generic::Remote::Tag.new(:guid => 'foo') ])
87
85
  #
88
86
  def destroy_all(*records)
89
- remote = self.new
90
- remote.execute(:destroy_all, parse_records(records))
91
- remote.serialize_records
87
+ response = rpc.execute(:destroy_all, parse_records(records))
88
+ serialize_records(response.records) if response.respond_to?(:records)
92
89
  end
93
90
 
94
91
  # Parse given records to get them ready to be built into a request.
@@ -135,9 +132,8 @@ module ActiveRemote
135
132
  # Tag.update_all(Generic::Remote::Tags.new(:records => [ Generic::Remote::Tag.new(:guid => 'foo', :name => 'baz') ])
136
133
  #
137
134
  def update_all(*records)
138
- remote = self.new
139
- remote.execute(:update_all, parse_records(records))
140
- remote.serialize_records
135
+ response = rpc.execute(:update_all, parse_records(records))
136
+ serialize_records(response.records) if response.respond_to?(:records)
141
137
  end
142
138
  end
143
139
  end
@@ -76,7 +76,10 @@ module ActiveRemote
76
76
  #
77
77
  def delete
78
78
  raise ReadOnlyRemoteRecord if readonly?
79
- execute(:delete, "guid" => read_attribute("guid"))
79
+ response = rpc.execute(:delete, scope_key_hash)
80
+
81
+ # TODO: add errors here so success? actually does something...
82
+ # add_errors(response)
80
83
 
81
84
  return success? ? freeze : false
82
85
  end
@@ -99,7 +102,10 @@ module ActiveRemote
99
102
  #
100
103
  def destroy
101
104
  raise ReadOnlyRemoteRecord if readonly?
102
- execute(:destroy, "guid" => read_attribute("guid"))
105
+ response = execute(:destroy, scope_key_hash)
106
+
107
+ # TODO: add errors here so success? actually does something...
108
+ # add_errors(response)
103
109
 
104
110
  return success? ? freeze : false
105
111
  end
@@ -128,6 +134,10 @@ module ActiveRemote
128
134
  assign_attributes(record)
129
135
  end
130
136
 
137
+ # TODO: figure out how to safely run after_find/search callbacks here
138
+ # currently, several functions use this code path, so an alternate path
139
+ # may need to be added
140
+
131
141
  run_callbacks :initialize
132
142
 
133
143
  @new_record = false
@@ -221,12 +231,12 @@ module ActiveRemote
221
231
  run_callbacks :create do
222
232
  # Use the getter here so we get the type casting.
223
233
  new_attributes = attributes
224
- new_attributes.delete("guid")
234
+ new_attributes.delete(primary_key.to_s)
225
235
 
226
- execute(:create, new_attributes)
236
+ response = rpc.execute(:create, new_attributes)
227
237
 
228
- assign_attributes(last_response.to_hash)
229
- add_errors_from_response
238
+ assign_attributes(response.to_hash)
239
+ add_errors(response)
230
240
 
231
241
  @new_record = has_errors?
232
242
  success?
@@ -251,12 +261,12 @@ module ActiveRemote
251
261
  # Use the getter here so we get the type casting.
252
262
  updated_attributes = attributes
253
263
  updated_attributes.slice!(*attribute_names)
254
- updated_attributes.merge!("guid" => self[:guid])
264
+ updated_attributes.merge!(scope_key_hash)
255
265
 
256
- execute(:update, updated_attributes)
266
+ response = rpc.execute(:update, updated_attributes)
257
267
 
258
- assign_attributes(last_response.to_hash)
259
- add_errors_from_response
268
+ assign_attributes(response.to_hash)
269
+ add_errors(response)
260
270
 
261
271
  success?
262
272
  end
@@ -0,0 +1,37 @@
1
+ module ActiveRemote
2
+ module PrimaryKey
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+
7
+ ##
8
+ # The default_primary_key is used to define what attribute is used
9
+ # as a primary key for your global code base. If you use a primary
10
+ # key other than this, you'll want to set it using the primary_key
11
+ # method listed below
12
+ #
13
+ def default_primary_key
14
+ :guid
15
+ end
16
+
17
+ ##
18
+ # In the event that you use a different primary key than the default
19
+ # primary key listed above (like :uuid, or :id), you'll use this method
20
+ # to change that primary key. This will be used when making remote
21
+ # calls to persist or refresh data.
22
+ #
23
+ def primary_key(value = nil)
24
+ @primary_key = value if value
25
+ @primary_key || default_primary_key
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Instance level access to either the default primary key, or whatever
31
+ # you configured the class level primary key to be.
32
+ #
33
+ def primary_key
34
+ self.class.primary_key
35
+ end
36
+ end
37
+ end
@@ -1,3 +1,4 @@
1
+ require 'active_remote/rpc_adapters/protobuf_adapter'
1
2
  require 'active_remote/serializers/protobuf'
2
3
 
3
4
  module ActiveRemote
@@ -5,68 +6,118 @@ module ActiveRemote
5
6
  extend ActiveSupport::Concern
6
7
 
7
8
  included do
8
- include Serializers::Protobuf
9
+ include Embedded
9
10
  end
10
11
 
11
- module ClassMethods
12
+ # TODO: Deprecate this pattern of executing RPC calls
13
+ #
14
+ module Embedded
15
+ extend ActiveSupport::Concern
12
16
 
13
- # Execute an RPC call to the remote service and return the raw response.
14
- #
15
- def remote_call(rpc_method, request_args)
16
- remote = self.new
17
- remote.execute(rpc_method, request_args)
18
- remote.last_response
17
+ included do
18
+ include Serializers::Protobuf
19
19
  end
20
20
 
21
- # Return a protobuf request object for the given rpc request.
22
- #
23
- def request(rpc_method, request_args)
24
- return request_args unless request_args.is_a?(Hash)
21
+ module ClassMethods
22
+ # Return a protobuf request object for the given rpc request.
23
+ #
24
+ # TODO: Add deprecation warning
25
+ #
26
+ def request(rpc_method, request_args)
27
+ return request_args unless request_args.is_a?(Hash)
28
+
29
+ message_class = request_type(rpc_method)
30
+ fields = fields_from_attributes(message_class, request_args)
31
+ message_class.new(fields)
32
+ end
25
33
 
26
- message_class = request_type(rpc_method)
27
- fields = fields_from_attributes(message_class, request_args)
28
- message_class.new(fields)
34
+ # Return the class applicable to the request for the given rpc method.
35
+ #
36
+ # TODO: Add deprecation warning
37
+ #
38
+ def request_type(rpc_method)
39
+ service_class.rpcs[rpc_method].request_type
40
+ end
29
41
  end
30
42
 
31
- # Return the class applicable to the request for the given rpc method.
43
+ # Invoke an RPC call to the service for the given rpc method.
32
44
  #
33
- def request_type(rpc_method)
34
- service_class.rpcs[rpc_method].request_type
35
- end
36
- end
45
+ # TODO: Add deprecation warning
46
+ #
47
+ def execute(rpc_method, request_args)
48
+ @last_request = request(rpc_method, request_args)
37
49
 
38
- # Invoke an RPC call to the service for the given rpc method.
39
- #
40
- def execute(rpc_method, request_args)
41
- @last_request = request(rpc_method, request_args)
50
+ _service_class.client.__send__(rpc_method, @last_request) do |c|
42
51
 
43
- _service_class.client.__send__(rpc_method, @last_request) do |c|
52
+ # In the event of service failure, raise the error.
53
+ c.on_failure do |error|
54
+ raise ActiveRemoteError, error.message
55
+ end
44
56
 
45
- # In the event of service failure, raise the error.
46
- c.on_failure do |error|
47
- raise ActiveRemoteError, error.message
57
+ # In the event of service success, assign the response.
58
+ c.on_success do |response|
59
+ @last_response = response
60
+ end
48
61
  end
49
62
 
50
- # In the event of service success, assign the response.
51
- c.on_success do |response|
52
- @last_response = response
53
- end
63
+ @last_response
54
64
  end
55
- end
56
65
 
57
- # Execute an RPC call to the remote service and return the raw response.
58
- #
59
- def remote_call(rpc_method, request_args)
60
- self.execute(rpc_method, request_args)
61
- self.last_response
66
+
67
+ # Execute an RPC call to the remote service and return the raw response.
68
+ #
69
+ # TODO: Add deprecation warning
70
+ #
71
+ def remote_call(rpc_method, request_args)
72
+ rpc.execute(rpc_method, request_args)
73
+ end
74
+
75
+ private
76
+
77
+ # Return a protobuf request object for the given rpc call.
78
+ #
79
+ # TODO: Add deprecation warning
80
+ #
81
+ def request(rpc_method, attributes)
82
+ self.class.request(rpc_method, attributes)
83
+ end
62
84
  end
63
85
 
64
- private
86
+ module ClassMethods
65
87
 
66
- # Return a protobuf request object for the given rpc call.
67
- #
68
- def request(rpc_method, attributes)
69
- self.class.request(rpc_method, attributes)
88
+ # Execute an RPC call to the remote service and return the raw response.
89
+ #
90
+ def remote_call(rpc_method, request_args)
91
+ rpc.execute(rpc_method, request_args)
92
+ end
93
+
94
+ def rpc
95
+ @rpc ||= rpc_adapter.new(service_class)
96
+ end
97
+
98
+ def rpc_adapter
99
+ # TODO: Make this pluggable
100
+ #
101
+ # raise(AdapterNotSpecified, "configuration does not specify adapter") unless adapter.present?
102
+ #
103
+ # path_to_adapter = "active_remote/rpc_adapters/#{adapter}_adapter"
104
+ #
105
+ # begin
106
+ # require path_to_adapter
107
+ # rescue Gem::LoadError => e
108
+ # raise Gem::LoadError, "Specified '#{adapter]}' for RPC adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRemote)."
109
+ # rescue LoadError => e
110
+ # raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter is valid. If you use an adapter other than 'protobuf' add the necessary adapter gem to the Gemfile.", e.backtrace
111
+ # end
112
+ #
113
+ # path_to_adapter.classify.constantize
114
+
115
+ RPCAdapters::ProtobufAdapter
116
+ end
117
+ end
118
+
119
+ def rpc
120
+ self.class.rpc
70
121
  end
71
122
  end
72
123
  end
@@ -0,0 +1,56 @@
1
+ require 'active_remote/serializers/protobuf'
2
+
3
+ module ActiveRemote
4
+ module RPCAdapters
5
+ class ProtobufAdapter
6
+ include Serializers::Protobuf
7
+
8
+ attr_reader :last_request, :last_response, :service_class
9
+
10
+ ##
11
+ # Constructor!
12
+ #
13
+ def initialize(service_class)
14
+ @service_class = service_class
15
+ end
16
+
17
+ # Invoke an RPC call to the service for the given rpc method.
18
+ #
19
+ def execute(rpc_method, request_args)
20
+ @last_request = request(rpc_method, request_args)
21
+
22
+ service_class.client.__send__(rpc_method, @last_request) do |c|
23
+ # In the event of service failure, raise the error.
24
+ c.on_failure do |error|
25
+ raise ActiveRemoteError, error.message
26
+ end
27
+
28
+ # In the event of service success, assign the response.
29
+ c.on_success do |response|
30
+ @last_response = response
31
+ end
32
+ end
33
+
34
+ @last_response
35
+ end
36
+
37
+ private
38
+
39
+ # Return a protobuf request object for the given rpc request.
40
+ #
41
+ def request(rpc_method, request_args)
42
+ return request_args unless request_args.is_a?(Hash)
43
+
44
+ message_class = request_type(rpc_method)
45
+ fields = fields_from_attributes(message_class, request_args)
46
+ message_class.new(fields)
47
+ end
48
+
49
+ # Return the class applicable to the request for the given rpc method.
50
+ #
51
+ def request_type(rpc_method)
52
+ service_class.rpcs[rpc_method].request_type
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ module ActiveRemote
2
+ module ScopeKeys
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include PrimaryKey
7
+
8
+ class_attribute :_scope_keys
9
+ self._scope_keys = []
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ ##
15
+ # Allows you to define, at a class level, what keys should be
16
+ # used as identifiers when making remote calls. For instance,
17
+ #
18
+ # class Tag < ActiveRemote::Base
19
+ # scope_key :user_guid
20
+ # end
21
+ #
22
+ # When #scope_keys is called on Tag, it will return the primary
23
+ # key in addition to :user_guid as the scope keys.
24
+ #
25
+ def scope_key(*keys)
26
+ self._scope_keys += keys.map(&:to_s)
27
+ end
28
+
29
+ ##
30
+ # Used to define what keys are required when making remote
31
+ # persistence or refresh calls.
32
+ #
33
+ def scope_keys
34
+ [ primary_key.to_s ] + _scope_keys
35
+ end
36
+ end
37
+
38
+ ##
39
+ # Instance level access to the scope key of the current class
40
+ #
41
+ def scope_keys
42
+ @scope_keys ||= self.class.scope_keys
43
+ end
44
+
45
+ ##
46
+ # Instance level hash of scope keys and their corresponding
47
+ # values. For instance,
48
+ #
49
+ # class Tag < ActiveRemote::Base
50
+ # scope_key :user_guid
51
+ # end
52
+ #
53
+ # would return this hash:
54
+ #
55
+ # {
56
+ # :guid => tag[:guid],
57
+ # :user_guid => tag[:user_guid]
58
+ # }
59
+ #
60
+ # This hash is used when accessing or modifying a remote object
61
+ # like when calling #update in the persistence module, for example.
62
+ def scope_key_hash
63
+ attributes.slice(*scope_keys)
64
+ end
65
+ end
66
+ end
@@ -91,9 +91,12 @@ module ActiveRemote
91
91
  def search(args)
92
92
  args = _active_remote_search_args(args)
93
93
 
94
- remote = self.new
95
- remote._active_remote_search(args)
96
- remote.serialize_records
94
+ response = rpc.execute(:search, args)
95
+
96
+ if response.respond_to?(:records)
97
+ records = serialize_records(response.records)
98
+ records.each { |record| record.run_callbacks :search }
99
+ end
97
100
  end
98
101
 
99
102
  # :noapi:
@@ -116,16 +119,16 @@ module ActiveRemote
116
119
  #
117
120
  def _active_remote_search(args)
118
121
  run_callbacks :search do
119
- execute(:search, args)
122
+ rpc.execute(:search, args)
120
123
  end
121
124
  end
122
125
 
123
126
  # Reload this record from the remote service.
124
127
  #
125
128
  def reload
126
- _active_remote_search(:guid => self.guid)
129
+ response = self.class.find(scope_key_hash)
127
130
 
128
- fresh_object = self.class.new(last_response.to_hash)
131
+ fresh_object = self.class.instantiate(response.to_hash)
129
132
  @attributes.update(fresh_object.instance_variable_get('@attributes'))
130
133
  end
131
134
  end
@@ -8,34 +8,60 @@ module ActiveRemote
8
8
  include Serializers::JSON
9
9
  end
10
10
 
11
+ module ClassMethods
12
+ # Serialize the given records into Active Remote objects.
13
+ #
14
+ # ====Examples
15
+ #
16
+ # records = [ Generic::Remote::TagRequest.new(:name => 'foo') ]
17
+ #
18
+ # Tag.serialize_records(records) # => [ Tag#{:name => 'foo'} ]
19
+ #
20
+ def serialize_records(records)
21
+ records.map { |record| instantiate(record.to_hash) }
22
+ end
23
+ end
24
+
11
25
  # Examine the given response and add any errors to our internal errors
12
- # list. If no response is given, use the last response.
26
+ # list.
13
27
  #
14
- def add_errors_from_response(response=self.last_response)
15
- return unless response.respond_to?(:errors)
16
-
17
- response.errors.each do |error|
28
+ # ====Examples
29
+ #
30
+ # response = remote_call(:action_that_returns_errors, { :stuff => 'foo' })
31
+ #
32
+ # add_errors(response.errors)
33
+ #
34
+ def add_errors(errors)
35
+ errors.each do |error|
18
36
  if error.respond_to?(:message)
19
- errors.add(error.field, error.message)
37
+ self.errors.add(error.field, error.message)
20
38
  elsif error.respond_to?(:messages)
21
39
  error.messages.each do |message|
22
- errors.add(error.field, message)
40
+ self.errors.add(error.field, message)
23
41
  end
24
42
  end
25
43
  end
26
44
  end
27
45
 
28
- # Examine the last response and serialize any records returned into Active
29
- # Remote objects.
46
+ # DEPRECATED Use :add_errors instead
30
47
  #
31
- def serialize_records
32
- return nil unless last_response.respond_to?(:records)
48
+ def add_errors_from_response(response = nil)
49
+ warn 'DEPRECATED :add_errors_from_response is deprecated and will be removed in Active Remote 3.0. Use :add_errors instead'
33
50
 
34
- last_response.records.map do |remote_record|
35
- record = self.class.allocate
36
- record.instantiate(remote_record.to_hash)
37
- record
38
- end
51
+ response ||= last_response
52
+
53
+ add_errors(response.errors) if response.respond_to?(:errors)
54
+ end
55
+
56
+ # DEPRECATED – Use the class-level :serialize_errors instead
57
+ #
58
+ def serialize_records(records = nil)
59
+ warn 'DEPRECATED Calling :serialize_records on an instance is deprecated and will be removed in Active Remote 3.0. Use the class-level :serialize_records instead'
60
+
61
+ records ||= last_response.records if last_response.respond_to?(:records)
62
+ return if records.nil?
63
+
64
+ self.class.serialize_records(records)
39
65
  end
40
66
  end
41
67
  end
@@ -5,7 +5,7 @@ module ActiveRemote
5
5
  #
6
6
  def as_json(options = {})
7
7
  options ||= {}
8
-
8
+
9
9
  default_options = { :only => _publishable_json_attributes, :methods => _publishable_json_methods }
10
10
  default_options.merge!(options)
11
11
 
@@ -1,3 +1,3 @@
1
1
  module ActiveRemote
2
- VERSION = "2.0.2"
2
+ VERSION = "2.1.0.beta1"
3
3
  end
@@ -5,37 +5,28 @@ describe ActiveRemote::Bulk do
5
5
  let(:serialized_records) { double(:serialized_records) }
6
6
 
7
7
  describe ".create_all" do
8
- before {
9
- Tag.stub(:parse_records).and_return(records)
10
- Tag.any_instance.stub(:serialize_records).and_return(serialized_records)
11
- }
8
+ before { Tag.better_stub(:parse_records).and_return(records) }
12
9
 
13
10
  it "creates remote records" do
14
- Tag.any_instance.should_receive(:execute).with(:create_all, records)
11
+ Tag.rpc.better_receive(:execute).with(:create_all, records)
15
12
  Tag.create_all(records)
16
13
  end
17
14
  end
18
15
 
19
16
  describe ".delete_all" do
20
- before {
21
- Tag.stub(:parse_records).and_return(records)
22
- Tag.any_instance.stub(:serialize_records).and_return(serialized_records)
23
- }
17
+ before { Tag.better_stub(:parse_records).and_return(records) }
24
18
 
25
19
  it "deletes remote records" do
26
- Tag.any_instance.should_receive(:execute).with(:delete_all, records)
20
+ Tag.rpc.better_receive(:execute).with(:delete_all, records)
27
21
  Tag.delete_all(records)
28
22
  end
29
23
  end
30
24
 
31
25
  describe ".destroy_all" do
32
- before {
33
- Tag.stub(:parse_records).and_return(records)
34
- Tag.any_instance.stub(:serialize_records).and_return(serialized_records)
35
- }
26
+ before { Tag.better_stub(:parse_records).and_return(records) }
36
27
 
37
28
  it "destroys remote records" do
38
- Tag.any_instance.should_receive(:execute).with(:destroy_all, records)
29
+ Tag.rpc.better_receive(:execute).with(:destroy_all, records)
39
30
  Tag.destroy_all(records)
40
31
  end
41
32
  end
@@ -78,13 +69,10 @@ describe ActiveRemote::Bulk do
78
69
  end
79
70
 
80
71
  describe ".update_all" do
81
- before {
82
- Tag.stub(:parse_records).and_return(records)
83
- Tag.any_instance.stub(:serialize_records).and_return(serialized_records)
84
- }
72
+ before { Tag.stub(:parse_records).and_return(records) }
85
73
 
86
74
  it "updates remote records" do
87
- Tag.any_instance.should_receive(:execute).with(:update_all, records)
75
+ Tag.rpc.better_receive(:execute).with(:update_all, records)
88
76
  Tag.update_all(records)
89
77
  end
90
78
  end
@@ -47,8 +47,7 @@ describe ActiveRemote::Dirty do
47
47
  subject { Post.new(:name => 'foo') }
48
48
 
49
49
  before {
50
- subject.stub(:last_response).and_return({})
51
- subject.stub(:execute)
50
+ Post.stub(:find).and_return({})
52
51
  subject.reload
53
52
  }
54
53
 
@@ -83,23 +82,18 @@ describe ActiveRemote::Dirty do
83
82
  its(:changes) { should be_empty }
84
83
  end
85
84
 
86
- describe "#serialize_records" do
85
+ describe "#instantiate" do
87
86
  let(:post) { Post.new }
88
87
  let(:record) { ::Generic::Remote::Post.new(:name => 'foo') }
89
- let(:response) { double(:response, :records => [ record ]) }
90
-
91
- before {
92
- post.stub(:last_response).and_return(response)
93
- }
94
88
 
95
89
  it "clears previous changes" do
96
- records = post.serialize_records
97
- records.first.previous_changes.should be_nil
90
+ new_record = post.instantiate(record.to_hash)
91
+ new_record.previous_changes.should be_nil
98
92
  end
99
93
 
100
94
  it "clears changes" do
101
- records = post.serialize_records
102
- records.first.changes.should be_empty
95
+ new_record = post.instantiate(record.to_hash)
96
+ new_record.changes.should be_empty
103
97
  end
104
98
  end
105
99
  end
@@ -1,20 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActiveRemote::Persistence do
4
+ let(:rpc) { Tag.new }
5
+
4
6
  subject { Tag.new }
5
7
 
6
- before { Tag.any_instance.stub(:last_response).and_return(HashWithIndifferentAccess.new) }
8
+ before {
9
+ rpc.better_stub(:execute).and_return(HashWithIndifferentAccess.new)
10
+ Tag.better_stub(:rpc).and_return(rpc)
11
+ }
12
+ after { Tag.unstub(:rpc) }
7
13
 
8
14
  describe ".create" do
9
- before { Tag.any_instance.stub(:execute) }
10
- after { Tag.any_instance.unstub(:execute) }
11
-
12
15
  it "runs create callbacks" do
13
16
  Tag.any_instance.should_receive(:after_create_callback)
14
17
  Tag.create(:name => 'foo')
15
18
  end
16
19
 
17
-
18
20
  it "initializes and saves a new record" do
19
21
  Tag.any_instance.should_receive(:save)
20
22
  Tag.create(:name => 'foo')
@@ -27,9 +29,6 @@ describe ActiveRemote::Persistence do
27
29
  end
28
30
 
29
31
  describe ".create!" do
30
- before { Tag.any_instance.stub(:execute) }
31
- after { Tag.any_instance.unstub(:execute) }
32
-
33
32
  it "initializes and saves a new record" do
34
33
  Tag.any_instance.should_receive(:save!)
35
34
  Tag.create!(:name => 'foo')
@@ -45,11 +44,8 @@ describe ActiveRemote::Persistence do
45
44
  end
46
45
 
47
46
  describe "#delete" do
48
- before { subject.stub(:execute) }
49
- after { subject.unstub(:execute) }
50
-
51
47
  it "deletes a remote record" do
52
- subject.should_receive(:execute).with(:delete, subject.attributes.slice("guid"))
48
+ rpc.should_receive(:execute).with(:delete, subject.scope_key_hash)
53
49
  subject.delete
54
50
  end
55
51
 
@@ -60,31 +56,35 @@ describe ActiveRemote::Persistence do
60
56
  end
61
57
  end
62
58
 
63
- context "when the record has errors" do
64
- before { subject.stub(:has_errors?).and_return(true) }
65
-
66
- it "returns false" do
67
- subject.delete.should be_false
68
- end
69
- end
59
+ # TODO: This spec passes, but the implementation does not actually work.
60
+ # Uncomment it once the implementation is correct.
61
+ #
62
+ # context "when the record has errors" do
63
+ # before { subject.stub(:has_errors?).and_return(true) }
64
+ #
65
+ # it "returns false" do
66
+ # subject.delete.should be_false
67
+ # end
68
+ # end
70
69
  end
71
70
 
72
71
  describe "#delete!" do
73
- before { subject.stub(:execute) }
74
- after { subject.unstub(:execute) }
75
-
76
72
  it "deletes a remote record" do
77
- subject.should_receive(:execute).with(:delete, subject.attributes.slice("guid"))
73
+ rpc.should_receive(:execute).with(:delete, subject.scope_key_hash)
78
74
  subject.delete!
79
75
  end
80
76
 
81
- context "when an error occurs" do
82
- before { subject.stub(:execute).and_raise(ActiveRemote::ActiveRemoteError) }
83
-
84
- it "raises an exception" do
85
- expect { subject.delete! }.to raise_error(ActiveRemote::ActiveRemoteError)
86
- end
87
- end
77
+ # FIXME: This spec tests that excute raises an execption, not that an
78
+ # exception is raised when an error is returned (as it should).
79
+ # Uncomment it once the implementation is correct.
80
+ #
81
+ # context "when an error occurs" do
82
+ # before { rpc.stub(:execute).and_raise(ActiveRemote::ActiveRemoteError) }
83
+ #
84
+ # it "raises an exception" do
85
+ # expect { subject.delete! }.to raise_error(ActiveRemote::ActiveRemoteError)
86
+ # end
87
+ # end
88
88
  end
89
89
 
90
90
  describe "#destroy" do
@@ -92,7 +92,7 @@ describe ActiveRemote::Persistence do
92
92
  after { subject.unstub(:execute) }
93
93
 
94
94
  it "destroys a remote record" do
95
- subject.should_receive(:execute).with(:destroy, subject.attributes.slice("guid"))
95
+ subject.should_receive(:execute).with(:destroy, subject.scope_key_hash)
96
96
  subject.destroy
97
97
  end
98
98
 
@@ -103,31 +103,35 @@ describe ActiveRemote::Persistence do
103
103
  end
104
104
  end
105
105
 
106
- context "when the record has errors" do
107
- before { subject.stub(:has_errors?).and_return(true) }
108
-
109
- it "returns false" do
110
- subject.destroy.should be_false
111
- end
112
- end
106
+ # TODO: This spec passes, but the implementation does not actually work.
107
+ # Uncomment it once the implementation is correct.
108
+ #
109
+ # context "when the record has errors" do
110
+ # before { subject.stub(:has_errors?).and_return(true) }
111
+ #
112
+ # it "returns false" do
113
+ # subject.destroy.should be_false
114
+ # end
115
+ # end
113
116
  end
114
117
 
115
118
  describe "#destroy!" do
116
- before { subject.stub(:execute) }
117
- after { subject.unstub(:execute) }
118
-
119
119
  it "destroys a remote record" do
120
120
  subject.should_receive(:execute).with(:destroy, subject.attributes.slice("guid"))
121
121
  subject.destroy!
122
122
  end
123
123
 
124
- context "when an error occurs" do
125
- before { subject.stub(:execute).and_raise(ActiveRemote::ActiveRemoteError) }
126
-
127
- it "raises an exception" do
128
- expect { subject.destroy! }.to raise_error(ActiveRemote::ActiveRemoteError)
129
- end
130
- end
124
+ # FIXME: This spec tests that excute raises an execption, not that an
125
+ # exception is raised when an error is returned (as it should).
126
+ # Uncomment it once the implementation is correct.
127
+ #
128
+ # context "when an error occurs" do
129
+ # before { subject.stub(:execute).and_raise(ActiveRemote::ActiveRemoteError) }
130
+ #
131
+ # it "raises an exception" do
132
+ # expect { subject.destroy! }.to raise_error(ActiveRemote::ActiveRemoteError)
133
+ # end
134
+ # end
131
135
  end
132
136
 
133
137
  describe "#readonly?" do
@@ -188,9 +192,6 @@ describe ActiveRemote::Persistence do
188
192
  end
189
193
 
190
194
  describe "#save" do
191
- before { subject.stub(:execute) }
192
- after { subject.unstub(:execute) }
193
-
194
195
  it "runs save callbacks" do
195
196
  subject.should_receive(:run_callbacks).with(:save)
196
197
  subject.save
@@ -201,7 +202,7 @@ describe ActiveRemote::Persistence do
201
202
 
202
203
  it "creates the record" do
203
204
  expected_attributes = subject.attributes.reject { |key, value| key == "guid" }
204
- subject.should_receive(:execute).with(:create, expected_attributes)
205
+ rpc.should_receive(:execute).with(:create, expected_attributes)
205
206
  subject.save
206
207
  end
207
208
  end
@@ -212,7 +213,7 @@ describe ActiveRemote::Persistence do
212
213
  subject { Tag.allocate.instantiate(attributes) }
213
214
 
214
215
  it "updates the record" do
215
- subject.should_receive(:execute).with(:update, attributes)
216
+ rpc.should_receive(:execute).with(:update, attributes)
216
217
  subject.save
217
218
  end
218
219
  end
@@ -277,16 +278,21 @@ describe ActiveRemote::Persistence do
277
278
 
278
279
  describe "#update_attributes" do
279
280
  let(:attributes) { HashWithIndifferentAccess.new(:name => 'bar') }
281
+ let(:tag) { Tag.allocate.instantiate({:guid => "123"}) }
280
282
 
281
- before { Tag.any_instance.stub(:execute) }
282
- after { Tag.any_instance.unstub(:execute) }
283
+ before { Tag.rpc.stub(:execute).and_return(HashWithIndifferentAccess.new) }
284
+ after { Tag.rpc.unstub(:execute) }
283
285
 
284
286
  it "runs update callbacks" do
285
- tag = Tag.allocate.instantiate({:guid => "123"})
286
287
  tag.should_receive(:after_update_callback)
287
288
  tag.update_attributes({})
288
289
  end
289
290
 
291
+ it "updates a remote record" do
292
+ Tag.rpc.should_receive(:execute).with(:update, tag.scope_key_hash)
293
+ tag.update_attributes({})
294
+ end
295
+
290
296
  before { subject.stub(:save) }
291
297
  after { subject.unstub(:save) }
292
298
 
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRemote::PrimaryKey do
4
+ let(:tag) { Tag.new(:id => '1234', :guid => 'TAG-123', :user_guid => 'USR-123') }
5
+
6
+ after { Tag.instance_variable_set :@primary_key, nil }
7
+
8
+ describe ".default_primary_key" do
9
+ it 'returns array of :guid' do
10
+ Tag.default_primary_key.should eq(:guid)
11
+ end
12
+ end
13
+
14
+ describe "primary_key" do
15
+ context "when no arguments are passed" do
16
+ it "returns default primary key" do
17
+ Tag.primary_key.should eq(:guid)
18
+ end
19
+ end
20
+
21
+ context "when arguments are passed" do
22
+ let(:specified_primary_key) { :name }
23
+
24
+ it "returns the given primary key" do
25
+ Tag.primary_key(specified_primary_key).should eq(specified_primary_key)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "#primary_key" do
31
+ it "returns the primary key for the class" do
32
+ Tag.new.primary_key.should eq Tag.primary_key
33
+ end
34
+ end
35
+ end
@@ -7,15 +7,15 @@ describe ActiveRemote::RPC do
7
7
  let(:args) { double(:args) }
8
8
  let(:response) { double(:response) }
9
9
 
10
- before { Tag.any_instance.stub(:execute) }
10
+ before { Tag.rpc.stub(:execute) }
11
11
 
12
12
  it "calls the given RPC method" do
13
- Tag.any_instance.should_receive(:execute).with(:remote_method, args)
13
+ Tag.rpc.should_receive(:execute).with(:remote_method, args)
14
14
  Tag.remote_call(:remote_method, args)
15
15
  end
16
16
 
17
17
  it "returns the response" do
18
- Tag.any_instance.stub(:last_response).and_return(response)
18
+ Tag.rpc.stub(:execute).and_return(response)
19
19
  Tag.remote_call(:remote_method, args).should eq response
20
20
  end
21
21
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRemote::ScopeKeys do
4
+ let(:key) { :user_guid }
5
+ let(:_scope_keys) { [ 'user_guid' ] }
6
+ let(:scope_keys) { [ 'guid' ] + _scope_keys }
7
+ let(:tag) { Tag.new(tag_hash) }
8
+ let(:tag_hash) { { :guid => 'TAG-123', :user_guid => 'USR-123', :name => 'teh tag' } }
9
+
10
+ describe ".scope_key" do
11
+ after { Tag._scope_keys = [] }
12
+
13
+ it 'adds scope_key to _scope_keys' do
14
+ Tag.scope_key(key)
15
+ Tag._scope_keys.should eq(_scope_keys)
16
+ end
17
+ end
18
+
19
+ describe ".scope_keys" do
20
+ before { Tag.better_stub(:_scope_keys).and_return(_scope_keys) }
21
+
22
+ it "combines primary key with _scope_keys" do
23
+ Tag.scope_keys.should eq(['guid'] + _scope_keys)
24
+ end
25
+ end
26
+
27
+ describe "#scope_keys" do
28
+ it "returns the scope keys for the class" do
29
+ Tag.new.scope_keys.should eq Tag.scope_keys
30
+ end
31
+ end
32
+
33
+ describe "#scope_key_hash" do
34
+ let(:scope_key_hash) { { 'guid' => 'TAG-123', 'user_guid' => 'USR-123' } }
35
+
36
+ before { tag.better_stub(:scope_keys).and_return(scope_keys) }
37
+
38
+ it "returns a attribute hash of scope_keys" do
39
+ tag.scope_key_hash.should eq(scope_key_hash)
40
+ end
41
+ end
42
+ end
@@ -1,6 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActiveRemote::Search do
4
+ let(:records) { [ Generic::Remote::Tag.new ] }
5
+ let(:response) { Generic::Remote::Tags.new(:records => records) }
6
+ let(:rpc) { double(:rpc) }
7
+
4
8
  describe ".find" do
5
9
  let(:args) { Hash.new }
6
10
  let(:record) { double(:record) }
@@ -33,23 +37,20 @@ describe ActiveRemote::Search do
33
37
  end
34
38
 
35
39
  describe ".search" do
40
+ let(:serialized_records) { [ Tag.new ] }
41
+
36
42
  context "given args that respond to :to_hash" do
37
43
  let(:args) { Hash.new }
38
44
 
39
- before {
40
- Tag.any_instance.stub(:_active_remote_search)
41
- }
45
+ before { Tag.rpc.stub(:execute).and_return(response) }
42
46
 
43
47
  it "searches with the given args" do
44
- Tag.any_instance.should_receive(:_active_remote_search).with(args)
48
+ Tag.rpc.should_receive(:execute).with(:search, args)
45
49
  Tag.search(args)
46
50
  end
47
51
 
48
52
  it "returns records" do
49
- records = double(:records)
50
-
51
- Tag.any_instance.stub(:serialize_records).and_return(records)
52
- Tag.search(args).should eq records
53
+ Tag.search(args).should eq serialized_records
53
54
  end
54
55
  end
55
56
 
@@ -67,30 +68,32 @@ describe ActiveRemote::Search do
67
68
 
68
69
  subject { Tag.new }
69
70
 
71
+ before {
72
+ rpc.stub(:execute).and_return(response)
73
+ Tag.better_stub(:rpc).and_return(rpc)
74
+ }
75
+
70
76
  it "runs callbacks" do
71
77
  subject.should_receive(:run_callbacks).with(:search)
72
78
  subject._active_remote_search(args)
73
79
  end
74
80
 
75
81
  it "executes the search" do
76
- subject.should_receive(:execute).with(:search, args)
82
+ rpc.should_receive(:execute).with(:search, args)
77
83
  subject._active_remote_search(args)
78
84
  end
79
85
  end
80
86
 
81
87
  describe "#reload" do
82
- let(:args) { { :guid => 'foo' } }
83
- let(:attributes) { HashWithIndifferentAccess.new(:guid => 'foo', :name => 'bar', :updated_at => nil) }
88
+ let(:args) { attributes.slice('guid', 'user_guid') }
89
+ let(:attributes) { HashWithIndifferentAccess.new(:guid => 'foo', :name => 'bar', :updated_at => nil, :user_guid => 'baz') }
84
90
 
85
91
  subject { Tag.new(args) }
86
92
 
87
- before {
88
- subject.stub(:_active_remote_search)
89
- subject.stub(:last_response).and_return(attributes)
90
- }
93
+ before { Tag.better_stub(:find).and_return(attributes) }
91
94
 
92
95
  it "reloads the record" do
93
- subject.should_receive(:_active_remote_search).with(args)
96
+ Tag.better_receive(:find).with(subject.scope_key_hash)
94
97
  subject.reload
95
98
  end
96
99
 
@@ -1,64 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActiveRemote::Serialization do
4
- describe "#add_errors_from_response" do
5
- let(:error) { Generic::Error.new(:field => 'name', :message => 'Boom!') }
6
- let(:response) {
7
- tag = Generic::Remote::Tag.new
8
- tag.errors << error
9
- tag
10
- }
4
+ describe ".serialize_records" do
5
+ let(:records) { [ { :foo => 'bar' } ] }
11
6
 
12
7
  subject { Tag.new }
13
8
 
14
- context "when the response has errors" do
15
- it "adds the errors to the active remote object" do
16
- subject.add_errors_from_response(response)
17
- subject.errors[:name].should =~ ['Boom!']
18
- end
19
- end
20
-
21
- context "when the response doesn't respond to errors" do
22
- let(:response) { Generic::Remote::Tag.new }
23
-
24
- it "doesn't add errors" do
25
- subject.add_errors_from_response(response)
26
- subject.errors.empty?.should be_true
27
- end
28
- end
29
-
30
- context "when no response is given" do
31
- before { subject.stub(:last_response).and_return(response) }
32
-
33
- it "uses the last response" do
34
- subject.add_errors_from_response
35
- subject.errors[:name].should =~ ['Boom!']
9
+ it "serializes records into active remote objects" do
10
+ Tag.serialize_records(records).each do |record|
11
+ record.should be_a Tag
36
12
  end
37
13
  end
38
14
  end
39
15
 
40
- describe "#serialize_records" do
41
- let(:last_response) {
42
- MessageWithOptions.new(:records => records)
16
+ describe "#add_errors" do
17
+ let(:error) { Generic::Error.new(:field => 'name', :message => 'Boom!') }
18
+ let(:response) {
19
+ tag = Generic::Remote::Tag.new
20
+ tag.errors << error
21
+ tag
43
22
  }
44
- let(:records) { [ { :foo => 'bar' } ] }
45
23
 
46
24
  subject { Tag.new }
47
25
 
48
- context "when the last response has records" do
49
-
50
- before { subject.stub(:last_response).and_return(last_response) }
51
-
52
- it "serializes records into active remote objects" do
53
- subject.serialize_records.each do |record|
54
- record.should be_a Tag
55
- end
56
- end
57
- end
58
-
59
- context "when the last response doesn't respond to records" do
60
- it "returns nil" do
61
- subject.serialize_records.should be_nil
26
+ context "when the response has errors" do
27
+ it "adds the errors to the active remote object" do
28
+ subject.add_errors(response.errors)
29
+ subject.errors[:name].should =~ ['Boom!']
62
30
  end
63
31
  end
64
32
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe ActiveRemote::Serializers::JSON do
4
4
  describe "#as_json" do
5
- let(:attributes) { { :guid => 'foo', :name => 'bar', :updated_at => nil } }
5
+ let(:attributes) { { :guid => 'foo', :name => 'bar', :updated_at => nil, :user_guid => 'baz' } }
6
6
  let(:serializable_attributes) { { "tag" => attributes.stringify_keys } }
7
7
 
8
8
  subject { Tag.new(attributes) }
@@ -9,6 +9,7 @@ class Tag < ::ActiveRemote::Base
9
9
  attribute :guid
10
10
  attribute :name
11
11
  attribute :updated_at
12
+ attribute :user_guid
12
13
 
13
14
  after_update :after_update_callback
14
15
  after_create :after_create_callback
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_remote
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.1.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Hutchison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-08 00:00:00.000000000 Z
11
+ date: 2014-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: active_attr
@@ -181,9 +181,12 @@ files:
181
181
  - lib/active_remote/errors.rb
182
182
  - lib/active_remote/integration.rb
183
183
  - lib/active_remote/persistence.rb
184
+ - lib/active_remote/primary_key.rb
184
185
  - lib/active_remote/publication.rb
185
186
  - lib/active_remote/railtie.rb
186
187
  - lib/active_remote/rpc.rb
188
+ - lib/active_remote/rpc_adapters/protobuf_adapter.rb
189
+ - lib/active_remote/scope_keys.rb
187
190
  - lib/active_remote/search.rb
188
191
  - lib/active_remote/serialization.rb
189
192
  - lib/active_remote/serializers/json.rb
@@ -197,8 +200,10 @@ files:
197
200
  - spec/lib/active_remote/dsl_spec.rb
198
201
  - spec/lib/active_remote/integration_spec.rb
199
202
  - spec/lib/active_remote/persistence_spec.rb
203
+ - spec/lib/active_remote/primary_key_spec.rb
200
204
  - spec/lib/active_remote/publication_spec.rb
201
205
  - spec/lib/active_remote/rpc_spec.rb
206
+ - spec/lib/active_remote/scope_keys_spec.rb
202
207
  - spec/lib/active_remote/search_spec.rb
203
208
  - spec/lib/active_remote/serialization_spec.rb
204
209
  - spec/lib/active_remote/serializers/json_spec.rb
@@ -238,9 +243,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
243
  version: '0'
239
244
  required_rubygems_version: !ruby/object:Gem::Requirement
240
245
  requirements:
241
- - - ">="
246
+ - - ">"
242
247
  - !ruby/object:Gem::Version
243
- version: '0'
248
+ version: 1.3.1
244
249
  requirements: []
245
250
  rubyforge_project:
246
251
  rubygems_version: 2.2.1
@@ -256,8 +261,10 @@ test_files:
256
261
  - spec/lib/active_remote/dsl_spec.rb
257
262
  - spec/lib/active_remote/integration_spec.rb
258
263
  - spec/lib/active_remote/persistence_spec.rb
264
+ - spec/lib/active_remote/primary_key_spec.rb
259
265
  - spec/lib/active_remote/publication_spec.rb
260
266
  - spec/lib/active_remote/rpc_spec.rb
267
+ - spec/lib/active_remote/scope_keys_spec.rb
261
268
  - spec/lib/active_remote/search_spec.rb
262
269
  - spec/lib/active_remote/serialization_spec.rb
263
270
  - spec/lib/active_remote/serializers/json_spec.rb