active_harmony 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ ---
2
+ BUNDLE_DISABLE_SHARED_GEMS: "1"
@@ -0,0 +1,5 @@
1
+ 1.0.1
2
+ Fixes in Gemfile that caused problems with WebMock.
3
+
4
+ 1.0.0
5
+ Initial version
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'http://gemcutter.org'
2
+
3
+ gem "bundler", "~> 1.0.0"
4
+
5
+ gem 'mongoid', '>= 2.0.0.beta.17'
6
+ gem 'bson_ext', '1.0.4'
7
+
8
+ group :development do
9
+ gem 'jeweler'
10
+ gem 'rspec', '>= 2.0.0.beta.22'
11
+ gem 'webmock'
12
+ gem 'mocha'
13
+ end
@@ -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.
@@ -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
@@ -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