rubysync 0.0.5 → 0.1.0

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.
Files changed (117) hide show
  1. data/HISTORY.txt +7 -0
  2. data/Manifest.txt +18 -76
  3. data/Rakefile +2 -2
  4. data/bin/rubysync +60 -21
  5. data/bin/rubysync.rb +60 -21
  6. data/examples/ar_webapp/public/dispatch.cgi +1 -1
  7. data/examples/ar_webapp/public/dispatch.fcgi +1 -1
  8. data/examples/ar_webapp/public/dispatch.rb +1 -1
  9. data/examples/csv_to_ldap/config/connectors/hr_connector.rb +14 -0
  10. data/examples/csv_to_ldap/config/connectors/ldap_vault_connector.rb +11 -0
  11. data/examples/{ar_webapp/.DS_Store → csv_to_ldap/config/db/HrImportPipeline(vault)_assoc_to_path.db} +0 -0
  12. data/{.DS_Store → examples/csv_to_ldap/config/db/HrImportPipeline(vault)_mirror.db} +0 -0
  13. data/examples/csv_to_ldap/config/db/HrImportPipeline(vault)_path_to_assoc.db +0 -0
  14. data/examples/csv_to_ldap/config/pipelines/hr_import_pipeline.rb +24 -0
  15. data/examples/csv_to_ldap/in/henchmen.csv.bak +3 -0
  16. data/examples/csv_to_xml/config/connectors/databank_connector.rb +8 -0
  17. data/examples/csv_to_xml/config/connectors/hr_connector.rb +14 -0
  18. data/examples/csv_to_xml/config/db/HrImportPipeline(vault)_assoc_to_path.db +0 -0
  19. data/examples/csv_to_xml/config/db/HrImportPipeline(vault)_mirror.db +0 -0
  20. data/examples/csv_to_xml/config/db/HrImportPipeline(vault)_path_to_assoc.db +0 -0
  21. data/examples/csv_to_xml/config/pipelines/hr_import_pipeline.rb +24 -0
  22. data/examples/csv_to_xml/databank.xml +1 -0
  23. data/examples/csv_to_xml/in/henchmen.csv.bak +3 -0
  24. data/examples/csv_to_xml/transcript.txt +1 -2
  25. data/examples/data/henchmen.csv +3 -0
  26. data/examples/data/more.csv +2 -0
  27. data/lib/ruby_sync/connectors/active_record_connector.rb +19 -4
  28. data/lib/ruby_sync/connectors/base_connector.rb +3 -9
  29. data/lib/ruby_sync/connectors/file_connector.rb +2 -2
  30. data/lib/ruby_sync/event.rb +16 -9
  31. data/lib/ruby_sync/pipelines/base_pipeline.rb +137 -164
  32. data/lib/ruby_sync/util/utilities.rb +14 -21
  33. data/lib/ruby_sync.rb +37 -39
  34. data/test/ruby_sync_test.rb +3 -2
  35. data/test/tc_active_record_connector.rb +15 -6
  36. data/test/tc_csv_file_connector.rb +10 -6
  37. data/test/tc_ldap_connector.rb +1 -1
  38. data/test/tc_memory_connectors.rb +5 -3
  39. data/test/tc_transformation.rb +15 -7
  40. data/test/tc_utilities.rb +1 -1
  41. data/test/tc_xml_connectors.rb +4 -0
  42. data/test/ts_rubysync.rb +3 -1
  43. metadata +21 -81
  44. data/.project +0 -17
  45. data/docs/in_pipeline.graffle +0 -2690
  46. data/docs/out_pipeline.graffle +0 -3274
  47. data/docs/to_sync.txt +0 -15
  48. data/docs/walkthru.txt +0 -186
  49. data/examples/ar_client_webapp/README +0 -182
  50. data/examples/ar_client_webapp/Rakefile +0 -10
  51. data/examples/ar_client_webapp/app/controllers/application.rb +0 -7
  52. data/examples/ar_client_webapp/app/controllers/user_controller.rb +0 -5
  53. data/examples/ar_client_webapp/app/helpers/application_helper.rb +0 -3
  54. data/examples/ar_client_webapp/app/helpers/user_helper.rb +0 -2
  55. data/examples/ar_client_webapp/app/models/user.rb +0 -2
  56. data/examples/ar_client_webapp/config/boot.rb +0 -45
  57. data/examples/ar_client_webapp/config/database.yml +0 -36
  58. data/examples/ar_client_webapp/config/environment.rb +0 -60
  59. data/examples/ar_client_webapp/config/environments/development.rb +0 -21
  60. data/examples/ar_client_webapp/config/environments/production.rb +0 -18
  61. data/examples/ar_client_webapp/config/environments/test.rb +0 -19
  62. data/examples/ar_client_webapp/config/routes.rb +0 -23
  63. data/examples/ar_client_webapp/db/migrate/001_create_users.rb +0 -13
  64. data/examples/ar_client_webapp/db/schema.rb +0 -13
  65. data/examples/ar_client_webapp/doc/README_FOR_APP +0 -2
  66. data/examples/ar_client_webapp/log/development.log +0 -753
  67. data/examples/ar_client_webapp/log/production.log +0 -0
  68. data/examples/ar_client_webapp/log/server.log +0 -0
  69. data/examples/ar_client_webapp/log/test.log +0 -0
  70. data/examples/ar_client_webapp/public/.htaccess +0 -40
  71. data/examples/ar_client_webapp/public/404.html +0 -30
  72. data/examples/ar_client_webapp/public/500.html +0 -30
  73. data/examples/ar_client_webapp/public/dispatch.cgi +0 -10
  74. data/examples/ar_client_webapp/public/dispatch.fcgi +0 -24
  75. data/examples/ar_client_webapp/public/dispatch.rb +0 -10
  76. data/examples/ar_client_webapp/public/favicon.ico +0 -0
  77. data/examples/ar_client_webapp/public/images/rails.png +0 -0
  78. data/examples/ar_client_webapp/public/index.html +0 -277
  79. data/examples/ar_client_webapp/public/javascripts/application.js +0 -2
  80. data/examples/ar_client_webapp/public/javascripts/controls.js +0 -833
  81. data/examples/ar_client_webapp/public/javascripts/dragdrop.js +0 -942
  82. data/examples/ar_client_webapp/public/javascripts/effects.js +0 -1088
  83. data/examples/ar_client_webapp/public/javascripts/prototype.js +0 -2515
  84. data/examples/ar_client_webapp/public/robots.txt +0 -1
  85. data/examples/ar_client_webapp/script/about +0 -3
  86. data/examples/ar_client_webapp/script/breakpointer +0 -3
  87. data/examples/ar_client_webapp/script/console +0 -3
  88. data/examples/ar_client_webapp/script/destroy +0 -3
  89. data/examples/ar_client_webapp/script/generate +0 -3
  90. data/examples/ar_client_webapp/script/performance/benchmarker +0 -3
  91. data/examples/ar_client_webapp/script/performance/profiler +0 -3
  92. data/examples/ar_client_webapp/script/plugin +0 -3
  93. data/examples/ar_client_webapp/script/process/inspector +0 -3
  94. data/examples/ar_client_webapp/script/process/reaper +0 -3
  95. data/examples/ar_client_webapp/script/process/spawner +0 -3
  96. data/examples/ar_client_webapp/script/runner +0 -3
  97. data/examples/ar_client_webapp/script/server +0 -3
  98. data/examples/ar_client_webapp/test/fixtures/users.yml +0 -5
  99. data/examples/ar_client_webapp/test/functional/user_controller_test.rb +0 -18
  100. data/examples/ar_client_webapp/test/test_helper.rb +0 -28
  101. data/examples/ar_client_webapp/test/unit/user_test.rb +0 -10
  102. data/examples/ar_client_webapp/tmp/sessions/ruby_sess.e2e3c63a67baef6d +0 -0
  103. data/examples/ar_webapp/app/.DS_Store +0 -0
  104. data/examples/ar_webapp/app/views/.DS_Store +0 -0
  105. data/examples/ar_webapp/app/views/people/.DS_Store +0 -0
  106. data/examples/ims2/connectors/hr_db_connector.rb +0 -6
  107. data/examples/ims2/connectors/my_csv_connector.rb +0 -12
  108. data/examples/ims2/pipelines/hr_import_pipeline.rb +0 -33
  109. data/examples/my_ims/connectors/my_csv_connector.rb +0 -10
  110. data/examples/my_ims/connectors/my_db_connector.rb +0 -7
  111. data/examples/my_ims/pipelines/my_pipeline.rb +0 -33
  112. data/lib/rubysync.rb +0 -19
  113. data/nbproject/private/private.properties +0 -3
  114. data/nbproject/project.properties +0 -8
  115. data/nbproject/project.xml +0 -16
  116. data/rubysync.tmproj +0 -568
  117. data/test/tc_rubysync.rb +0 -28
