Class: RubySync::Connectors::BaseConnector

Inherits:
Object
  • Object
show all
Includes:
ConnectorEventProcessing, Utilities
Defined in:
lib/ruby_sync/connectors/base_connector.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ConnectorEventProcessing

#associated_path, #delete_from_mirror, #perform_add, #perform_delete, #perform_modify, #process, #update_mirror

Methods included from Utilities

#as_array, #call_if_exists, #class_called, #class_for_name, #class_name_for, #connector_called, #dump_after, #dump_before, #effective_operations, #ensure_dir_exists, #get_preference, #get_preference_file_path, #include_in_search_path, #log_progress, #perform_operations, #perform_transform, #pipeline_called, #set_preference, #something_called, #with_rescue

Constructor Details

#initialize(options = {}) ⇒ BaseConnector

Returns a new instance of BaseConnector.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/ruby_sync/connectors/base_connector.rb', line 27

def initialize options={}
  base_path # call this once to get the working directory before anything else
  # in the connector changes the cwd
  options = self.class.default_options.merge(options)
  once_only = false
  self.name = options[:name]
  self.is_vault = options[:is_vault]
  if is_vault && !can_act_as_vault?
	raise "#{self.class.name} can't act as an identity vault."
  end
  options.each do |key, value|
	if self.respond_to? "#{key}="
	  self.send("#{key}=", value) 
	else
	  log.debug "#{name}: doesn't respond to #{key}="
	end
  end
end

Instance Attribute Details

#is_vaultObject

Returns the value of attribute is_vault.



25
26
27
# File 'lib/ruby_sync/connectors/base_connector.rb', line 25

def is_vault
  @is_vault
end

#nameObject

Returns the value of attribute name.



25
26
27
# File 'lib/ruby_sync/connectors/base_connector.rb', line 25

def name
  @name
end

#once_onlyObject

Returns the value of attribute once_only.



25
26
27
# File 'lib/ruby_sync/connectors/base_connector.rb', line 25

def once_only
  @once_only
end

#pipelineObject

Returns the value of attribute pipeline.



25
26
27
# File 'lib/ruby_sync/connectors/base_connector.rb', line 25

def pipeline
  @pipeline
end

Class Method Details

.event_method(name, &blk) ⇒ Object



60
61
62
63
64
# File 'lib/ruby_sync/connectors/base_connector.rb', line 60

def self.event_method name,&blk
  define_method name do |event|
	event.instance_eval(&blk)
  end
end

.fieldsObject

Return an array of possible fields for this connector. Implementations should override this to query the datasource for possible fields.



255
256
257
# File 'lib/ruby_sync/connectors/base_connector.rb', line 255

def self.fields
  nil
end

.sample_configObject

# Return the class name for a path style connector name

def self.class_name_for connector_name
  '::' + "#{connector_name}_connector".camelize
end


57
58
# File 'lib/ruby_sync/connectors/base_connector.rb', line 57

def self.sample_config
end

.target_transform(&blk) ⇒ Object



67
# File 'lib/ruby_sync/connectors/base_connector.rb', line 67

def self.target_transform(&blk) event_method :target_transform,&blk; end

.track_associations_with(method) ⇒ Object



369
370
371
# File 'lib/ruby_sync/connectors/base_connector.rb', line 369

def self.track_associations_with method
  include_something_called method, "association_tracking"
end

.track_changes_with(method) ⇒ Object



365
366
367
# File 'lib/ruby_sync/connectors/base_connector.rb', line 365

def self.track_changes_with method
  include_something_called method, "change_tracking"
end

Instance Method Details

#add(id, operations) ⇒ Object

Subclasses must override this. Called by perform_add to actually store the new record in the datastore. Returned value will be used as the association id if this connector is acting as the client.



50
51
52
# File 'lib/ruby_sync/connectors/base_connector.rb', line 50

def add id, operations
  raise "add method not implemented"
end

#association_contextObject

The context to be used to for all associations created where this connector is the client.



202
203
204
# File 'lib/ruby_sync/connectors/base_connector.rb', line 202

def association_context
  self.name
end

#association_for(context, path) ⇒ Object

Return the association object given the association context and path. This should only be called on the vault.



185
186
187
188
189
# File 'lib/ruby_sync/connectors/base_connector.rb', line 185

def association_for(context, path)
  raise "#{name} is not a vault." unless is_vault?
  key = association_key_for context, path
  key and RubySync::Association.new(context, key)
end

#can_act_as_vault?Boolean

Whether this connector is capable of acting as a vault. The vault is responsible for storing the association key of the client application and must be able to retrieve records for that association key.

Returns:

  • (Boolean)


173
174
175
176
177
178
179
# File 'lib/ruby_sync/connectors/base_connector.rb', line 173

def can_act_as_vault?
  defined? associate and
    defined? path_for_association and
    defined? association_key_for and
    defined? remove_association and
    defined? associations_for
end

#cleanObject



206
207
# File 'lib/ruby_sync/connectors/base_connector.rb', line 206

def clean
end

#create_operations_for(record) ⇒ Object

Return an array of operations that would create the given record if applied to an empty hash.



247
248
249
# File 'lib/ruby_sync/connectors/base_connector.rb', line 247

def create_operations_for record
  record.keys.map {|key| RubySync::Operation.new(:add, key, record[key])}
end

#each_entryObject

Subclasses must override this to interface with the external system and generate entries for every entry in the scope passing the entry path (id) and its data (as a hash of arrays). This method will be called repeatedly until the connector is stopped.



74
75
76
# File 'lib/ruby_sync/connectors/base_connector.rb', line 74

def each_entry
  raise "Not implemented"
end

