rubysync 0.0.3 → 0.0.4
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/HISTORY.txt +4 -0
- data/Manifest.txt +25 -12
- data/README.txt +0 -2
- data/bin/rubysync +20 -6
- data/bin/rubysync.rb +333 -0
- data/docs/in_pipeline.graffle +2690 -0
- data/docs/init_openldap.ldif +11 -0
- data/docs/out_pipeline.graffle +3274 -0
- data/docs/schema/99rubysync.ldif +27 -0
- data/docs/schema/rubysync.schema +16 -0
- data/docs/to_sync.txt +15 -0
- data/docs/walkthru.txt +186 -0
- data/lib/ruby_sync.rb +7 -29
- data/lib/ruby_sync/connectors/base_connector.rb +55 -86
- data/lib/ruby_sync/connectors/csv_file_connector.rb +16 -4
- data/lib/ruby_sync/connectors/ldap_associations.rb +126 -0
- data/lib/ruby_sync/connectors/ldap_changelog_connector.rb +127 -0
- data/lib/ruby_sync/connectors/ldap_connector.rb +29 -192
- data/lib/ruby_sync/connectors/memory_connector.rb +1 -1
- data/lib/ruby_sync/connectors/xml_connector.rb +105 -32
- data/lib/ruby_sync/event.rb +40 -12
- data/lib/ruby_sync/operation.rb +18 -2
- data/lib/ruby_sync/pipelines/base_pipeline.rb +44 -6
- data/lib/ruby_sync/util/utilities.rb +97 -4
- data/lib/rubysync.rb +1 -1
- data/rubysync.tmproj +279 -59
- data/test/.LCKts_rubysync.rb~ +1 -0
- data/test/ruby_sync_test.rb +9 -4
- data/test/{test_active_record_vault.rb → tc_active_record_connector.rb} +11 -7
- data/test/{test_base_connector.rb → tc_base_connector.rb} +1 -1
- data/test/{test_base_pipeline.rb → tc_base_pipeline.rb} +1 -1
- data/test/tc_changelog_ldap_connector.rb +93 -0
- data/test/{test_csv_file_connector.rb → tc_csv_file_connector.rb} +14 -5
- data/test/{test_event.rb → tc_event.rb} +1 -1
- data/test/{test_ldap_changelog.rb → tc_ldap_changelog.rb} +1 -1
- data/test/{test_ldap_connector.rb → tc_ldap_connector.rb} +20 -22
- data/test/{test_ldap_vault.rb → tc_ldap_vault.rb} +2 -2
- data/test/{test_ldif.rb → tc_ldif.rb} +1 -1
- data/test/{test_memory_connectors.rb → tc_memory_connectors.rb} +10 -6
- data/test/{test_rubysync.rb → tc_rubysync.rb} +4 -4
- data/test/tc_transformation.rb +71 -0
- data/test/{test_utilities.rb → tc_utilities.rb} +28 -1
- data/test/tc_xml_connectors.rb +107 -6
- data/test/ts_rubysync.rb +11 -6
- metadata +33 -28
@@ -0,0 +1,27 @@
|
|
1
|
+
#
|
2
|
+
################################################################################
|
3
|
+
#
|
4
|
+
dn: cn=schema
|
5
|
+
#
|
6
|
+
################################################################################
|
7
|
+
#
|
8
|
+
attributeTypes: (
|
9
|
+
1.3.6.1.4.1.28955.50.1.1
|
10
|
+
NAME 'RubySyncAssociation'
|
11
|
+
DESC 'Context:Key provided by connected system'
|
12
|
+
EQUALITY caseExactMatch
|
13
|
+
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
14
|
+
)
|
15
|
+
#
|
16
|
+
################################################################################
|
17
|
+
#
|
18
|
+
objectClasses: (
|
19
|
+
1.3.6.1.4.1.28955.50.2.1
|
20
|
+
NAME 'RubySyncSynchable'
|
21
|
+
DESC 'Object can preserve links to other objects via RubySync'
|
22
|
+
AUXILIARY
|
23
|
+
MAY RubySyncAssociation
|
24
|
+
)
|
25
|
+
#
|
26
|
+
################################################################################
|
27
|
+
#
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# rubysync
|
2
|
+
# Generated By LDAPStudio on : Mar 10, 2007 10:49:43 PM
|
3
|
+
|
4
|
+
attributetype ( 1.3.6.1.4.1.28955.1.1.1
|
5
|
+
NAME 'RubySyncAssociation'
|
6
|
+
DESC 'Context:Key provided by connected system'
|
7
|
+
EQUALITY caseExactMatch
|
8
|
+
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
9
|
+
)
|
10
|
+
|
11
|
+
objectclass ( 1.3.6.1.4.1.28955.1.2.1 NAME 'RubySyncSynchable'
|
12
|
+
DESC 'Object can preserve links to other objects via RubySync'
|
13
|
+
AUXILIARY
|
14
|
+
MAY RubySyncAssociation )
|
15
|
+
|
16
|
+
|
data/docs/to_sync.txt
ADDED
data/docs/walkthru.txt
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
$ mkdir x
|
2
|
+
$ cd x
|
3
|
+
$ rubysync create kaos
|
4
|
+
$ ls -l
|
5
|
+
total 0
|
6
|
+
drwxr-xr-x 7 ritchiey ritchiey 238 Aug 21 16:47 kaos
|
7
|
+
$ cd kaos
|
8
|
+
$ ls -l
|
9
|
+
total 0
|
10
|
+
drwxr-xr-x 2 ritchiey ritchiey 68 Aug 21 16:47 connectors
|
11
|
+
drwxr-xr-x 2 ritchiey ritchiey 68 Aug 21 16:47 db
|
12
|
+
drwxr-xr-x 2 ritchiey ritchiey 68 Aug 21 16:47 log
|
13
|
+
drwxr-xr-x 2 ritchiey ritchiey 68 Aug 21 16:47 pipelines
|
14
|
+
drwxr-xr-x 5 ritchiey ritchiey 170 Aug 21 16:47 shared
|
15
|
+
$ rubysync connector hr -t csv_file
|
16
|
+
$ rubysync connector kaos_vault -t xml$ mate ..
|
17
|
+
$ cat connectors/hr_connector.rb
|
18
|
+
class HrConnector < RubySync::Connectors::CsvFileConnector
|
19
|
+
|
20
|
+
field_names ['id', 'first_name', 'last_name', 'skills']
|
21
|
+
path_field 'id'
|
22
|
+
in_path '/Users/ritchiey/x/in'
|
23
|
+
out_path '/Users/ritchiey/x/out'
|
24
|
+
in_glob '*.csv'
|
25
|
+
out_extension '.csv'
|
26
|
+
|
27
|
+
end
|
28
|
+
$ cat connectors/kaos_vault_connector.rb
|
29
|
+
class KaosVaultConnector < RubySync::Connectors::XmlConnector
|
30
|
+
#
|
31
|
+
# "filename" should be the full name of the file containing
|
32
|
+
# the xml representation of the synchronized content.
|
33
|
+
# You probably want to change this:
|
34
|
+
#
|
35
|
+
filename "/Users/ritchiey/x/kaos.xml"
|
36
|
+
|
37
|
+
end
|
38
|
+
$ rubysync fields hr
|
39
|
+
id
|
40
|
+
first_name
|
41
|
+
last_name
|
42
|
+
skills
|
43
|
+
$ rubysync fields kaos_vault
|
44
|
+
|
45
|
+
$ rubysync pipeline hr_import -C hr -V kaos_vault
|
46
|
+
$ # Now edit the pipeline config
|
47
|
+
$ # Actually, here's the pipeline before editing
|
48
|
+
$ cat pipelines/hr_import_pipeline.rb
|
49
|
+
class HrImportPipeline < RubySync::Pipelines::BasePipeline
|
50
|
+
|
51
|
+
client :hr
|
52
|
+
|
53
|
+
vault :kaos_vault
|
54
|
+
|
55
|
+
# Remove any fields that you don't want to set in the client from the vault
|
56
|
+
allow_out :allow, :these, :fields, :through
|
57
|
+
|
58
|
+
# Remove any fields that you don't want to set in the vault from the client
|
59
|
+
allow_in :allow, :these, :fields, :through
|
60
|
+
|
61
|
+
# If the client and vault have different names for the same field, define the
|
62
|
+
# the mapping here. For example, if the vault has a field called "first name" and
|
63
|
+
# the client has a field called givenName you may put:
|
64
|
+
# 'first name' => 'givenName'
|
65
|
+
# separate each mapping with a comma.
|
66
|
+
# The following fields were detected on the client:
|
67
|
+
# 'id', 'first_name', 'last_name', 'skills'
|
68
|
+
map_vault_to_client({
|
69
|
+
#'allow' => 'a_client_field',
|
70
|
+
#'these' => 'a_client_field',
|
71
|
+
#'fields' => 'a_client_field',
|
72
|
+
#'through' => 'a_client_field'
|
73
|
+
})
|
74
|
+
|
75
|
+
# "in" means going from client to vault
|
76
|
+
#in_transform do
|
77
|
+
#end
|
78
|
+
|
79
|
+
# "out" means going from vault to client
|
80
|
+
#out_transform do
|
81
|
+
#end
|
82
|
+
|
83
|
+
end
|
84
|
+
$ # now edit the pipeline
|
85
|
+
$ cat pipelines/hr_import_pipeline.rb
|
86
|
+
class HrImportPipeline < RubySync::Pipelines::BasePipeline
|
87
|
+
|
88
|
+
client :hr
|
89
|
+
|
90
|
+
vault :kaos_vault
|
91
|
+
|
92
|
+
# Remove any fields that you don't want to set in the client from the vault
|
93
|
+
allow_out :id, :first_name, :last_name
|
94
|
+
|
95
|
+
# Remove any fields that you don't want to set in the vault from the client
|
96
|
+
allow_in :id, :first_name, :last_name
|
97
|
+
|
98
|
+
# If the client and vault have different names for the same field, define the
|
99
|
+
# the mapping here. For example, if the vault has a field called "first name" and
|
100
|
+
# the client has a field called givenName you may put:
|
101
|
+
# 'first name' => 'givenName'
|
102
|
+
# separate each mapping with a comma.
|
103
|
+
# The following fields were detected on the client:
|
104
|
+
# 'id', 'first_name', 'last_name', 'skills'
|
105
|
+
map_vault_to_client({
|
106
|
+
#'allow' => 'a_client_field',
|
107
|
+
#'these' => 'a_client_field',
|
108
|
+
#'fields' => 'a_client_field',
|
109
|
+
#'through' => 'a_client_field'
|
110
|
+
})
|
111
|
+
|
112
|
+
# "in" means going from client to vault
|
113
|
+
#in_transform do
|
114
|
+
#end
|
115
|
+
|
116
|
+
# "out" means going from vault to client
|
117
|
+
#out_transform do
|
118
|
+
#end
|
119
|
+
|
120
|
+
end
|
121
|
+
$ # so all we've done specified which fields to allow in an out. Notice that we left "skills" out.
|
122
|
+
$ # also note that we're exporting as well as importing for now
|
123
|
+
$ # so now we can run our sync
|
124
|
+
$ rubysync once hr_import
|
125
|
+
$ # oops. Forgot to give it any data
|
126
|
+
$ # notice, though how it created the import and export directories for us.
|
127
|
+
$ cd ../in
|
128
|
+
$ # lets make some henchmen
|
129
|
+
$ cat > henchmen.csv
|
130
|
+
bobby,BareKnuckle,Bobby,pugilism:yoga
|
131
|
+
tt,Testy,Terry,kidnapping:interrogation:juggling
|
132
|
+
$ ls -l
|
133
|
+
total 8
|
134
|
+
-rw-r--r-- 1 ritchiey ritchiey 87 Aug 21 17:11 henchmen.csv
|
135
|
+
$ # we need to run rubysync from within the configuration directory
|
136
|
+
$ cd ../kaos/
|
137
|
+
$ rubysync once hr_import
|
138
|
+
$ ls -l ../in
|
139
|
+
total 8
|
140
|
+
-rw-r--r-- 1 ritchiey ritchiey 87 Aug 21 17:11 henchmen.csv.bak
|
141
|
+
$ # note that the csv has been renamed
|
142
|
+
$ # lets have a look at the xml file thats been created
|
143
|
+
$ cat ../kaos.xml
|
144
|
+
<opt>
|
145
|
+
<tt>
|
146
|
+
<id>tt</id>
|
147
|
+
<first_name>Testy</first_name>
|
148
|
+
<last_name>Terry</last_name>
|
149
|
+
</tt>
|
150
|
+
<bobby>
|
151
|
+
<id>bobby</id>
|
152
|
+
<first_name>BareKnuckle</first_name>
|
153
|
+
<last_name>Bobby</last_name>
|
154
|
+
</bobby>
|
155
|
+
</opt>
|
156
|
+
$ # ok, great. We've got a couple of records in there.
|
157
|
+
$ # remember that we also configured an output directory
|
158
|
+
$ ls -l ../out
|
159
|
+
$ # no output. ok what if we modify the vault itself
|
160
|
+
$ cat ../kaos.xml
|
161
|
+
<opt>
|
162
|
+
<tt>
|
163
|
+
<id>tt</id>
|
164
|
+
<first_name>Testy</first_name>
|
165
|
+
<last_name>Terry</last_name>
|
166
|
+
</tt>
|
167
|
+
<bobby>
|
168
|
+
<id>bobby</id>
|
169
|
+
<first_name>BareKnuckle</first_name>
|
170
|
+
<last_name>Bobby</last_name>
|
171
|
+
</bobby>
|
172
|
+
<desd>
|
173
|
+
<id>desd</id>
|
174
|
+
<first_name>Dangerous</first_name>
|
175
|
+
<last_name>Des</last_name>
|
176
|
+
</desd>
|
177
|
+
</opt>
|
178
|
+
$ # and run it again
|
179
|
+
$ rubysync once hr_import
|
180
|
+
$ ls -l ../out
|
181
|
+
total 8
|
182
|
+
-rw-r--r-- 1 ritchiey ritchiey 20 Aug 21 17:25 20070821172521.csv
|
183
|
+
$ cat ../out/20070821172521.csv
|
184
|
+
desd,Dangerous,Des,
|
185
|
+
$ # So it just sends through the changes to the file
|
186
|
+
$ # How's it doingr
|
data/lib/ruby_sync.rb
CHANGED
@@ -21,28 +21,9 @@ require 'rubygems'
|
|
21
21
|
require 'active_support'
|
22
22
|
require 'ruby_sync/util/utilities'
|
23
23
|
require 'ruby_sync/util/metaid'
|
24
|
-
#require 'ruby_sync/connectors/base_connector'
|
25
|
-
#require 'ruby_sync/pipelines/base_pipeline'
|
26
24
|
require 'ruby_sync/operation'
|
27
25
|
require 'ruby_sync/event'
|
28
26
|
|
29
|
-
class Object
|
30
|
-
|
31
|
-
# If not already an array, slip into one
|
32
|
-
def as_array
|
33
|
-
(instance_of? Array)? self : [self]
|
34
|
-
end
|
35
|
-
|
36
|
-
# Make the log method globally available
|
37
|
-
def log
|
38
|
-
unless defined? @@log
|
39
|
-
@@log = Logger.new(STDOUT)
|
40
|
-
#@@log.level = Logger::DEBUG
|
41
|
-
@@log.datetime_format = "%H:%M:%S"
|
42
|
-
end
|
43
|
-
@@log
|
44
|
-
end
|
45
|
-
end
|
46
27
|
|
47
28
|
class Module
|
48
29
|
# Add an option that will be defined by a class method, stored in a class variable
|
@@ -66,17 +47,17 @@ class Configuration
|
|
66
47
|
include RubySync::Utilities
|
67
48
|
|
68
49
|
def initialize
|
69
|
-
include_in_search_path "#{base_path}/pipelines"
|
70
|
-
include_in_search_path "#{base_path}/connectors"
|
71
|
-
include_in_search_path "#{base_path}/shared/connectors"
|
72
|
-
include_in_search_path "#{base_path}/shared/pipelines"
|
73
|
-
include_in_search_path "#{base_path}/shared/lib"
|
50
|
+
base_path and include_in_search_path "#{base_path}/pipelines"
|
51
|
+
base_path and include_in_search_path "#{base_path}/connectors"
|
52
|
+
base_path and include_in_search_path "#{base_path}/shared/connectors"
|
53
|
+
base_path and include_in_search_path "#{base_path}/shared/pipelines"
|
54
|
+
base_path and include_in_search_path "#{base_path}/shared/lib"
|
74
55
|
|
75
56
|
lib_path = File.dirname(__FILE__)
|
76
57
|
require_all_in_dir "#{lib_path}/ruby_sync/connectors", "*_connector.rb"
|
77
|
-
require_all_in_dir "#{base_path}/shared/connectors", "*_connector.rb"
|
58
|
+
base_path and require_all_in_dir "#{base_path}/shared/connectors", "*_connector.rb"
|
78
59
|
require_all_in_dir "#{lib_path}/ruby_sync/pipelines", "*_pipeline.rb"
|
79
|
-
require_all_in_dir "#{base_path}/shared/pipelines", "*_pipeline.rb"
|
60
|
+
base_path and require_all_in_dir "#{base_path}/shared/pipelines", "*_pipeline.rb"
|
80
61
|
end
|
81
62
|
|
82
63
|
# We find the first directory in the search path that is a parent of the specified
|
@@ -108,6 +89,3 @@ class Configuration
|
|
108
89
|
end
|
109
90
|
|
110
91
|
Configuration.new
|
111
|
-
|
112
|
-
|
113
|
-
|
@@ -14,7 +14,8 @@
|
|
14
14
|
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
15
15
|
|
16
16
|
require 'ruby_sync/connectors/connector_event_processing'
|
17
|
-
require '
|
17
|
+
require 'yaml'
|
18
|
+
require 'yaml/dbm'
|
18
19
|
require 'digest/md5'
|
19
20
|
|
20
21
|
module RubySync::Connectors
|
@@ -23,11 +24,15 @@ module RubySync::Connectors
|
|
23
24
|
include RubySync::Utilities
|
24
25
|
include ConnectorEventProcessing
|
25
26
|
|
26
|
-
attr_accessor :once_only, :name, :is_vault
|
27
|
+
attr_accessor :once_only, :name, :is_vault, :pipeline
|
27
28
|
option :dbm_path
|
28
29
|
|
29
30
|
# set a default dbm path
|
30
|
-
def dbm_path()
|
31
|
+
def dbm_path()
|
32
|
+
p = "#{base_path}/db"
|
33
|
+
::FileUtils.mkdir_p p
|
34
|
+
::File.join(p,name)
|
35
|
+
end
|
31
36
|
|
32
37
|
# Stores association keys indexed by path:association_context
|
33
38
|
def path_to_association_dbm_filename
|
@@ -53,7 +58,7 @@ module RubySync::Connectors
|
|
53
58
|
self.name = options[:name]
|
54
59
|
self.is_vault = options[:is_vault]
|
55
60
|
if is_vault && !can_act_as_vault?
|
56
|
-
raise
|
61
|
+
raise "#{self.class.name} can't act as an identity vault."
|
57
62
|
end
|
58
63
|
options.each do |key, value|
|
59
64
|
if self.respond_to? "#{key}="
|
@@ -64,16 +69,20 @@ module RubySync::Connectors
|
|
64
69
|
end
|
65
70
|
end
|
66
71
|
|
72
|
+
|
73
|
+
# Subclasses must override this. Called by perform_add to actually
|
74
|
+
# store the new record in the datastore. Returned value will be used
|
75
|
+
# as the association id if this connector is acting as the client.
|
76
|
+
def add id, operations
|
77
|
+
raise "add method not implemented"
|
78
|
+
end
|
79
|
+
|
67
80
|
|
68
81
|
# Override this to return a string that will be included within the class definition of
|
69
82
|
# of configurations based on your connector.
|
70
83
|
def self.sample_config
|
71
84
|
end
|
72
85
|
|
73
|
-
# Override this to perform actions that must be performed the
|
74
|
-
# when the connector starts running. (Eg, opening network connections)
|
75
|
-
def started
|
76
|
-
end
|
77
86
|
|
78
87
|
# Subclasses must override this to
|
79
88
|
# interface with the external system and generate entries for every
|
@@ -107,31 +116,32 @@ module RubySync::Connectors
|
|
107
116
|
unless self[key]
|
108
117
|
yield RubySync::Event.delete(self, key)
|
109
118
|
dbm.delete key
|
119
|
+
if is_vault? and @pipeline
|
120
|
+
association = association_for @pipeline.association_context, key
|
121
|
+
remove_association association
|
122
|
+
end
|
110
123
|
end
|
111
124
|
end
|
112
125
|
end
|
113
126
|
end
|
114
127
|
|
115
128
|
def digest(o)
|
116
|
-
Digest::MD5.hexdigest(
|
129
|
+
Digest::MD5.hexdigest(o.to_yaml)
|
117
130
|
end
|
118
131
|
|
119
|
-
# Override this to perform actions that must be performed when
|
120
|
-
# the connector exits (eg closing network conections).
|
121
|
-
def stopped; end
|
122
132
|
|
123
133
|
|
124
134
|
# Call each_change repeatedly (or once if in once_only mode)
|
125
135
|
# to generate events.
|
126
136
|
# Should generally only be called by the pipeline to which it is attached.
|
127
137
|
def start &blk
|
128
|
-
log.
|
138
|
+
log.debug "#{name}: Started"
|
129
139
|
@running = true
|
130
|
-
|
140
|
+
sync_started()
|
131
141
|
while @running
|
132
142
|
each_change do |event|
|
133
143
|
if event.type == :force_resync
|
134
|
-
each_entry
|
144
|
+
each_entry(&blk)
|
135
145
|
next
|
136
146
|
end
|
137
147
|
if is_delete_echo?(event) || is_echo?(event)
|
@@ -150,9 +160,26 @@ module RubySync::Connectors
|
|
150
160
|
sleep 1
|
151
161
|
end
|
152
162
|
end
|
153
|
-
|
163
|
+
sync_stopped
|
154
164
|
end
|
155
165
|
|
166
|
+
|
167
|
+
# Called by start() after last call to each_change or each_entry
|
168
|
+
def sync_stopped; end
|
169
|
+
|
170
|
+
# Called by start() before first call to each_change or each_entry
|
171
|
+
def sync_started; end
|
172
|
+
|
173
|
+
# Override this to perform actions that must be performed the
|
174
|
+
# when the connector starts running. (Eg, opening network connections)
|
175
|
+
def started
|
176
|
+
end
|
177
|
+
|
178
|
+
# Override this to perform actions that must be performed when
|
179
|
+
# the connector exits (eg closing network conections).
|
180
|
+
def stopped; end
|
181
|
+
|
182
|
+
|
156
183
|
# Politely stop the connector.
|
157
184
|
def stop
|
158
185
|
log.info "#{name}: Attempting to stop"
|
@@ -198,12 +225,6 @@ module RubySync::Connectors
|
|
198
225
|
# Whether this connector is capable of acting as a vault.
|
199
226
|
# The vault is responsible for storing the association key of the client application
|
200
227
|
# and must be able to retrieve records for that association key.
|
201
|
-
# Typically, databases and directories can act as vaults, text documents and HR or finance
|
202
|
-
# applications probably can't.
|
203
|
-
# To enable a connector to act as a vault, define the following methods:
|
204
|
-
# => path_for_foreign_key(pipeline_id, key)
|
205
|
-
# => foreign_key_for(path)
|
206
|
-
# and associate_with_foreign_key(key, path).
|
207
228
|
def can_act_as_vault?
|
208
229
|
defined? associate and
|
209
230
|
defined? path_for_association and
|
@@ -215,11 +236,10 @@ module RubySync::Connectors
|
|
215
236
|
|
216
237
|
# Store association for the given path
|
217
238
|
def associate association, path
|
218
|
-
DBM.open(path_to_association_dbm_filename) do |dbm|
|
219
|
-
|
220
|
-
assocs =
|
221
|
-
|
222
|
-
dbm[path.to_s] = Marshal.dump(assocs)
|
239
|
+
YAML::DBM.open(path_to_association_dbm_filename) do |dbm|
|
240
|
+
assocs = dbm[path.to_s] || {}
|
241
|
+
assocs[association.context.to_s] = association.key.to_s
|
242
|
+
dbm[path.to_s] = assocs
|
223
243
|
end
|
224
244
|
DBM.open(association_to_path_dbm_filename) do |dbm|
|
225
245
|
dbm[association.to_s] = path
|
@@ -233,45 +253,28 @@ module RubySync::Connectors
|
|
233
253
|
end
|
234
254
|
end
|
235
255
|
|
236
|
-
# Default implementation does nothing
|
237
256
|
def associations_for path
|
238
|
-
DBM.open(path_to_association_dbm_filename) do |dbm|
|
239
|
-
|
240
|
-
assocs = (assocs_string)? Marshal.load(assocs_string) : {}
|
257
|
+
YAML::DBM.open(path_to_association_dbm_filename) do |dbm|
|
258
|
+
assocs = dbm[path.to_s]
|
241
259
|
assocs.values
|
242
260
|
end
|
243
261
|
end
|
244
262
|
|
245
|
-
|
263
|
+
|
246
264
|
def remove_association association
|
247
265
|
path = nil
|
248
266
|
DBM.open(association_to_path_dbm_filename) do |dbm|
|
249
267
|
return unless path =dbm.delete(association.to_s)
|
250
268
|
end
|
251
|
-
DBM.open(path_to_association_dbm_filename) do |dbm|
|
252
|
-
|
253
|
-
assocs
|
254
|
-
assocs.delete(association.context) and dbm[path.to_s] = Marshal.dump(assocs)
|
269
|
+
YAML::DBM.open(path_to_association_dbm_filename) do |dbm|
|
270
|
+
assocs = dbm[path.to_s]
|
271
|
+
assocs.delete(association.context) and dbm[path.to_s] = assocs
|
255
272
|
end
|
256
273
|
end
|
257
274
|
|
258
|
-
# Could be more efficient for the default case where the
|
259
|
-
# associations are actually stored as a serialized hash but
|
260
|
-
# then it wouldn't be as generic and other implementations would
|
261
|
-
# have to reimplement it.
|
262
|
-
# def association_key_for context, path
|
263
|
-
# raise "#{name} is not a vault." unless is_vault?
|
264
|
-
# associations_for(path).each do |assoc|
|
265
|
-
# (c, key) = assoc.split(RubySync::Association.delimiter, 2)
|
266
|
-
# return key if c == context
|
267
|
-
# end
|
268
|
-
# return nil
|
269
|
-
# end
|
270
|
-
|
271
275
|
def association_key_for context, path
|
272
|
-
DBM.open(path_to_association_dbm_filename) do |dbm|
|
273
|
-
|
274
|
-
assocs = (assocs_string)? Marshal.load(assocs_string) : {}
|
276
|
+
YAML::DBM.open(path_to_association_dbm_filename) do |dbm|
|
277
|
+
assocs = dbm[path.to_s] || {}
|
275
278
|
assocs[context.to_s]
|
276
279
|
end
|
277
280
|
end
|
@@ -356,40 +359,6 @@ module RubySync::Connectors
|
|
356
359
|
end
|
357
360
|
|
358
361
|
|
359
|
-
# Performs the given operations on the given record. The record is a
|
360
|
-
# Hash in which each key is a field name and each value is an array of
|
361
|
-
# values for that field.
|
362
|
-
# Operations is an Array of RubySync::Operation objects to be performed on the record.
|
363
|
-
def perform_operations operations, record={}
|
364
|
-
operations.each do |op|
|
365
|
-
unless op.instance_of? RubySync::Operation
|
366
|
-
log.warn "!!!!!!!!!! PROBLEM, DUMP FOLLOWS: !!!!!!!!!!!!!!"
|
367
|
-
p op
|
368
|
-
end
|
369
|
-
case op.type
|
370
|
-
when :add
|
371
|
-
if record[op.subject]
|
372
|
-
existing = record[op.subject].as_array
|
373
|
-
(existing & op.values).empty? or
|
374
|
-
raise Exception.new("Attempt to add duplicate elements to #{name}")
|
375
|
-
record[op.subject] = existing + op.values
|
376
|
-
else
|
377
|
-
record[op.subject] = op.values
|
378
|
-
end
|
379
|
-
when :replace
|
380
|
-
record[op.subject] = op.values
|
381
|
-
when :delete
|
382
|
-
if value == nil || value == "" || value == []
|
383
|
-
record.delete(op.subject)
|
384
|
-
else
|
385
|
-
record[op.subject] -= values
|
386
|
-
end
|
387
|
-
else
|
388
|
-
raise Exception.new("Unknown operation '#{op.type}'")
|
389
|
-
end
|
390
|
-
end
|
391
|
-
return record
|
392
|
-
end
|
393
362
|
|
394
363
|
|
395
364
|
# Return an array of possible fields for this connector.
|