@@ -51,7 +51,6 @@ module RubySync::Connectors
51
51
  db_config_filename = File.join(rails_app_path, 'config', 'database.yml')
52
52
  db_config = YAML::load(ERB.new(IO.read(db_config_filename)).result)[rails_env]
53
53
  # Require the models
54
- log.debug "Loading the models for #{self.class.name}:"
55
54
  Dir.chdir(File.join(rails_app_path,'app','models')) do
56
55
  Dir.glob('*.rb') do |filename|
57
56
  log.debug("\t#{filename}")
@@ -76,9 +75,25 @@ module RubySync::Connectors
76
75
  def self.sample_config
77
76
  return <<END
78
77
 
79
- application '/path/to/a/rails/application'
80
- model 'name_of_model_to_sync'
81
-
78
+ # Uncomment and adjust the following if your app is a Ruby on Rails
79
+ # application. It will grab the config from the RoR database.yml.
80
+ # application '/path/to/a/rails/application'
81
+ # model 'name_of_model_to_sync'
82
+ # rails_env 'development' # typically development or production
83
+
84
+ # OR
85
+
86
+ # Uncomment and adjust the following if your app is not
87
+ # a Ruby on Rails application (EXPERIMENTAL)
88
+ #
89
+ # db_type 'mysql' # eg 'db2', 'mysql', 'oci', 'postgresql', 'sqlite', 'sqlserver'
90
+ # db_host 'localhost' # network name of db server
91
+ # db_name 'database_name' # Name of the database (not the table)
92
+ # model 'my_model'
93
+ # class MyModel < ActiveRecord::Base
94
+ # set_table_name "users"
95
+ # end
96
+
82
97
  END
