sk-api 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ nbproject/*
2
+ coverage/*
3
+ rdoc/*
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Georg Leciejewski
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/Manifest ADDED
@@ -0,0 +1,30 @@
1
+ MIT-LICENSE
2
+ README.rdoc
3
+ Rakefile
4
+ init.rb
5
+ lib/activeresource_patches/README
6
+ lib/activeresource_patches/base.rb
7
+ lib/activeresource_patches/validations.rb
8
+ lib/resources/address.rb
9
+ lib/resources/base.rb
10
+ lib/resources/client.rb
11
+ lib/resources/credit_note.rb
12
+ lib/resources/line_item.rb
13
+ lib/sk_api.rb
14
+ lib/utils/field_map.rb
15
+ lib/utils/serializer.rb
16
+ lib/version.rb
17
+ nbproject/private/private.properties
18
+ nbproject/project.properties
19
+ nbproject/project.xml
20
+ spec/resources/client_spec.rb
21
+ spec/resources/credit_note_spec.rb
22
+ spec/spec_helper.rb
23
+ spec/utils/field_map_spec.rb
24
+ tasks/sk_api_tasks.rake
25
+ vendor/jsonschema-1.0.0/README.rdoc
26
+ vendor/jsonschema-1.0.0/Rakefile
27
+ vendor/jsonschema-1.0.0/lib/jsonschema.rb
28
+ vendor/jsonschema-1.0.0/ruby-jsonschema.gemspec
29
+ vendor/jsonschema-1.0.0/test/jsonschema_test.rb
30
+ Manifest
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ = SalesKing Api
2
+
3
+ This plugin is used internally by SalesKing to deliver the API and
4
+ can be used externally as an API client.
5
+
6
+ The SalesKing API client uses ActiveResource, ,with some little build in
7
+ fixes due to json parsing. For now it only supports JSON, XML comes later.
8
+
9
+ To be able to use the API you must have an SalesKing account and API access.
10
+ Than you can start writing your own middelware-stack.
11
+
12
+ == Upcomming
13
+
14
+ The code is used in production, but still there is a lot to come, so if you
15
+ try to use it please tell us, so we can give you API access and support.
16
+
17
+ With the rails 3.0 release we are going to pack this code as gem for
18
+ easier delivery & bundling
19
+
20
+ == Example
21
+
22
+ client = SkApi::Resources::Client.new(:last_name=>'Eder')
23
+ client.first_name = "Meister"
24
+ client.save
25
+
26
+ For more take a look into the specs.
27
+
28
+ == Salesking API Resources
29
+
30
+ Right now there are only two resources available:
31
+
32
+ * clients
33
+ * credit_notes
34
+
35
+ == Tests
36
+
37
+ To run the test you must insert some credentials into the spec helper.
38
+ We will gladly provide you with a test account on one of our development
39
+ systems.
40
+
41
+
42
+ Copyright (c) 2009, 2010 Georg Leciejewski, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "sk-api"
10
+ gem.summary = %Q{Interact with SalesKing}
11
+ gem.description = %Q{Interact with SalesKing}
12
+ gem.email = "gl@salesking.eu"
13
+ gem.homepage = "http://github.com/salesking/sk-api"
14
+ gem.authors = ["Georg Leciejewski"]
15
+ gem.add_development_dependency "rspec", ">= 0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ desc 'Default: run specs.'
24
+ task :default => :spec
25
+
26
+ spec_files = Rake::FileList["spec/**/*_spec.rb"]
27
+
28
+ desc "Run specs"
29
+ Spec::Rake::SpecTask.new do |t|
30
+ t.spec_files = spec_files
31
+ t.spec_opts = ["-c"]
32
+ end
33
+
34
+ desc "Generate code coverage"
35
+ Spec::Rake::SpecTask.new(:coverage) do |t|
36
+ t.spec_files = spec_files
37
+ t.rcov = true
38
+ t.rcov_opts = ['--exclude', 'spec,/var/lib/gems']
39
+ end
40
+
41
+ desc 'Generate sk-api documentation.'
42
+ Rake::RDocTask.new(:rdoc) do |rdoc|
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = 'SalesKing-Api'
45
+ rdoc.options << '--line-numbers' << '--inline-source'
46
+ rdoc.rdoc_files.include('README')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "#{File.dirname(__FILE__)}/lib/sk_api"
@@ -0,0 +1,3 @@
1
+ Patches for activerecord as long as the 3.0 version is not out
2
+
3
+ * parse error from json response
@@ -0,0 +1,23 @@
1
+ module ActiveResource
2
+ # Overridden methods to suit SalesKing.
3
+ # Some changes might be kicked when AR 3.0 is out
4
+ class Base
5
+
6
+ # override encode because json is also returned nested by SalesKing
7
+ def encode(options={})
8
+ case self.class.format
9
+ when ActiveResource::Formats[:xml]
10
+ self.class.format.encode(attributes, {:root => self.class.element_name}.merge(options))
11
+ else # json also nested
12
+ self.class.format.encode( {self.class.element_name => attributes}, options)
13
+ end
14
+ end
15
+
16
+ # override ARes method to parse only the client part
17
+ def load_attributes_from_response(response)
18
+ if response['Content-Length'] != "0" && response.body.strip.size > 0
19
+ load( self.class.format.decode(response.body)[self.class.element_name] )
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module ActiveResource
2
+
3
+ module Validations
4
+ # Validate a resource and save (POST) it to the remote web service.
5
+ def save_with_validation
6
+ save_without_validation
7
+ true
8
+ rescue ResourceInvalid => error
9
+ case error.response['Content-Type']
10
+ when /xml/ #PATCH
11
+ errors.from_xml(error.response.body)
12
+ when /json/ #PATCH
13
+ errors.from_json(error.response.body)
14
+ end
15
+ false
16
+ end
17
+ end
18
+
19
+ class Errors
20
+
21
+ # Patched cause we dont need no attribute name magic .. and its just simpler
22
+ def from_array(messages)
23
+ clear
24
+ messages.each do |msg|
25
+ add msg[0], msg[1]
26
+ end
27
+ end
28
+ end #Errors
29
+
30
+ end
@@ -0,0 +1,35 @@
1
+ module SKApi
2
+ module Resources
3
+ class Address < SKApi::Resources::Base
4
+
5
+ def save
6
+ save_with_validation
7
+ end
8
+
9
+ def self.schema
10
+ { "type" => "object",
11
+ "properties" => SKApi::Resources::Address.schema_props}
12
+ end
13
+
14
+ def self.schema_props
15
+ {
16
+ "id" => {"type" => "string", "identity" => true, "readonly" => true},
17
+ "address1" => {"type" => "string", "optional" => true},
18
+ "address2" => {"type" => "string", "optional" => true},
19
+ "city" => {"type" => "string", "optional" => true},
20
+ "country" => {"type" => "string", "optional" => true},
21
+ "state" => {"type" => "string", "optional" => true},
22
+ "zip" => {"type" => "string", "optional" => true},
23
+ "pobox" => {"type" => "string", "optional" => true},
24
+ "long" => {"type" => "string", "optional" => true},
25
+ "lat" => {"type" => "string", "optional" => true},
26
+ "address_type" => {"type" => "string", "optional" => true},
27
+ "created_at" => {"type" => "string", "format" =>"date-time", "readonly"=> true},
28
+ "updated_at" => {"type" => "string", "format" =>"date-time", "readonly"=> true},
29
+
30
+ }
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ module SKApi
2
+ module Resources
3
+
4
+ class Base < ActiveResource::Base
5
+ include SKApi::Utils::Serializer
6
+ self.format = :json # bug in AR must be set here
7
+
8
+ def initialize(attributes = {})
9
+ # json comes in nested {client={data}
10
+ attr = attributes[self.class.element_name] || attributes
11
+ super(attr)
12
+ end
13
+
14
+ # Define the connection to be used when talking to a salesking server
15
+ def self.set_connection(opts)
16
+ self.site = opts[:site] #"http://demo.salesking.local:3000/api/"
17
+ self.user = opts[:user] # "demo@salesking.eu"
18
+ self.password = opts[:password] #"demo"
19
+ self.format = opts[:format].to_sym #:json
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,62 @@
1
+ module SKApi
2
+ module Resources
3
+ class Client < SKApi::Resources::Base
4
+
5
+ def save
6
+ # validate_schema
7
+ save_with_validation
8
+ end
9
+
10
+ # not realy stable yet
11
+ def validate_schema
12
+ json = self.to_json
13
+ obj = Rufus::Json.decode(json)
14
+ JSON::Schema.validate(obj, SKApi::Resources::Client.schema)
15
+ end
16
+
17
+ def self.schema
18
+ { "type" => "object",
19
+ "properties" => SKApi::Resources::Client.schema_props}
20
+ end
21
+
22
+ def self.schema_props
23
+ {
24
+ "id" => {"type" => "string", "identity" => true , "readonly" => true},
25
+ "number" => {"type" => "string", "optional" => true},
26
+ "organisation" => {"type" => "string"},
27
+ "first_name" => {"type" => "string", "optional" => true},
28
+ "last_name" => {"type" => "string", "optional" => true},
29
+ "title" => {"type" => "string", "optional" => true},
30
+ "position" => {"type" => "string", "optional" => true},
31
+ "gender" => {"type" => "string", "enum" => ["male", "female"] , "optional" => true},
32
+ "email" => {"type" => "string", "optional" => true},
33
+ "birthday" => {"type" => "string", "format" =>"date", "optional" => true},
34
+ "url" => {"type" => "string", "optional" => true},
35
+ "phone_fax" => {"type" => "string", "optional" => true},
36
+ "phone_mobile" => {"type" => "string", "optional" => true},
37
+ "phone_office" => {"type" => "string", "optional" => true},
38
+ "phone_home" => {"type" => "string", "optional" => true},
39
+ "bank_owner" => {"type" => "string", "optional" => true},
40
+ "bank_name" => {"type" => "string", "optional" => true},
41
+ "bank_number" => {"type" => "string", "optional" => true},
42
+ "bank_account_number" => {"type" => "string", "optional" => true},
43
+ "bank_iban" => {"type" => "string", "optional" => true},
44
+ "bank_swift" => {"type" => "string", "optional" => true},
45
+ "vat_number" => {"type" => "string", "optional" => true},
46
+ "tax_number" => {"type" => "string", "optional" => true},
47
+ "tag_list" => {"type" => "string", "optional" => true},
48
+ "cash_discount" => {"type" => "number", "optional" => true},
49
+ "due_days" => {"type" => "integer", "optional" => true},
50
+ "payment_method" => {"type" => "string", "enum" => ["draft", "open", "closed"], "optional" => true},
51
+ "sending_method" => {"type" => "string", "enum" => ["draft", "open", "closed"], "optional" => true},
52
+ "lock_version" => {"type" => "integer", "readonly" => true, "optional" => true},
53
+ "created_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly" => true},
54
+ "updated_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly" => true},
55
+ "address_field" => {"type" => "string", "optional" => true, "readonly" => true},
56
+ "addresses" => {"type" => "array"},
57
+ }
58
+ end #schema_props
59
+
60
+ end #Client
61
+ end #Resources
62
+ end #SKApi
@@ -0,0 +1,49 @@
1
+ module SKApi
2
+ module Resources
3
+ class CreditNote < SKApi::Resources::Base
4
+
5
+ def save
6
+ save_with_validation
7
+ end
8
+
9
+ ##########################################################################
10
+ # Class methods
11
+ ##########################################################################
12
+
13
+ def self.schema
14
+ { "type" => "object",
15
+ "properties" => SKApi::Resources::CreditNote.schema_props}
16
+ end
17
+
18
+ def self.schema_props
19
+ {
20
+ "id" => {"type" => "string", "identity" => true, "optional" => true, "readonly" => true},
21
+ "number" => {"type" => "string", "optional" => true},
22
+ "date" => {"type" => "string", "format" =>"date", "optional" => true},
23
+ "due_days" => {"type" => "integer", "optional" => true},
24
+ "title" => {"type" => "string", "optional" => true},
25
+ "status" => {"type" => "string", "enum" => ["draft", "open", "closed"], "default" =>"draft", "optional" => true},
26
+ "payment_method" => {"type" => "string", "enum" => ["cash", "bank_transfer", "credit_card", "paypal", "direct_debit", "cheque"], "optional" => true},
27
+ "due_date" => {"type" => "string", "format" =>"date", "optional" => true},
28
+ "notes_before" => {"type" => "string", "optional" => true},
29
+ "notes_after" => {"type" => "string", "optional" => true},
30
+ "price_total" => {"type" => "number", "optional" => true, "readonly" => true},
31
+ "price_tax" => {"type" => "number", "optional" => true, "readonly" => true},
32
+ "created_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly"=> true},
33
+ "updated_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly"=> true},
34
+ "address_field" => {"type" => "string", "optional" => true, "readonly" => true},
35
+ "lock_version" => {"type" => "integer", "optional" => true, "readonly" => true},
36
+ "client_id" => {"type" => "string"},
37
+ "client" => {"type" => "object", "properties" => SKApi::Resources::Client.schema_props, "optional" => true, "readonly" => true},
38
+ "line_items" => {"type" => "array","properties" => SKApi::Resources::LineItem.schema_props, "optional" => true,},
39
+ }
40
+ end
41
+
42
+ def self.api_links
43
+ #internal links on fields=> id => salesking.eu/clients/4567.json
44
+ #external links to actions and related objects => invoeis => salesking.eu/clients/4567/invoices.json
45
+ [:edit, :destroy, :copy, :print, :show, :payments, :payment_new]
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ module SKApi
2
+ module Resources
3
+ class LineItem < SKApi::Resources::Base
4
+
5
+ def save
6
+ save_with_validation
7
+ end
8
+
9
+ def self.schema
10
+ { "type" => "object",
11
+ "properties" => SKApi::Resources::LineItem.schema_props}
12
+ end
13
+
14
+ def self.schema_props
15
+ {
16
+ "id" => {"type" => "string", "identity" => true , "readonly" => true},
17
+ "position" => {"type" => "integer"},
18
+ "product_id" => {"type" => "string", "optional" => true},
19
+ "quantity" => {"type" => "number"},
20
+ "quantity_unit" => {"type" => "string", "optional" => true},
21
+ "name" => {"type" => "string", "optional" => true},
22
+ "description" => {"type" => "string", "optional" => true},
23
+ "price_single" => {"type" => "number"},
24
+ "discount" => {"type" => "number", "optional" => true},
25
+ "tax" => {"type" => "number", "optional" => true},
26
+ "created_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly" => true},
27
+ "updated_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly" => true}
28
+ }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ module SKApi
2
+ module Resources
3
+ class Product < SKApi::Resources::Base
4
+
5
+ def save
6
+ # validate_schema
7
+ save_with_validation
8
+ end
9
+
10
+ # not realy stable yet
11
+ def validate_schema
12
+ json = self.to_json
13
+ obj = Rufus::Json.decode(json)
14
+ JSON::Schema.validate(obj, SKApi::Resources::Product.schema)
15
+ end
16
+
17
+ def self.schema
18
+ { "type" => "object",
19
+ "properties" => SKApi::Resources::Product.schema_props}
20
+ end
21
+
22
+ def self.schema_props
23
+ {
24
+ # category
25
+ # template_id
26
+ "id" => {"type" => "string", "identity" => true , "readonly" => true},
27
+ "number" => {"type" => "string", "optional" => true},
28
+ "name" => {"type" => "string"},
29
+ "description" => {"type" => "string", "optional" => true},
30
+ "price" => {"type" => "number"},
31
+ "tax" => {"type" => "number", "optional" => true},
32
+ "quantity_unit"=> {"type" => "string", "optional" => true},
33
+ "tag_list" => {"type" => "string", "optional" => true},
34
+ "lock_version" => {"type" => "integer", "readonly" => true, "optional" => true},
35
+ "published_at" => {"type" => "string", "format" =>"date", "optional" => true},
36
+ "created_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly" => true},
37
+ "updated_at" => {"type" => "string", "format" =>"date-time", "optional" => true, "readonly" => true},
38
+ }
39
+ end #schema_props
40
+
41
+ end #Product
42
+ end #Resources
43
+ end #SKApi
data/lib/sk_api.rb ADDED
@@ -0,0 +1,30 @@
1
+ # external
2
+ require 'active_resource'
3
+ unless RUBY_PLATFORM =~ /java/
4
+ require 'yajl'
5
+ require 'rufus-json'
6
+
7
+ Rufus::Json.backend = :yajl
8
+ else
9
+ require 'json'
10
+ require 'rufus-json'
11
+
12
+ Rufus::Json.backend = :json
13
+ end
14
+
15
+ #vendored
16
+ require File.dirname(__FILE__) + '/../vendor/jsonschema-1.0.0/lib/jsonschema'
17
+
18
+ # utilities
19
+ require File.dirname(__FILE__) + '/utils/field_map'
20
+ require File.dirname(__FILE__) + '/utils/serializer'
21
+
22
+ #resources
23
+ require File.dirname(__FILE__) + '/activeresource_patches/validations'
24
+ require File.dirname(__FILE__) + '/activeresource_patches/base'
25
+ require File.dirname(__FILE__) + '/resources/base'
26
+ require File.dirname(__FILE__) + '/resources/product'
27
+ require File.dirname(__FILE__) + '/resources/client'
28
+ require File.dirname(__FILE__) + '/resources/address'
29
+ require File.dirname(__FILE__) + '/resources/credit_note'
30
+ require File.dirname(__FILE__) + '/resources/line_item'
@@ -0,0 +1,114 @@
1
+ module SKApi
2
+ module Utils
3
+ # Provide methods for mapping the fields of a remote object to local object
4
+ # The class holds two objects (f.ex. a remote and a local object) and works
5
+ # with a hash which maps the fields between those two.
6
+ # If you use such a mapping both of your objects MUST respond to the method
7
+ # names passed in the mapping-table(hash)
8
+ #
9
+ # When an object is updated you can check the #log for changes
10
+ # ==== Example
11
+ #
12
+ # contact_map = {
13
+ # :loc_key => :name,
14
+ # :rem_key => :firstname,
15
+ # :obj => 'Stanza::SalesKing::Mapping::ContactTrans',
16
+ # :loc_trans=>:set_local_name,
17
+ # :rem_trans=> :set_remote_name
18
+ # }
19
+ # map = SKApi::Utils::FieldMap.new(@local_user, @remote_user, contact_map)
20
+ # map.update_remote #Does not save! only sets the field values on the remote object
21
+ #
22
+ # ==== Mapping Hash Explanation
23
+ #
24
+ # {
25
+ # :loc_key => :name, => Local fieldname #
26
+ # :rem_key => :firstname, => remote fieldname
27
+ # :obj => 'ATransitionClass', => The class which hold the following Transition methods as Class.methods
28
+ # :loc_trans=>:set_local_name, => Method called when local field is updated
29
+ # :rem_trans=> :set_remote_name => Method called when remote field is update
30
+ # }
31
+ class FieldMap
32
+
33
+ # The local object
34
+ attr_accessor :loc_obj
35
+ # The remote object
36
+ attr_accessor :rem_obj
37
+ # <Hash{Symbol=>Symbol, Symbol=>{Hash} }>::the field mapping
38
+ attr_accessor :fields
39
+ # the outdated fields
40
+ attr_reader :outdated
41
+ # <Array[String]>::log field changes
42
+ attr_reader :log
43
+
44
+ # Takes a local and remote object which should respond to function defined
45
+ # in the mapping hash
46
+ def initialize(loc_obj, rem_obj, fields)
47
+ @loc_obj = loc_obj
48
+ @rem_obj = rem_obj
49
+ @fields = fields
50
+ @log = []
51
+ end
52
+
53
+ # check if the any of the fields are outdated
54
+ # populates self.outdated array with outdated fields
55
+ # ==== Returns
56
+ # <Boolean>:: false if not outdated
57
+ def outdated?
58
+ @outdated = []
59
+ fields.each do |fld|
60
+ if fld[:trans]
61
+ # SomeTransferObject.remote_tranfer_function(remote_obj_data)
62
+ virtual_local_val = fld[:trans][:obj].constantize.send( fld[:trans][:rem_trans], loc_obj.send( fld[:loc_key] ) )
63
+ @outdated << fld if virtual_local_val != rem_obj.send( fld[:rem_key] )
64
+ else
65
+ @outdated << fld if rem_obj.send( fld[:rem_key] ) != loc_obj.send( fld[:loc_key] )
66
+ end
67
+ end
68
+ !@outdated.empty?
69
+ end
70
+
71
+ # update all local outdated fields wit hvcalues from remote object
72
+ def update_local_outdated
73
+ update_local(@outdated) if outdated?
74
+ end
75
+ # update all remote outdated fields wit hvalues from local object
76
+ def update_remote_outdated
77
+ update_remote(@outdated) if outdated?
78
+ end
79
+
80
+ # update all local fields with values from remote
81
+ def update_local(field=nil)
82
+ flds = field ? ( field.is_a?(Array) ? field : [field] ) : fields
83
+ flds.each do |fld|
84
+ old_val = loc_obj.send(fld[:loc_key]) rescue 'empty'
85
+ new_val = if fld[:trans] #call transfer function
86
+ fld[:trans][:obj].constantize.send( fld[:trans][:loc_trans], rem_object.send( fld[:rem_key] ) )
87
+ else # lookup directly on local object
88
+ rem_obj.send(fld[:rem_key])
89
+ end
90
+ loc_obj.send( "#{fld[:loc_key]}=", new_val )
91
+ # write to log
92
+ log << "local: #{fld[:loc_key]} was: #{old_val} updated from remote: #{fld[:rem_key]} with value: #{new_val}"
93
+ end
94
+ end
95
+
96
+ # Update all or given remote fields with the value of the local fields
97
+ #
98
+ def update_remote(field=nil)
99
+ flds = field ? ( field.is_a?(Array) ? field : [field] ) : fields
100
+ flds.each do |fld|
101
+ old_val = rem_obj.send(fld[:rem_key]) rescue 'empty'# rember for log
102
+ new_val = if fld[:trans] #call transfer function
103
+ fld[:trans][:obj].constantize.send( fld[:trans][:rem_trans], loc_obj.send( fld[:loc_key] ) )
104
+ else # lookup directly on local object
105
+ loc_obj.send( fld[:loc_key] )
106
+ end
107
+ rem_obj.send( "#{fld[:rem_key]}=" , new_val )
108
+ log << "remote: #{fld[:rem_key]} was: #{old_val} updated from local: #{fld[:loc_key]} with value: #{new_val}"
109
+ end
110
+ end
111
+
112
+ end # FieldMapping
113
+ end # Utils
114
+ end #SKApi