#entry_for_own_association_key(key) ⇒ Object

Returns the entry matching the association key. This is only called on the client.



159
160
161
# File 'lib/ruby_sync/connectors/base_connector.rb', line 159

def entry_for_own_association_key(key)
  self[path_for_own_association_key(key)]
end

#find_associated(association) ⇒ Object

Should only be called on the vault. Returns the entry associated with the association passed. Some connectors may wish to override this if they have a more efficient way of retrieving the record for a given association.



195
196
197
198
# File 'lib/ruby_sync/connectors/base_connector.rb', line 195

def find_associated association
  path = path_for_association association
  path and self[path]
end

#has_entry_for_key?(key) ⇒ Boolean

True if there is an entry matching the association key. Only called on the client. Override if you have a quicker way of determining whether an entry exists for given key than retrieving the entry.

Returns:

  • (Boolean)


166
167
168
# File 'lib/ruby_sync/connectors/base_connector.rb', line 166

def has_entry_for_key?(key)
  entry_for_own_association_key(key)
end

#is_delete_echo?(event) ⇒ Boolean

Attempts to delete non-existent items may occur due to echoing. Many systems won’t be able to record the fact that an entry has been deleted by rubysync because after the delete, there is no entry left to record the information in. Therefore, they may issue a notification that the item has been deleted. This becomes an event and the connector won’t know that it caused the delete. The story usually has a reasonably happy ending though. The inappropriate delete event is processed by the pipeline and a delete attempt is made on the datastore that actually triggered the original delete event in the first place. Most of the time, there will be no entry there for it to delete and it will fail harmlessly. Problems may arise, however, if the original delete event was the result of manipulation in the pipeline and the original entry is in fact supposed to stay there. For example, say a student in an enrolment system was marked as not enrolled anymore. This modify event is translated by the pipeline that connects to the identity vault to become a delete because only the enrolment system is interested in non-enrolled students. As the student is removed from the identity vault, a new delete event is generated targeted back and the enrolment system. If the pipeline has been configured to honour delete requests from the vault to the enrolment system then the students entry in the enrolment system would be deleted.

Returns:

  • (Boolean)


224
225
226
# File 'lib/ruby_sync/connectors/base_connector.rb', line 224

def is_delete_echo? event
  false #TODO implement delete event caching
end

#is_echo?(event) ⇒ Boolean

Returns:

  • (Boolean)


228
# File 'lib/ruby_sync/connectors/base_connector.rb', line 228

def is_echo? event; false end

#is_vault?Boolean

Returns:

  • (Boolean)


135
136
137
# File 'lib/ruby_sync/connectors/base_connector.rb', line 135

def is_vault?
  @is_vault
end

#own_association_key_for(path) ⇒ Object

Returns the association key for the given path. Called if this connector is the client. The default implementation returns the path itself. If there is a more efficient key for looking up an entry in the client, override to return that instead.



145
146
147
# File 'lib/ruby_sync/connectors/base_connector.rb', line 145

def own_association_key_for(path)
  path
end

#path_for_own_association_key(key) ⇒ Object

Returns the appropriate entry for the association key. This key will have been provided by a previous call to the association_key method. This will only be called on the client connector. It is not expected that the client will have to store this key.



154
155
156
# File 'lib/ruby_sync/connectors/base_connector.rb', line 154

def path_for_own_association_key(key)
  key
end

#start(&blk) ⇒ Object

Call each_change repeatedly (or once if in once_only mode) to generate events. Should generally only be called by the pipeline to which it is attached.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/ruby_sync/connectors/base_connector.rb', line 82

def start &blk
  log.debug "#{name}: Started"
  @running = true
  sync_started()
  while @running
	each_change do |event|
	  if event.type == :force_resync
 each_entry(&blk)
 next
	  end
	  if is_delete_echo?(event) || is_echo?(event)
 log.debug "Ignoring echoed event"
	  else
 call_if_exists :source_transform, event
 yield(event)
	  end
	end

	if once_only
	  log.debug "#{name}: Stopped"
	  @running = false
	else
	  log.debug "#{name}: sleeping"
	  sleep 1
	end
  end
  sync_stopped
end

#startedObject

Override this to perform actions that must be performed the when the connector starts running. (Eg, opening network connections)



120
121
# File 'lib/ruby_sync/connectors/base_connector.rb', line 120

def started
end

#stopObject

Politely stop the connector.



129
130
131
132
# File 'lib/ruby_sync/connectors/base_connector.rb', line 129

def stop
  log.info "#{name}: Attempting to stop"
  @running = false
end

#stoppedObject

Override this to perform actions that must be performed when the connector exits (eg closing network conections).



125
# File 'lib/ruby_sync/connectors/base_connector.rb', line 125

def stopped; end

#sync_startedObject

Called by start() before first call to each_change or each_entry



116
# File 'lib/ruby_sync/connectors/base_connector.rb', line 116

def sync_started; end

#sync_stoppedObject

Called by start() after last call to each_change or each_entry



113
# File 'lib/ruby_sync/connectors/base_connector.rb', line 113

def sync_stopped; end

#test_add(id, details) ⇒ Object

Called by unit tests to inject data



231
232
233
# File 'lib/ruby_sync/connectors/base_connector.rb', line 231

def test_add id, details
  add id, details
end

#test_delete(id) ⇒ Object

Called by unit tests to delete a record



241
242
243
# File 'lib/ruby_sync/connectors/base_connector.rb', line 241

def test_delete id
  delete id
end

#test_modify(id, details) ⇒ Object

Called by unit tests to modify data



236
237
238
# File 'lib/ruby_sync/connectors/base_connector.rb', line 236

def test_modify id, details
  modify id, details
end