83
98
  end
84
99
 
@@ -371,18 +371,12 @@ module RubySync::Connectors
371
371
  # Ensures that the named connector is loaded and returns its class object
372
372
  def self.class_for connector_name
373
373
  name = class_name_for connector_name
374
- (name)? eval("::"+name) : nil
374
+ (name)? eval(name) : nil
375
375
  end
376
376
 
377
- # Ensures that the named connector is loaded and returns its class name.
377
+ # Return the class name for a path style connector name
378
378
  def self.class_name_for connector_name
379
- filename = "#{connector_name}_connector"
380
- class_name = filename.camelize
381
- eval "defined? #{class_name}" or
382
- $".include?(filename) or
383
- require filename or
384
- raise Exception.new("Can't find connector '#{filename}'")
385
- class_name
379
+ '::' + "#{connector_name}_connector".camelize
386
380
  end
387
381
 
388
382
  private
@@ -22,8 +22,8 @@ module RubySync
22
22
  out_extension ".out"
23
23
 
24
24
  def started
25
- ensure_dir_exists in_path
26
- ensure_dir_exists out_path
25
+ defined? in_path and ensure_dir_exists in_path
26
+ defined? out_path and ensure_dir_exists out_path
27
27
  end
28
28
 
29
29
  def each_change(&blk)
@@ -46,8 +46,9 @@ module RubySync
46
46
 
47
47
  include RubySync::Utilities
48
48
 
49
- attr_accessor :type, # delete, add, modify ...
49
+ attr_accessor :type, # :delete, :add, :modify ...
50
50
  :source,
51
+ :target,
51
52
  :payload,
52
53
  :source_path,
53
54
  :target_path,
@@ -145,14 +146,14 @@ module RubySync
145
146
 
146
147
  # Remove any operations from the payload that affect fields with the given key or
147
148
  # keys (key can be a single field name or an array of field names).
