active_harmony 1.0.1
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/.bundle/config +2 -0
- data/CHANGELOG +5 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +62 -0
- data/LICENSE +20 -0
- data/README.markdown +110 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/active_harmony.gemspec +77 -0
- data/lib/active_harmony.rb +14 -0
- data/lib/active_harmony/queue.rb +50 -0
- data/lib/active_harmony/queue_item.rb +48 -0
- data/lib/active_harmony/service.rb +293 -0
- data/lib/active_harmony/service_manager.rb +31 -0
- data/lib/active_harmony/service_url.rb +18 -0
- data/lib/active_harmony/synchronizable.rb +7 -0
- data/lib/active_harmony/synchronizable/core.rb +50 -0
- data/lib/active_harmony/synchronizable/mongoid.rb +30 -0
- data/lib/active_harmony/synchronizer.rb +131 -0
- data/lib/active_harmony/synchronizer_configuration.rb +71 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/unit/active_harmony/queue_item_spec.rb +41 -0
- data/spec/unit/active_harmony/queue_spec.rb +62 -0
- data/spec/unit/active_harmony/service_manager_spec.rb +44 -0
- data/spec/unit/active_harmony/service_spec.rb +313 -0
- data/spec/unit/active_harmony/synchronizable/core_spec.rb +53 -0
- data/spec/unit/active_harmony/synchronizable/mongoid_spec.rb +26 -0
- data/spec/unit/active_harmony/synchronizer_configuration_spec.rb +71 -0
- data/spec/unit/active_harmony/synchronizer_spec.rb +185 -0
- metadata +104 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module ActiveHarmony
|
4
|
+
describe ServiceManager do
|
5
|
+
before :each do
|
6
|
+
manager = ServiceManager.new
|
7
|
+
manager.instance_variable_set(:@services, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
it 'should setup hash for known services' do
|
12
|
+
manager = ServiceManager.new
|
13
|
+
manager.instance_variable_get(:@services).should == {}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#add_service_for_identifier' do
|
18
|
+
it 'should add service to services hash' do
|
19
|
+
service = Service.new
|
20
|
+
manager = ServiceManager.new
|
21
|
+
manager.add_service_for_identifier(service, :my_service)
|
22
|
+
manager.instance_variable_get(:@services).should == \
|
23
|
+
{:my_service => service}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#service_with_identifier' do
|
28
|
+
it 'should return service by identifier' do
|
29
|
+
service = Service.new
|
30
|
+
manager = ServiceManager.new
|
31
|
+
manager.instance_variable_set(:@services,
|
32
|
+
{:my_service => service})
|
33
|
+
manager.service_with_identifier(:my_service).should == service
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should raise an exception when theres no such service' do
|
37
|
+
manager = ServiceManager.new
|
38
|
+
lambda {
|
39
|
+
manager.service_with_identifier(:awsum_service)
|
40
|
+
}.should raise_exception
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
module ActiveHarmony
|
6
|
+
describe Service do
|
7
|
+
before :each do
|
8
|
+
@service = Service.new
|
9
|
+
@base_url = "http://chunky.bacon"
|
10
|
+
@service.base_url = @base_url
|
11
|
+
end
|
12
|
+
|
13
|
+
############################################################
|
14
|
+
# Initialization
|
15
|
+
describe "#initialize" do
|
16
|
+
it "should create new instance of Service::Rest" do
|
17
|
+
@service.should be_a(Service)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
############################################################
|
22
|
+
# URL and header configuration
|
23
|
+
describe "#base_url=" do
|
24
|
+
it "should set base url for the service" do
|
25
|
+
@service.base_url = @base_url
|
26
|
+
@service.base_url.should == @base_url
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#header=" do
|
31
|
+
it "should be empty hash as default" do
|
32
|
+
@service.header.should == {}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set extra headers for the service" do
|
36
|
+
extra_header = {
|
37
|
+
"Header name" => "Header Value"
|
38
|
+
}
|
39
|
+
@service.header = extra_header
|
40
|
+
@service.header.should == extra_header
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#set_header" do
|
45
|
+
it "should set one header" do
|
46
|
+
@service.set_header("test_header", "bla")
|
47
|
+
@service.header["test_header"].should == "bla"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Generating URLs
|
53
|
+
describe "#generate_rest_url" do
|
54
|
+
it "should generate list url" do
|
55
|
+
@service.generate_rest_url(:list, :my_object).path.should == \
|
56
|
+
"#{@base_url}/my_objects"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should generate show url" do
|
60
|
+
@service.generate_rest_url(:show, :my_object, 123).path.should == \
|
61
|
+
"#{@base_url}/my_objects/123"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should generate update url" do
|
65
|
+
@service.generate_rest_url(:update, :my_object, 123).path.should == \
|
66
|
+
"#{@base_url}/my_objects/123"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "#generate_url" do
|
71
|
+
it "should generate url for path" do
|
72
|
+
@service.generate_url("rainbow").should == \
|
73
|
+
"#{@base_url}/rainbow"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should generate url for path with slash at beginning" do
|
77
|
+
@service.generate_url("/rainbow").should == \
|
78
|
+
"#{@base_url}/rainbow"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Retrieving data
|
84
|
+
describe "#retrieve" do
|
85
|
+
it "should retrieve data from a url" do
|
86
|
+
url = "http://chunky.bacon/foo.bar"
|
87
|
+
response = "Chunky Bacon !!!"
|
88
|
+
stub_request(:get, "http://chunky.bacon/foo.bar").to_return(:body => response)
|
89
|
+
@service.retrieve(url).should == response
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should use header while making the request" do
|
93
|
+
url = "http://chunky.bacon/bar.foo"
|
94
|
+
stub_request(:get, url).
|
95
|
+
with(:headers => { 'Some-Header' => 'Some Value' }).
|
96
|
+
to_return(:body => "Success!")
|
97
|
+
@service.set_header 'Some-Header', 'Some Value'
|
98
|
+
@service.retrieve(url).should == "Success!"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#list" do
|
103
|
+
it "should list objects" do
|
104
|
+
objects = <<-EOF
|
105
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
106
|
+
<my_objects type="array">
|
107
|
+
<my_object>
|
108
|
+
<id type="integer">1</id>
|
109
|
+
<name>first</name>
|
110
|
+
</my_object>
|
111
|
+
<my_object>
|
112
|
+
<id type="integer">2</id>
|
113
|
+
<name>second</name>
|
114
|
+
</my_object>
|
115
|
+
</my_objects>
|
116
|
+
EOF
|
117
|
+
stub_request(:get, "http://chunky.bacon/my_objects").to_return(:body => objects)
|
118
|
+
response = @service.list(:my_object)
|
119
|
+
response.should == [
|
120
|
+
{"id" => 1, "name" => "first"},
|
121
|
+
{"id" => 2, "name" => "second"}
|
122
|
+
]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#show" do
|
127
|
+
it "should show an object" do
|
128
|
+
object = <<-EOF
|
129
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
130
|
+
<my_object>
|
131
|
+
<id type="integer">1</id>
|
132
|
+
<name>first</name>
|
133
|
+
</my_object>
|
134
|
+
EOF
|
135
|
+
stub_request(:get, "http://chunky.bacon/my_objects/1").to_return(:body => object)
|
136
|
+
response = @service.show(:my_object, 1)
|
137
|
+
response.should == {"id" => 1, "name" => "first"}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe "#update" do
|
142
|
+
it "should update object" do
|
143
|
+
response = <<-EOF
|
144
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
145
|
+
<my_object>
|
146
|
+
<id type="integer">1</id>
|
147
|
+
<name>updated name</name>
|
148
|
+
</my_object>
|
149
|
+
EOF
|
150
|
+
changes = {'name' => 'updated name'}
|
151
|
+
expected_headers = {'Content-type' => 'application/xml'}
|
152
|
+
expected_data = '<?xml version="1.0" encoding="UTF-8"?>
|
153
|
+
<my-object>
|
154
|
+
<name>updated name</name>
|
155
|
+
</my-object>'
|
156
|
+
expected_url = 'http://chunky.bacon/my_objects/123'
|
157
|
+
stub_request(:put, expected_url).to_return(:body => response)
|
158
|
+
@service.update(:my_object, 123, changes)
|
159
|
+
request(:put, expected_url).with(:body => /<name>updated name<\/name>/).should have_been_made
|
160
|
+
# FIXME Add specs that checks for hash in response
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#create" do
|
165
|
+
it "should create object" do
|
166
|
+
response = <<-EOF
|
167
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
168
|
+
<my_object>
|
169
|
+
<id type="integer">1</id>
|
170
|
+
<name>new name</name>
|
171
|
+
</my_object>
|
172
|
+
EOF
|
173
|
+
changes = {'name' => 'new name'}
|
174
|
+
expected_headers = {'Content-type' => 'application/xml'}
|
175
|
+
expected_url = 'http://chunky.bacon/my_objects'
|
176
|
+
stub_request(:post, expected_url).to_return(:body => response)
|
177
|
+
result = @service.create(:my_object, changes)
|
178
|
+
request(:post, expected_url).with(:body => /<name>new name<\/name>/).should have_been_made
|
179
|
+
result.should == {'id' => 1, 'name' => 'new name'}
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe "#set_contexts" do
|
184
|
+
it "should generate url with contexts in it" do
|
185
|
+
@service.set_contexts({
|
186
|
+
:first_context => 123,
|
187
|
+
:second_context => 456
|
188
|
+
})
|
189
|
+
url = @service.generate_rest_url(:list, :bacon)
|
190
|
+
url.path.should == "http://chunky.bacon/first_context/123/second_context/456/bacons"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#clear_contexts" do
|
195
|
+
it "should reset contexts on a service" do
|
196
|
+
@service.set_contexts({
|
197
|
+
:first_context => 123,
|
198
|
+
:second_context => 456
|
199
|
+
})
|
200
|
+
url = @service.generate_rest_url(:list, :bacon)
|
201
|
+
url.path.should == "http://chunky.bacon/first_context/123/second_context/456/bacons"
|
202
|
+
@service.clear_contexts
|
203
|
+
url = @service.generate_rest_url(:list, :bacon)
|
204
|
+
url.path.should == "http://chunky.bacon/bacons"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "with custom path" do
|
209
|
+
before :each do
|
210
|
+
@service.add_custom_url(:my_object, :list, "my_objects/all")
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "#add_custom_url" do
|
214
|
+
it "should use custom path for specific object/action" do
|
215
|
+
@service.generate_rest_url(:list, :my_object).path.should == "http://chunky.bacon/my_objects/all"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "#custom_url_for" do
|
220
|
+
it "should find custom path" do
|
221
|
+
@service.custom_url_for(:my_object, :list).path.should == "http://chunky.bacon/my_objects/all"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "with custom path and method" do
|
227
|
+
before :each do
|
228
|
+
@service.add_custom_url(:my_object, :update, "my_objects/update", :post)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should generate an url" do
|
232
|
+
url = @service.generate_rest_url(:update, :my_object, 1)
|
233
|
+
url.should be_a(ServiceUrl)
|
234
|
+
url.path.should == 'http://chunky.bacon/my_objects/update'
|
235
|
+
url.method.should == :post
|
236
|
+
end
|
237
|
+
|
238
|
+
it "should make a request" do
|
239
|
+
expected_url = 'http://chunky.bacon/my_objects/update'
|
240
|
+
stub_request(:post, expected_url)
|
241
|
+
@service.update(:my_object, 1, {})
|
242
|
+
request(:post, expected_url).should have_been_made
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "with custom root" do
|
247
|
+
before :each do
|
248
|
+
@service.root = "things/hidden/somewhere/my_objects"
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "#list" do
|
252
|
+
it "should list" do
|
253
|
+
object = <<-EOF
|
254
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
255
|
+
<things>
|
256
|
+
<hidden>
|
257
|
+
<somewhere>
|
258
|
+
<my_objects type="array">
|
259
|
+
<my_object>
|
260
|
+
<id type="integer">1</id>
|
261
|
+
<name>first</name>
|
262
|
+
</my_object>
|
263
|
+
<my_object>
|
264
|
+
<id type="integer">2</id>
|
265
|
+
<name>second</name>
|
266
|
+
</my_object>
|
267
|
+
</my_objects>
|
268
|
+
</somewhere>
|
269
|
+
</hidden>
|
270
|
+
</things>
|
271
|
+
EOF
|
272
|
+
stub_request(:get, "http://chunky.bacon/my_objects").to_return(:body => object)
|
273
|
+
response = @service.list(:my_object)
|
274
|
+
response.should == [
|
275
|
+
{"id" => 1, "name" => "first"},
|
276
|
+
{"id" => 2, "name" => "second"}
|
277
|
+
]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe "#show" do
|
282
|
+
it "should show" do
|
283
|
+
@service.root = "things/hidden/somewhere/my_object"
|
284
|
+
object = <<-EOF
|
285
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
286
|
+
<things>
|
287
|
+
<hidden>
|
288
|
+
<somewhere>
|
289
|
+
<my_object>
|
290
|
+
<id type="integer">1</id>
|
291
|
+
<name>updated name</name>
|
292
|
+
</my_object>
|
293
|
+
</somewhere>
|
294
|
+
</hidden>
|
295
|
+
</things>
|
296
|
+
EOF
|
297
|
+
stub_request(:get, "http://chunky.bacon/my_objects/1").to_return(:body => object)
|
298
|
+
response = @service.show(:my_object, 1)
|
299
|
+
response.should == {"id" => 1, "name" => "updated name"}
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
context "with custom object name" do
|
305
|
+
describe "#add_object_name" do
|
306
|
+
it "should add object name" do
|
307
|
+
@service.add_object_name(:old_name, :show, 'new_name')
|
308
|
+
@service.object_name_for(:old_name, :show).should == 'new_name'
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class MyClass
|
4
|
+
include Mongoid::Document
|
5
|
+
include ActiveHarmony::Synchronizable::Core
|
6
|
+
field :foo
|
7
|
+
end
|
8
|
+
|
9
|
+
module ActiveHarmony
|
10
|
+
module Synchronizable
|
11
|
+
describe Core do
|
12
|
+
describe "included" do
|
13
|
+
it 'should assign a new synchronizer' do
|
14
|
+
MyClass.synchronizer.should_not be_nil
|
15
|
+
MyClass.synchronizer.should be_a(ActiveHarmony::Synchronizer)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should set synchronizers factory to self' do
|
19
|
+
MyClass.synchronizer.factory.should == MyClass
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#push" do
|
24
|
+
context "queued" do
|
25
|
+
it "should create queue item" do
|
26
|
+
my_object = MyClass.new
|
27
|
+
my_object.foo = "bar"
|
28
|
+
my_object.save
|
29
|
+
my_object.push
|
30
|
+
queue_item = ActiveHarmony::QueueItem.last
|
31
|
+
queue_item.kind.should == "push"
|
32
|
+
queue_item.state.should == "new"
|
33
|
+
queue_item.result.should == nil
|
34
|
+
queue_item.object_type.should == "MyClass"
|
35
|
+
queue_item.object_remote_id.should == nil
|
36
|
+
queue_item.object_local_id.to_s.should == my_object.id.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "instant" do
|
41
|
+
it "should trigger an instant push" do
|
42
|
+
my_object = MyClass.new
|
43
|
+
my_object.foo = "bar"
|
44
|
+
my_object.synchronizer.expects(:push_object).with(my_object)
|
45
|
+
my_object.push(true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module ActiveHarmony
|
4
|
+
module Synchronizable
|
5
|
+
class MyModel; include ::Mongoid::Document; end
|
6
|
+
|
7
|
+
describe Mongoid do
|
8
|
+
|
9
|
+
before do
|
10
|
+
MyModel.send :include, ActiveHarmony::Synchronizable::Mongoid
|
11
|
+
@new_model = MyModel.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should add _remote_id_field" do
|
15
|
+
@new_model._remote_id = 1
|
16
|
+
@new_model._remote_id.should == 1
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should add _collection_order field" do
|
20
|
+
@new_model._collection_order = 1
|
21
|
+
@new_model._collection_order.should == 1
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ActiveHarmony
|
4
|
+
describe SynchronizerConfiguration do
|
5
|
+
before :each do
|
6
|
+
@config = SynchronizerConfiguration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#push' do
|
10
|
+
it 'should add a push field to synchronizable fields' do
|
11
|
+
@config.push :chunky
|
12
|
+
@config.push :bacon
|
13
|
+
@config.instance_variable_get(:@synchronizable_fields).should == \
|
14
|
+
[{:type => SynchronizerConfiguration::TYPE_PUSH, :field => :chunky},
|
15
|
+
{:type => SynchronizerConfiguration::TYPE_PUSH, :field => :bacon}]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#pull' do
|
20
|
+
it 'should add a pull field to synchronizable fields' do
|
21
|
+
@config.pull :chunky
|
22
|
+
@config.pull :bacon
|
23
|
+
@config.instance_variable_get(:@synchronizable_fields).should == \
|
24
|
+
[{:type => SynchronizerConfiguration::TYPE_PULL, :field => :chunky},
|
25
|
+
{:type => SynchronizerConfiguration::TYPE_PULL, :field => :bacon}]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#synchronize' do
|
30
|
+
it 'should add field of certain type to synchronizable fields' do
|
31
|
+
@config.synchronize :chunky, :type => :chunky_type
|
32
|
+
@config.instance_variable_get(:@synchronizable_fields).should == \
|
33
|
+
[{:field => :chunky, :type => :chunky_type}]
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should add field with no type to synchronizable fields' do
|
37
|
+
@config.synchronize :chunky
|
38
|
+
@config.instance_variable_get(:@synchronizable_fields).should == \
|
39
|
+
[{:field => :chunky, :type => SynchronizerConfiguration::TYPE_ALL}]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#synchronizable_for_push' do
|
44
|
+
it 'returns fields for push' do
|
45
|
+
@config.push :chunky
|
46
|
+
@config.push :bacon
|
47
|
+
@config.pull :meh
|
48
|
+
@config.synchronizable_for_push.should == [:chunky, :bacon]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#synchronizable_for_pull' do
|
53
|
+
it 'returns fields for pull' do
|
54
|
+
@config.pull :chunky
|
55
|
+
@config.pull :bacon
|
56
|
+
@config.push :meh
|
57
|
+
@config.synchronizable_for_pull.should == [:chunky, :bacon]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '#synchronizable_for_types' do
|
62
|
+
it 'returns synchronizables for specified types' do
|
63
|
+
@config.synchronize :chunky, :type => :chunky_type
|
64
|
+
@config.synchronize :bacon, :type => :bacon_type
|
65
|
+
@config.synchronize :meh, :type => :meh_type
|
66
|
+
@config.synchronizable_for_types([:chunky_type, :meh_type]).should == \
|
67
|
+
[:chunky, :meh]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|