active_harmony 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/.bundle/config
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://gemcutter.org/
|
3
|
+
specs:
|
4
|
+
activemodel (3.0.0)
|
5
|
+
activesupport (= 3.0.0)
|
6
|
+
builder (~> 2.1.2)
|
7
|
+
i18n (~> 0.4.1)
|
8
|
+
activesupport (3.0.0)
|
9
|
+
addressable (2.2.1)
|
10
|
+
bson (1.0.4)
|
11
|
+
bson_ext (1.0.4)
|
12
|
+
builder (2.1.2)
|
13
|
+
crack (0.1.8)
|
14
|
+
diff-lcs (1.1.2)
|
15
|
+
gemcutter (0.6.1)
|
16
|
+
git (1.2.5)
|
17
|
+
i18n (0.4.1)
|
18
|
+
jeweler (1.4.0)
|
19
|
+
gemcutter (>= 0.1.0)
|
20
|
+
git (>= 1.2.5)
|
21
|
+
rubyforge (>= 2.0.0)
|
22
|
+
json_pure (1.4.6)
|
23
|
+
mocha (0.9.8)
|
24
|
+
rake
|
25
|
+
mongo (1.0.7)
|
26
|
+
bson (>= 1.0.4)
|
27
|
+
mongoid (2.0.0.beta.18)
|
28
|
+
activemodel (~> 3.0.0)
|
29
|
+
bson (= 1.0.4)
|
30
|
+
mongo (= 1.0.7)
|
31
|
+
tzinfo (~> 0.3.22)
|
32
|
+
will_paginate (~> 3.0.pre)
|
33
|
+
rake (0.8.7)
|
34
|
+
rspec (2.0.0.beta.22)
|
35
|
+
rspec-core (= 2.0.0.beta.22)
|
36
|
+
rspec-expectations (= 2.0.0.beta.22)
|
37
|
+
rspec-mocks (= 2.0.0.beta.22)
|
38
|
+
rspec-core (2.0.0.beta.22)
|
39
|
+
rspec-expectations (2.0.0.beta.22)
|
40
|
+
diff-lcs (>= 1.1.2)
|
41
|
+
rspec-mocks (2.0.0.beta.22)
|
42
|
+
rspec-core (= 2.0.0.beta.22)
|
43
|
+
rspec-expectations (= 2.0.0.beta.22)
|
44
|
+
rubyforge (2.0.4)
|
45
|
+
json_pure (>= 1.1.7)
|
46
|
+
tzinfo (0.3.23)
|
47
|
+
webmock (1.3.5)
|
48
|
+
addressable (>= 2.1.1)
|
49
|
+
crack (>= 0.1.7)
|
50
|
+
will_paginate (3.0.pre2)
|
51
|
+
|
52
|
+
PLATFORMS
|
53
|
+
ruby
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bson_ext (= 1.0.4)
|
57
|
+
bundler (~> 1.0.0)
|
58
|
+
jeweler
|
59
|
+
mocha
|
60
|
+
mongoid (>= 2.0.0.beta.17)
|
61
|
+
rspec (>= 2.0.0.beta.22)
|
62
|
+
webmock
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Vojto Rinik
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# Active Harmony
|
2
|
+
|
3
|
+
## What does it do
|
4
|
+
|
5
|
+
Active Harmony is a Ruby library for synchronizing Ruby objects with remote REST services.
|
6
|
+
|
7
|
+
Synchronizing just objects doesn't make much sense - so Active Harmony expects that you will use some persistance layer for your Ruby objects. For now, we support Mongoid, but you can easily code support for your favorite persistance layer. (Mongoid support is 30 lines - see synchronizable/harmony.rb)
|
8
|
+
|
9
|
+
## How to use it
|
10
|
+
|
11
|
+
### Installing
|
12
|
+
|
13
|
+
First, add Active Harmony to your Gemfile
|
14
|
+
|
15
|
+
gem 'active_harmony', :git => 'git://github.com/vojto/active_harmony.git'
|
16
|
+
|
17
|
+
### Setting up services
|
18
|
+
|
19
|
+
Before you move on to your objects, you need to setup a remote service.
|
20
|
+
|
21
|
+
Start by initializing new Service Manager. This will be object that knows about all services. If you're using Rails, you can add this to your initializers:
|
22
|
+
|
23
|
+
SERVICE_MANAGER = ActiveHarmony::ServiceManager.new
|
24
|
+
|
25
|
+
You will need to access manager in your models later - to setup synchronization with the service. But, customize your service first:
|
26
|
+
|
27
|
+
my_service.base_url = 'http://myservice.com/api'
|
28
|
+
my_service.header['MyServiceKey'] = 'abcdef'
|
29
|
+
|
30
|
+
Service allows more customization, like adding authentication, changing paths, and so on. See documentation for ActiveHarmony::Service for more information.
|
31
|
+
|
32
|
+
You might want to know, that service supports only XML type of requests/responses for now. XML path for finding entities in XML responses is also one of the things you can customize on Service class.
|
33
|
+
|
34
|
+
Now you need to add your newly created service to your manager.
|
35
|
+
|
36
|
+
SERVICE_MANAGER.add_service_for_identifier(my_service, :my_service)
|
37
|
+
|
38
|
+
### Setting up Ruby objects
|
39
|
+
|
40
|
+
Setup your Ruby object that will by synced. These lines set up persistence using Mongoid and include two classes from Active Harmony: Core adds basic synchronizable functionality, and Mongoid extends this to Mongoid persistence layer.
|
41
|
+
|
42
|
+
class Bacon
|
43
|
+
include Mongoid::Document
|
44
|
+
|
45
|
+
include ActiveHarmony::Synchronizable::Core
|
46
|
+
include ActiveHarmony::Synchronizable::Mongoid
|
47
|
+
|
48
|
+
field :tastyness
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
### Assigning Service to Ruby objects
|
54
|
+
|
55
|
+
Now you have added services to your managers, and added synchronization capabilities to your objects. Last step is wiring things up together.
|
56
|
+
|
57
|
+
A good place to do this is your model, you'll see why:
|
58
|
+
|
59
|
+
class Bacon
|
60
|
+
synchronizer.service = \
|
61
|
+
SERVICE_MANAGER.service_with_identifier :my_service
|
62
|
+
|
63
|
+
synchronizer.configure do |config|
|
64
|
+
config.synchronize :tastyness
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
As you can see, you're using class variable __synchronizer__. This variable was initialized for you when you included _Synchronizable::Core_, so you can change Service, or configure synchronizable fields.
|
69
|
+
|
70
|
+
### Synchronizing objects
|
71
|
+
|
72
|
+
Check out documentation of _Synchronizable::Core_ for methods you can use directly on your objects, or check out _Synchronizer_ class that has methods like pulling whole collection from remote server.
|
73
|
+
|
74
|
+
Here's a simple example of pushing local changes:
|
75
|
+
|
76
|
+
chunky_bacon = Bacon.first
|
77
|
+
chunky_bacon.push
|
78
|
+
ActiveHarmony::Queue.instance.run
|
79
|
+
|
80
|
+
Please note that using Queue requires Mongoid. If you want to do an instant push, you can pass in true as argument to push:
|
81
|
+
|
82
|
+
chunky_bacon.push(true)
|
83
|
+
|
84
|
+
# License
|
85
|
+
|
86
|
+
Copyright (c) 2010 Vojto Rinik
|
87
|
+
|
88
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
89
|
+
a copy of this software and associated documentation files (the
|
90
|
+
"Software"), to deal in the Software without restriction, including
|
91
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
92
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
93
|
+
permit persons to whom the Software is furnished to do so, subject to
|
94
|
+
the following conditions:
|
95
|
+
|
96
|
+
The above copyright notice and this permission notice shall be
|
97
|
+
included in all copies or substantial portions of the Software.
|
98
|
+
|
99
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
100
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
101
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
102
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
103
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
104
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
105
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
106
|
+
|
107
|
+
# Credits
|
108
|
+
|
109
|
+
* Vojto Rinik: vojto (at) rinik (dot) net
|
110
|
+
* Ryan Smith: ryandotsmith (at) gmail (dot) com
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'jeweler'
|
2
|
+
|
3
|
+
begin
|
4
|
+
Jeweler::Tasks.new do |gemspec|
|
5
|
+
gemspec.name = "active_harmony"
|
6
|
+
gemspec.summary = "Ruby synchronization with REST services"
|
7
|
+
gemspec.description = "Active Harmony is a Ruby library that takes care of synchronizing changes between local Ruby objects and remote REST services."
|
8
|
+
gemspec.email = "vojto@rinik.net"
|
9
|
+
gemspec.homepage = "http://github.com/vojto/active_harmony"
|
10
|
+
gemspec.authors = ["Vojto Rinik"]
|
11
|
+
end
|
12
|
+
rescue LoadError
|
13
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
14
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.1
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{active_harmony}
|
8
|
+
s.version = "1.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Vojto Rinik"]
|
12
|
+
s.date = %q{2010-09-22}
|
13
|
+
s.description = %q{Active Harmony is a Ruby library that takes care of synchronizing changes between local Ruby objects and remote REST services.}
|
14
|
+
s.email = %q{vojto@rinik.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.markdown"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".bundle/config",
|
21
|
+
"CHANGELOG",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE",
|
25
|
+
"README.markdown",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"active_harmony.gemspec",
|
29
|
+
"lib/active_harmony.rb",
|
30
|
+
"lib/active_harmony/queue.rb",
|
31
|
+
"lib/active_harmony/queue_item.rb",
|
32
|
+
"lib/active_harmony/service.rb",
|
33
|
+
"lib/active_harmony/service_manager.rb",
|
34
|
+
"lib/active_harmony/service_url.rb",
|
35
|
+
"lib/active_harmony/synchronizable.rb",
|
36
|
+
"lib/active_harmony/synchronizable/core.rb",
|
37
|
+
"lib/active_harmony/synchronizable/mongoid.rb",
|
38
|
+
"lib/active_harmony/synchronizer.rb",
|
39
|
+
"lib/active_harmony/synchronizer_configuration.rb",
|
40
|
+
"spec/spec_helper.rb",
|
41
|
+
"spec/unit/active_harmony/queue_item_spec.rb",
|
42
|
+
"spec/unit/active_harmony/queue_spec.rb",
|
43
|
+
"spec/unit/active_harmony/service_manager_spec.rb",
|
44
|
+
"spec/unit/active_harmony/service_spec.rb",
|
45
|
+
"spec/unit/active_harmony/synchronizable/core_spec.rb",
|
46
|
+
"spec/unit/active_harmony/synchronizable/mongoid_spec.rb",
|
47
|
+
"spec/unit/active_harmony/synchronizer_configuration_spec.rb",
|
48
|
+
"spec/unit/active_harmony/synchronizer_spec.rb"
|
49
|
+
]
|
50
|
+
s.homepage = %q{http://github.com/vojto/active_harmony}
|
51
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
52
|
+
s.require_paths = ["lib"]
|
53
|
+
s.rubygems_version = %q{1.3.7}
|
54
|
+
s.summary = %q{Ruby synchronization with REST services}
|
55
|
+
s.test_files = [
|
56
|
+
"spec/spec_helper.rb",
|
57
|
+
"spec/unit/active_harmony/queue_item_spec.rb",
|
58
|
+
"spec/unit/active_harmony/queue_spec.rb",
|
59
|
+
"spec/unit/active_harmony/service_manager_spec.rb",
|
60
|
+
"spec/unit/active_harmony/service_spec.rb",
|
61
|
+
"spec/unit/active_harmony/synchronizable/core_spec.rb",
|
62
|
+
"spec/unit/active_harmony/synchronizable/mongoid_spec.rb",
|
63
|
+
"spec/unit/active_harmony/synchronizer_configuration_spec.rb",
|
64
|
+
"spec/unit/active_harmony/synchronizer_spec.rb"
|
65
|
+
]
|
66
|
+
|
67
|
+
if s.respond_to? :specification_version then
|
68
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
69
|
+
s.specification_version = 3
|
70
|
+
|
71
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
72
|
+
else
|
73
|
+
end
|
74
|
+
else
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'mongoid'
|
2
|
+
|
3
|
+
require 'active_harmony/queue'
|
4
|
+
require 'active_harmony/queue_item'
|
5
|
+
require 'active_harmony/service'
|
6
|
+
require 'active_harmony/service_manager'
|
7
|
+
require 'active_harmony/service_url'
|
8
|
+
require 'active_harmony/synchronizable'
|
9
|
+
require 'active_harmony/synchronizer'
|
10
|
+
require 'active_harmony/synchronizer_configuration'
|
11
|
+
|
12
|
+
module ActiveHarmony
|
13
|
+
mattr_accessor :logger
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ActiveHarmony
|
2
|
+
class Queue
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
##
|
6
|
+
# Queues object for push
|
7
|
+
# @param [Object] Object
|
8
|
+
def queue_push(object)
|
9
|
+
queue_item = QueueItem.new( :kind => "push",
|
10
|
+
:object_type => object.class.name,
|
11
|
+
:object_local_id => object.id,
|
12
|
+
:state => "new" )
|
13
|
+
queue_item.save
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Queues object for pull
|
18
|
+
# @param [Class] Class of object
|
19
|
+
# @param [String] Remote ID for object
|
20
|
+
def queue_pull(object_class, remote_id)
|
21
|
+
queue_item = QueueItem.new( :kind => "pull",
|
22
|
+
:object_type => object_class.name,
|
23
|
+
:object_remote_id => remote_id,
|
24
|
+
:state => "new" )
|
25
|
+
queue_item.save
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# Runs queue
|
30
|
+
def run
|
31
|
+
queued_items.each do |item|
|
32
|
+
item.process_item
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Returns queued items
|
38
|
+
# @return [Array<QueueItem>] Queued Items
|
39
|
+
def queued_items
|
40
|
+
QueueItem.where(:state => "new").all
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Returns true if there are any items in queue
|
45
|
+
# @return [Boolean] Should run
|
46
|
+
def should_run?
|
47
|
+
queued_items.count > 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActiveHarmony
|
2
|
+
class QueueItem
|
3
|
+
include Mongoid::Document
|
4
|
+
|
5
|
+
field :kind
|
6
|
+
field :state
|
7
|
+
field :result
|
8
|
+
|
9
|
+
field :object_type
|
10
|
+
field :object_remote_id
|
11
|
+
field :object_local_id
|
12
|
+
|
13
|
+
##
|
14
|
+
# Processes queued item
|
15
|
+
def process_item
|
16
|
+
if kind == "push"
|
17
|
+
self.process_push
|
18
|
+
elsif kind == "pull"
|
19
|
+
self.process_pull
|
20
|
+
else
|
21
|
+
raise "Unrecognized Queue kind #{kind}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Processes queued item of type push
|
27
|
+
def process_push
|
28
|
+
factory = "::#{object_type}".constantize
|
29
|
+
local_object = factory.find(object_local_id)
|
30
|
+
syncer = factory.synchronizer
|
31
|
+
syncer.push_object(local_object)
|
32
|
+
|
33
|
+
self.state = 'done'
|
34
|
+
self.save
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Processes queued item of type pull
|
39
|
+
def process_pull
|
40
|
+
factory = "::#{object_type}".constantize
|
41
|
+
syncer = factory.synchronizer
|
42
|
+
syncer.pull_object(self.object_remote_id)
|
43
|
+
|
44
|
+
self.state = 'done'
|
45
|
+
self.save
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,293 @@
|
|
1
|
+
module ActiveHarmony
|
2
|
+
class Service
|
3
|
+
attr_accessor :base_url, :header, :root, :auth
|
4
|
+
|
5
|
+
############################################################
|
6
|
+
# Initialization & Configuration
|
7
|
+
|
8
|
+
##
|
9
|
+
# Initializes new Rest Service
|
10
|
+
def initialize
|
11
|
+
@header = {}
|
12
|
+
@contexts = {}
|
13
|
+
@paths = []
|
14
|
+
@object_names = []
|
15
|
+
@root = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Adds header
|
20
|
+
# @param [String] Header name
|
21
|
+
# @param [String] Header value
|
22
|
+
def set_header(name, value)
|
23
|
+
@header[name] = value
|
24
|
+
end
|
25
|
+
|
26
|
+
############################################################
|
27
|
+
# Generating URLs
|
28
|
+
|
29
|
+
##
|
30
|
+
# Generates url for REST service
|
31
|
+
# @param [Symbol] Action: show, list, update or destroy
|
32
|
+
# @param [Symbol] Object type
|
33
|
+
# @param [Integer] Object ID
|
34
|
+
# @return [Service::ServiceUrl] Generated URL
|
35
|
+
def generate_rest_url(action, object_type, object_id = nil)
|
36
|
+
url = custom_url_for(object_type, action)
|
37
|
+
|
38
|
+
method = if action == :list || action == :show
|
39
|
+
:get
|
40
|
+
elsif action == :create
|
41
|
+
:post
|
42
|
+
elsif action == :update
|
43
|
+
:put
|
44
|
+
end
|
45
|
+
|
46
|
+
if url
|
47
|
+
url.request_method ||= method
|
48
|
+
if object_id
|
49
|
+
url.path.sub!(':id', object_id.to_s)
|
50
|
+
end
|
51
|
+
return url if url
|
52
|
+
end
|
53
|
+
|
54
|
+
path = ''
|
55
|
+
path += @contexts.collect { |name, value|
|
56
|
+
"#{name}/#{value}"
|
57
|
+
}.join('/')
|
58
|
+
path += '/'
|
59
|
+
if action == :list || action == :create
|
60
|
+
path += object_type.to_s.pluralize
|
61
|
+
elsif action == :show || action == :update
|
62
|
+
path += "#{object_type.to_s.pluralize}/#{object_id.to_s}"
|
63
|
+
end
|
64
|
+
url = generate_url(path)
|
65
|
+
|
66
|
+
ServiceUrl.new(url, method)
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# Generates URL for path using base_url
|
71
|
+
# @param [String] Path
|
72
|
+
# @return [String] URL
|
73
|
+
def generate_url(path)
|
74
|
+
if path =~ /^\//
|
75
|
+
path.sub!('/', '')
|
76
|
+
end
|
77
|
+
path = "#{@base_url}/#{path}"
|
78
|
+
end
|
79
|
+
|
80
|
+
############################################################
|
81
|
+
# Retrieving data from networks
|
82
|
+
|
83
|
+
##
|
84
|
+
# Retrieves data from URL
|
85
|
+
# @param [String] URL
|
86
|
+
# @return [String] Retrieved data from the URL
|
87
|
+
def retrieve(url, method = :get, headers = {}, data = nil)
|
88
|
+
puts "[ActiveHarmony] Retrieving data:"
|
89
|
+
puts "[ActiveHarmony] URL: #{url}"
|
90
|
+
puts "[ActiveHarmony] Method: #{method}"
|
91
|
+
puts "[ActiveHarmony] Headers: #{headers.inspect}"
|
92
|
+
puts "[ActiveHarmony] Data: #{data.inspect}"
|
93
|
+
if defined?(Rails) && !Rails.env.test?
|
94
|
+
data = retrieve_with_typhoeus(url, method, headers, data)
|
95
|
+
else
|
96
|
+
data = retrieve_with_http(url, method, headers, data)
|
97
|
+
end
|
98
|
+
data
|
99
|
+
end
|
100
|
+
|
101
|
+
def retrieve_with_typhoeus(url, method, headers, data)
|
102
|
+
request_options = {:headers => @header.merge(headers),
|
103
|
+
:body => data}
|
104
|
+
request_options.merge!(auth) if auth
|
105
|
+
# puts request_options
|
106
|
+
response = Typhoeus::Request.send(method, url, request_options)
|
107
|
+
response.body
|
108
|
+
end
|
109
|
+
|
110
|
+
def retrieve_with_http(url, method, headers, data)
|
111
|
+
url = URI.parse(url)
|
112
|
+
response = nil
|
113
|
+
headers = @header.merge(headers)
|
114
|
+
Net::HTTP.start(url.host, url.port) do |http|
|
115
|
+
if method == :get
|
116
|
+
response = http.get(url.path, headers).body
|
117
|
+
elsif [:put, :post].include?(method)
|
118
|
+
response = http.send(method, url.path, data, headers).body
|
119
|
+
end
|
120
|
+
end
|
121
|
+
response
|
122
|
+
end
|
123
|
+
|
124
|
+
############################################################
|
125
|
+
# Parsing response
|
126
|
+
|
127
|
+
##
|
128
|
+
# Parses XML
|
129
|
+
# @param [String] XML
|
130
|
+
# @return [Array<Hash>] Parsed XML in Ruby primitives
|
131
|
+
def parse_xml(xml_string)
|
132
|
+
return {} if xml_string.blank?
|
133
|
+
begin
|
134
|
+
Hash.from_xml(xml_string)
|
135
|
+
rescue Exception => e
|
136
|
+
return {}
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def find_object_in_result(result, object_type, action)
|
141
|
+
data = result
|
142
|
+
# puts result
|
143
|
+
if @root
|
144
|
+
@root.split('/').each do |branch|
|
145
|
+
break if data.nil?
|
146
|
+
data = data[branch]
|
147
|
+
end
|
148
|
+
data
|
149
|
+
else
|
150
|
+
path = if action == :list
|
151
|
+
object_type.to_s.pluralize
|
152
|
+
else
|
153
|
+
object_type.to_s
|
154
|
+
end
|
155
|
+
data[path] # Pluralize? When? action?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
############################################################
|
160
|
+
# Working with REST services
|
161
|
+
|
162
|
+
##
|
163
|
+
# List collection of remote objects
|
164
|
+
# @param [Symbol] Object type
|
165
|
+
# @return [Array<Hash>] Array of objects
|
166
|
+
def list(object_type)
|
167
|
+
url = generate_rest_url(:list, object_type)
|
168
|
+
result = retrieve(url.path)
|
169
|
+
parsed_result = parse_xml(result)
|
170
|
+
find_object_in_result(parsed_result, object_type, :list)
|
171
|
+
end
|
172
|
+
|
173
|
+
##
|
174
|
+
# Shows remote object
|
175
|
+
# @param [Symbol] Object type
|
176
|
+
# @param [String] Object ID
|
177
|
+
# @return [Hash] Object
|
178
|
+
def show(object_type, id)
|
179
|
+
url = generate_rest_url(:show, object_type, id)
|
180
|
+
result = retrieve(url.path)
|
181
|
+
parsed_result = parse_xml(result)
|
182
|
+
find_object_in_result(parsed_result, object_type, :show)
|
183
|
+
end
|
184
|
+
|
185
|
+
##
|
186
|
+
# Updates remote object
|
187
|
+
# @param [Symbol] Object type
|
188
|
+
# @param [String] Object ID
|
189
|
+
# @param [Hash] Data to be sent
|
190
|
+
def update(object_type, id, data)
|
191
|
+
url = generate_rest_url(:update, object_type, id)
|
192
|
+
object_name = object_name_for(object_type, :update)
|
193
|
+
xml_data = data.to_xml(:root => object_name, :skip_instruct => true, :dasherize => false)
|
194
|
+
result = retrieve(url.path, url.method, {'Content-type' => 'application/xml'}, xml_data)
|
195
|
+
find_object_in_result(result, object_type, :update)
|
196
|
+
end
|
197
|
+
|
198
|
+
##
|
199
|
+
# Creates a new remote object
|
200
|
+
# @param [Symbol] Object type
|
201
|
+
# @param [Hash] Data to be sent
|
202
|
+
def create(object_type, data)
|
203
|
+
url = generate_rest_url(:create, object_type)
|
204
|
+
object_name = object_name_for(object_type, :create)
|
205
|
+
xml_data = data.to_xml(:root => object_name, :skip_instruct => true, :dasherize => false)
|
206
|
+
result = retrieve(url.path, url.method, {'Content-type' => 'application/xml'}, xml_data)
|
207
|
+
parsed_result = parse_xml(result)
|
208
|
+
find_object_in_result(parsed_result, object_type, :create)
|
209
|
+
end
|
210
|
+
|
211
|
+
############################################################
|
212
|
+
# Setting contexts
|
213
|
+
|
214
|
+
##
|
215
|
+
# Set contexts for service
|
216
|
+
# @param [Hash] Contexts
|
217
|
+
def set_contexts(contexts)
|
218
|
+
contexts.each do |name, value|
|
219
|
+
@contexts[name.to_sym] = value
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
##
|
224
|
+
# Clears contexts for service
|
225
|
+
def clear_contexts
|
226
|
+
@contexts = {}
|
227
|
+
end
|
228
|
+
|
229
|
+
############################################################
|
230
|
+
# Setting custom URLs
|
231
|
+
|
232
|
+
##
|
233
|
+
# Adds custom path
|
234
|
+
# @param [Symbol] Object type
|
235
|
+
# @param [Symbol] Action
|
236
|
+
# @param [String] Path
|
237
|
+
def add_custom_url(object_type, action, path, method = nil)
|
238
|
+
@paths << {
|
239
|
+
:object_type => object_type,
|
240
|
+
:action => action,
|
241
|
+
:path => path,
|
242
|
+
:method => method
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
##
|
247
|
+
# Returns custom path
|
248
|
+
# @param [Symbol] Object type
|
249
|
+
# @param [Symbol] Action
|
250
|
+
# @return [Service::ServiceUrl] Custom path
|
251
|
+
def custom_url_for(object_type, action)
|
252
|
+
path = @paths.find do |path|
|
253
|
+
path[:object_type] == object_type &&
|
254
|
+
path[:action] == action
|
255
|
+
end
|
256
|
+
if path
|
257
|
+
ServiceUrl.new(generate_url(path[:path]), path[:method])
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
############################################################
|
262
|
+
# Setting custom object names for objects
|
263
|
+
|
264
|
+
##
|
265
|
+
# Adds new name for some type of object
|
266
|
+
# @param [Symbol] Object type
|
267
|
+
# @param [Symbol] Action
|
268
|
+
# @param [String] New object name
|
269
|
+
def add_object_name(object_type, action, new_object_name)
|
270
|
+
@object_names << {
|
271
|
+
:object_type => object_type,
|
272
|
+
:action => action,
|
273
|
+
:new_object_name => new_object_name.to_s
|
274
|
+
}
|
275
|
+
end
|
276
|
+
|
277
|
+
##
|
278
|
+
# Returns custom object name for action
|
279
|
+
# @param [Symbol] Object type
|
280
|
+
# @param [Symbol] Action
|
281
|
+
def object_name_for(object_type, action)
|
282
|
+
object_name = @object_names.find do |object_name|
|
283
|
+
object_name[:object_type] == object_type
|
284
|
+
object_name[:action] == action
|
285
|
+
end
|
286
|
+
object_name = object_name ? object_name[:new_object_name] : nil
|
287
|
+
unless object_name
|
288
|
+
object_name = object_type.to_s.gsub('-', '_')
|
289
|
+
end
|
290
|
+
object_name
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|