148
- def drop_changes_to subject
149
- subjects = as_array(subject).map {|s| s.to_s}
149
+ def drop_changes_to *subjects
150
+ subjects = subjects.flatten.collect {|s| s.to_s}
150
151
  uncommitted_operations
151
152
  @uncommitted_operations = @uncommitted_operations.delete_if {|op| subjects.include? op.subject }
152
153
  end
153
154
 
154
- def drop_all_but_changes_to subject
155
- subjects = as_array(subject).map {|s| s.to_s}
155
+ def drop_all_but_changes_to *subjects
156
+ subjects = subjects.flatten.collect {|s| s.to_s}
156
157
  @uncommitted_operations = uncommitted_operations.delete_if {|op| !subjects.include?(op.subject.to_s)}
157
158
  end
158
159
 
@@ -170,15 +171,17 @@ module RubySync
170
171
  uncommitted_operations << Operation.new(:replace, field_name.to_s, as_array(value))
171
172
  end
172
173
 
173
- def values_for field_name
174
+ def values_for field_name, default=[]
174
175
  values = perform_operations @payload, {}, :subjects=>[field_name.to_s]
175
- values[field_name.to_s]
176
+ values[field_name.to_s] || default
176
177
  end
178
+ alias_method :values_of, :values_for
177
179
 
178
- def value_for field_name
180
+ def value_for field_name, default=''
179
181
  values = values_for field_name
180
- (values)? values[0] : nil
182
+ values[0] || default
181
183
  end
184
+ alias_method :value_of, :value_for
182
185
 
183
186
  def uncommitted_operations
184
187
  @uncommitted_operations ||= @payload || []
@@ -223,6 +226,10 @@ module RubySync
223
226
  uncommitted_operations << RubySync::Operation.replace(left.to_s, blk.call)
224
227
  end
225
228
  end
229
+
230
+ def place(&blk)
231
+ self.target_path = blk.call
232
+ end
226
233
 
227
234
  protected
228
235
 
@@ -13,11 +13,12 @@
13
13
  # You should have received a copy of the GNU General Public License along with RubySync; if not, write to the
14
14
  # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15
15
 
16
- lib_path = File.dirname(__FILE__) + '/../..'
17
- $:.unshift lib_path unless $:.include?(lib_path) || $:.include?(File.expand_path(lib_path))
16
+ lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ $:.unshift lib_path unless $:.include?(lib_path)
18
18
 
19
19
  require 'active_support'
20
20
  require 'ruby_sync/util/metaid'
21
+ require 'ruby_sync/util/utilities'
21
22
  require 'yaml'
22
23
 
23
24
 
@@ -42,6 +43,10 @@ module RubySync
42
43
 
43
44
  attr_accessor :delay # delay in seconds between checking connectors
44
45
 
46
+ array_option :dump_before, :dump_after
47
+ dump_before []
48
+ dump_after []
49
+
45
50
  def initialize
46
51
  @delay = 5
47
52
  end
@@ -55,7 +60,7 @@ module RubySync
55
60
  options[:name] ||= "#{self.name}(client)"
56
61
  options[:is_vault] = false
57
62
  class_def 'client' do
58
- @client ||= eval("::#{class_name}").new(options)
63
+ @client ||= eval(class_name).new(options)
59
64
  end
60
65
  end
61
66
 
@@ -65,163 +70,72 @@ module RubySync
65
70
  options[:is_vault] = true
66
71
  class_def 'vault' do
67
72
  unless @vault
68
- @vault = eval("::" + class_name).new(options)
73
+ @vault = eval(class_name).new(options)
69
74
  @vault.pipeline = self
70
75
  end
71
76
  @vault
72
77
  end
73
78
  end
74
79
 
75
- def self.map_client_to_vault mappings
76
- remove_method :client_to_vault_map if method_defined? :client_to_vault_map
77
- class_def 'client_to_vault_map' do
78
- unless @client_to_vault_map
79
- @client_to_vault_map = {}
80
- mappings.each {|k,v| @client_to_vault_map[k.to_s] = v.to_s}
81
- end
82
- @client_to_vault_map
83
- end
84
- unless method_defined? :vault_to_client_map
85
- class_def 'vault_to_client_map' do
86
- @vault_to_client_map ||= client_to_vault_map.invert
87
- end
88
- end
89
- end
90
-
91
- def self.map_vault_to_client mappings
92
- remove_method :vault_to_client_map if method_defined? :vault_to_client_map
93
- class_def 'vault_to_client_map' do
94
- unless @vault_to_client_map
95
- @vault_to_client_map = {}
96
- mappings.each {|k,v| @vault_to_client_map[k.to_s] = v.to_s}
97
- end
98
- @vault_to_client_map
99
- end
100
- unless method_defined? :client_to_vault_map
101
- class_def 'client_to_vault_map' do
102
- @client_to_vault_map ||= vault_to_client_map.invert
103
- end
104
- end
105
- end
106
-
107
- def self.out_transform &blk
108
- define_method :out_transform do |event|
109
- event.meta_def :transform, &blk
110
- event.transform
111
- end
112
- end
113
-
114
- def self.in_transform &blk
115
- define_method :in_transform do |event|
116
- event.meta_def :transform, &blk
117
- event.transform
118
- end
119
- end
120
-
121
-
122
- # Called by the identity-vault connector in the 'out' thread to process events generated
123
- # by the identity vault.
124
- def out_handler(event)
125
80
 
126
- event.retrieve_association(association_context)
127
- event.convert_to_modify if event.associated? and event.type == :add
128
-
129
- hint = " (#{vault.name} => #{client.name})"
130
- log.info "Processing out-going #{event.type} event #{hint}"
131
- log.info YAML.dump(event)
132
- return unless out_event_filter event
133
-
134
- # Remove unwanted attributes
135
- perform_transform :out_filter, event
81
+ def self.in_transform(&blk) event_method :in_transform,&blk; end
82
+ def self.out_transform(&blk) event_method :out_transform,&blk; end
83
+ def self.in_match(&blk) event_method :in_match_if,&blk; end
84
+ def self.out_match(&blk) event_method :out_match_if,&blk; end
85
+ def self.in_create_if(&blk) event_method :in_create_if,&blk; end
86
+ def self.out_create_if(&blk) event_method :out_create_if,&blk; end
136
87
 
137
- unless event.associated?
138
- if [:delete, :remove_association].include? event.type
139
- log.info "#{name}: No action for #{event.type} of unassociated entry"
140
- log.info YAML.dump(event)
141
- return
142
- end
88
+ def self.event_method name,&blk
89
+ define_method name do |event|
90
+ event.instance_eval &blk
143
91
  end
92
+ end
144
93
 
145
- if event.type == :modify
146
- unless event.associated? and client.has_entry_for_key?(event.association.key)
147
- event.convert_to_add
148
- end
149
- end
94
+ def self.in_place(&blk) place :in, &blk; end
95
+ def self.out_place(&blk) place :out, &blk; end
150
96
 
151
- if event.type == :add
152
- match = out_match(event)
153
- log.info "Attempting to match"
154
- if match # exactly one event record on the client matched
155
- log.info "Match found, merging"
156
- event.merge(match)
157
- association = Association.new(self.association_context, match.src_path)
158
- vault.associate asssociation, event.source_path
159
- return
160
- end
161
- log.info "No match found, creating"
162
- return unless out_create(event)
163
- perform_transform :out_place, event
164
- end
165
-
166
- perform_transform :out_map_schema, event
167
- perform_transform :out_transform, event
168
- association_key = nil
169
- with_rescue("#{client.name}: Processing command") do
170
- association_key = client.process(event)
171
- end
172
- if association_key
173
- association = Association.new(association_context, association_key)
174
- with_rescue("#{client.name}: Storing association #{association} in vault") do
175
- vault.associate(association, event.source_path)
176
- end
97
+ def self.place direction, &blk
98
+ define_method "#{direction}_place" do |event|
99
+ event.target_path = event.instance_eval &blk
177
100
  end
178
101
  end
179
102
 
180
- # Override to map schema from vault namespace to client namespace
181
- # def out_map_schema event
182
- # end
183
103
 
184
104
  # Override to implement some kind of matching
185
- def out_match event
105
+ def default_match event
186
106
  log.debug "Default matching rule - source path exists on client?"
187
- client.respond_to?('[]') and client[event.source_path]
188
- false
107
+ event.target.respond_to?('[]') and event.target[event.source_path]
189
108
  end
109
+ alias_method :in_match, :default_match
110
+ alias_method :out_match, :default_match
190
111
 
191
112
  # Override to restrict creation on the client
192
- def out_create event
113
+ def default_create event
193
114
  log.debug "Create allowed through default rule"
194
115
  true
195
116
  end
117
+ alias_method :in_create, :default_create
118
+ alias_method :out_create, :default_create
196
119
 
197
- # Override to restrict creation on the vault
198
- def in_create event
199
- log.debug "Create allowed through default rule"
200
- true
201
- end
202
120
 
203
121
  # Override to modify the target path for creation on the client
204
- def out_place(event)
122
+ def default_place(event)
205
123
  log.debug "Default placement rule target_path = source_path"
206
124
  event.target_path = event.source_path
207
125
  end
126
+ alias_method :in_place, :default_place
127
+ alias_method :out_place, :default_place
208
128
 
209
- # Override to modify the target path for creation in the vault
210
- def in_place(event)
211
- log.debug "Default placement rule target_path = source_path"
212
- event.target_path = event.source_path
213
- end
214
129
 
215
130
  def perform_transform name, event, hint=""
131
+ log.info "Performing #{name}"
132
+ log.info event.to_yaml if dump_before.include?(name.to_sym)
216
133
  call_if_exists name, event, hint
217
134
  event.commit_changes
218
- log_progress name, event, hint
135
+ log.info event.to_yaml if dump_after.include?(name.to_sym)
136
+ #log_progress name, event, hint
219
137
  end
220
-
221
- # Transform the out-going event before the client receives it
222
- # def out_transform(event)
223
- # end
224
-
138
+
225
139
  # Execute the pipeline once then return.
226
140
  def run_once
227
141
  log.info "Running #{name} pipeline once"
@@ -243,6 +157,7 @@ module RubySync
243
157
 
244
158
  # Execute the in pipe once and then return
245
159
  def run_in_once
160
+ return unless allowed_in
246
161
  log.debug "Running #{name} 'in' pipeline once"
247
162
  client.once_only = true
248
163
  client.start {|event| in_handler(event)}
@@ -250,6 +165,7 @@ module RubySync
250
165
 
251
166
  # Execute the out pipe once and then return
252
167
  def run_out_once
168
+ return unless allowed_out
253
169
  log.debug "Running #{name} 'out' pipeline once"
254
170
  vault.once_only = true
255
171
  vault.start {|event| out_handler(event)}
@@ -282,51 +198,127 @@ module RubySync
282
198
 
283
199
  # Called by the 'in' connector in the 'in' thread to process events generated by the client.
284
200
  def in_handler(event)
285
- event.retrieve_association(association_context)
201
+ event.target = @vault
202
+ event.retrieve_association(association_context)
286
203
 
287
- hint = " (#{client.name} => #{vault.name})"
288
- log.info "Processing incoming #{event.type} event"+hint
289
- log.info YAML.dump(event)
290
- perform_transform :in_map_schema, event, hint
291
- perform_transform :in_transform, event, hint
204
+ hint = "(#{client.name} => #{vault.name}) #{event.source_path}"
205
+ log.info "Processing incoming #{event.type} event "+hint
292
206
  perform_transform :in_filter, event, hint
293
207
 
294
208
  # The client can't really know whether its an add or a modify because it doesn't store
295
209
  # the association.
296
210
  if event.type == :modify
297
- event.convert_to_add unless event.associated? and vault.find_associated(event.association)
211
+ unless event.associated? and vault.find_associated(event.association)
212
+ log.info "No associated entry in vault for modify event. Converting to add"
213
+ event.convert_to_add
214
+ end
298
215
  elsif event.type == :add and event.associated? and vault.find_associated(event.association)
216
+ log.info "Associated entry in vault for add event. Converting to modify"
299
217
  event.convert_to_modify
300
218
  end
219
+
220
+ perform_transform :in_transform, event, hint
221
+
301
222
 
223
+ # todo: Maybe we should merge any add or modify that is associated or matched
302
224
  if event.type == :add
303
- match = in_match(event) # exactly one event record in the vault matched
225
+ match = in_match(event)
304
226
  if match
227
+ log.info "Matching record found in vault. Merging."
305
228
  event.merge(match)
306
- return
229
+ log.info "---\n"; return
307
230
  end
308
231
 
309
232
  if in_create(event)
310
233
  perform_transform :in_place, event, hint
234
+ log.info "Create on vault allowed. Placing at #{event.target_path}"
311
235
  else
312
- return
236
+ log.info "Create rule disallowed creation"
237
+ log.info "---\n"; return
313
238
  end
314
239
  end
315
-
240
+
316
241
  with_rescue("#{vault.name}: Processing command") {vault.process(event)}
242
+ log.info "---\n"
317
243
 
318
244
  end
319
245
 
246
+
247
+ # Called by the identity-vault connector in the 'out' thread to process events generated
248
+ # by the identity vault.
249
+ def out_handler(event)
250
+ event.target = @client
251
+ event.retrieve_association(association_context)
252
+ event.convert_to_modify if event.associated? and event.type == :add
253
+
254
+ hint = "(path=#{event.source_path} #{vault.name} => #{client.name})"
255
+ log.info "Processing out-going #{event.type} event #{hint}"
256
+ #log.info YAML.dump(event)
257
+ unless out_event_filter event
258
+ log.info "Disallowed by out_event_filter"
259
+ log.info "---\n"; return
260
+ end
261
+
262
+ # Remove unwanted attributes
263
+ perform_transform :out_filter, event
264
+
265
+ unless event.associated?
266
+ log.info "no association"
267
+ if [:delete, :remove_association].include? event.type
268
+ log.info "#{name}: No action for #{event.type} of unassociated entry"
269
+ log.info "---\n"; return
270
+ end
271
+ end
272
+
273
+ if event.type == :modify
274
+ unless event.associated? and client.has_entry_for_key?(event.association.key)
275
+ log.info "Can't find associated client record so converting modify to add"
276
+ event.convert_to_add
277
+ end
278
+ end
279
+
280
+ if event.type == :add
281
+ match = out_match(event)
282
+ log.info "Attempting to match"
283
+ if match # exactly one event record on the client matched
284
+ log.info "Match found, merging"
285
+ event.merge(match)
286
+ association = Association.new(self.association_context, match.source_path)
287
+ vault.associate asssociation, event.source_path
288
+ log.info "---\n"; return
289
+ end
290
+ log.info "No match found, creating"
291
+ unless out_create(event)
292
+ log.info "Creation denied by create rule"
293
+ log.info "---\n"; return
294
+ end
295
+ perform_transform :out_place, event
296
+ log.info "Placing new entry at #{event.target_path}"
297
+ end
298
+
299
+ perform_transform :out_transform, event
300
+ association_key = nil
301
+ with_rescue("#{client.name}: Processing command") do
302
+ association_key = client.process(event)
303
+ end
304
+ if association_key
305
+ association = Association.new(association_context, association_key)
306
+ with_rescue("#{client.name}: Storing association #{association} in vault") do
307
+ vault.associate(association, event.source_path)
308
+ end
309
+ else
310
+ log.info "Client didn't return an association key"
311
+ end
312
+ end
313
+
314
+
315
+
320
316
  # The context for all association keys used by this pipeline.
321
317
  # By default, defer to the client
322
318
  def association_context
323
319
  @client.association_context
324
320
  end
325
-
326
- def in_match event
327
- log.debug "Default match rule - source path exists in vault"
328
- vault.respond_to?('[]') and vault[event.source_path]
329
- end
321
+
330
322
 
331
323
  # If client_to_vault_map is defined (usually by map_client_to_vault)
332
324
  # then fix up the contents of the payload to refer to the fields by
@@ -345,15 +337,6 @@ module RubySync
345
337
  op.subject = map[op.subject] || op.subject if op.subject
346
338
  end
347
339
  end
348
-
349
-
350
-
351
-
352
- # Override to perform whatever transformation on the event is required
353
- #def in_transform(event); event; end
354
-
355
- # Convert fields in the incoming event to those used by the identity vault
356
- #def in_map_schema(event); end
357
340
 
358
341
  # Specify which fields will be allowed through the incoming filter
359
342
  # If nil (the default), all fields are allowed.
@@ -366,14 +349,9 @@ module RubySync
366
349
  # default allowed_in in case allow_in doesn't get called
367
350
  def allowed_in; nil; end
368
351
 
369
- # Default method for allowed_in. Override by calling allow_in
370
- #def allowed_in; false; end
352
+
371
353
  def in_filter(event)
372
- if allowed_in
373
- event.drop_all_but_changes_to allowed_in
374
- else
375
- event
376
- end
354
+ allowed_in == [] or event.drop_all_but_changes_to(allowed_in || [])
377
355
  end
378
356
 
379
357
 
@@ -388,17 +366,12 @@ module RubySync
388
366
  # default allowed_out in case allow_out doesn't get called
389
367
  def allowed_out; nil; end
390
368
 
391
- # Default method for allowed_out. Override by calling allow_in
392
- #def allowed_out; false; end
393
369
  def out_filter(event)
394
- if allowed_out
395
- event.drop_all_but_changes_to allowed_out
396
- else
397
- event
398
- end
370
+ allowed_out == [] or event.drop_all_but_changes_to(allowed_out || [])
399
371
  end
400
372
 
401
373
 
374
+
402
375
  end
403
376
  end
404
377
  end
@@ -13,22 +13,17 @@
13
13
  # You should have received a copy of the GNU General Public License along with RubySync; if not, write to the
14
14
  # Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
15
15
 
16
+ lib_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
17
+ $:.unshift lib_path unless $:.include?(lib_path)
16
18
 
17
19
  require 'fileutils'
20
+ require 'rubygems'
21
+ require 'active_support'
18
22
  require 'irb'
19
23
 
20
24
 
21
- class ::File
22
- def self.delete_if_exists(files)
23
- files.kind_of?(Array) or files = [files]
24
- files.each do |file|
25
- File.delete(file) if File.exist?(file)
26
- end
27
- end
28
- end
29
-
30
- class Object
31
25
 
26
+ module Kernel
32
27
  # Make the log method globally available
33
28
  def log
34
29
  unless defined? @@log
@@ -39,7 +34,12 @@ class Object
39
34
  @@log
40
35
  end
41
36
  end
42
-
37
+
38
+ class Array
39
+ def to_ruby
40
+ map {|f| "'#{f}'"}.join(', ')
41
+ end
42
+ end
43
43
 
44
44
  # Generally useful methods
45
45
  module RubySync
@@ -104,7 +104,6 @@ module RubySync
104
104
  # the given name.
105
105
  def something_called name, extension
106
106
  filename = "#{name.to_s}_#{extension}"
107
- $".include?(filename) or require filename or return nil
108
107
  eval(filename.camelize).new
109
108
  end
110
109
 
@@ -115,8 +114,8 @@ module RubySync
115
114
  $:.unshift path unless $:.include?(path)
116
115
  end
117
116
 
118
- # Return the base_path
119
- def base_path
117
+ # Return the base_path
118
+ ::Kernel.send :define_method, :base_path do
120
119
  @@base_path = find_base_path unless @@base_path
121
120
  @@base_path
122
121
  end
@@ -125,7 +124,7 @@ module RubySync
125
124
  # all of it's ancestors until it finds one that looks like a rubysync configuration
126
125
  # directory.
127
126
  # Returns false if no suitable directory was found
128
- def find_base_path
127
+ ::Kernel.send :define_method, :find_base_path do
129
128
  bp = File.expand_path(".")
130
129
  last = nil
131
130
  # Keep going up until we start repeating ourselves
@@ -138,12 +137,6 @@ module RubySync
138
137
  return false
139
138
  end
140
139
 
141
- # Make and instance method _name_ that returns the value set by the
142
- # class method _name_.
143
- # def self.class_option name
144
- # self.class_eval "def #{name}() self.class.instance_variable_get :#{name}; end"
145
- # self.instance_eval "def #{name}(value) @#{name}=value; end"
146
- # end
147
140
 
148
141
  def get_preference(name, file_name=nil)
149
142
  class_name ||= get_preference_file