Class: Roby::Transaction

Inherits:
Plan show all
Includes:
Log::TransactionHooks
Defined in:
lib/roby/transactions.rb,
lib/roby/query.rb,
lib/roby/transactions/updates.rb

Overview

A transaction is a special kind of plan. It allows to build plans in a separate sandbox, and then to apply the modifications to the real plan (using #commit_transaction), or to discard all modifications (using #discard)

Direct Known Subclasses

Distributed::Transaction

Defined Under Namespace

Modules: Proxy

Constant Summary

Constants included from Log::TransactionHooks

Log::TransactionHooks::HOOKS

Constants included from Log::PlanHooks

Log::PlanHooks::HOOKS

Constants included from Log::BasicObjectHooks

Log::BasicObjectHooks::HOOKS

Instance Attribute Summary collapse

Attributes inherited from Plan

#force_gc, #free_events, #gc_quarantine, #keepalive, #known_tasks, #missions, #repairs, #task_events, #task_index, #transactions

Attributes inherited from BasicObject

#distribute

Instance Method Summary collapse

Methods inherited from Plan

#add_repair, #added_transaction, can_gc?, #discarded, #discover_event_set, #discover_task_set, #discovered, #discovered_events, #discovered_tasks, #droby_dump, #each_task, #empty?, #finalized, #finalized_event, #finalized_task, #find_tasks, #garbage, #garbage_collect, #handle_replace, #include?, #inserted, #inspect, #local_tasks, #locally_useful_tasks, #mission?, #owns?, #partition_event_task, #permanent?, #real_plan, #remote_tasks, #remove_repair, #remove_task, #remove_transaction, #removed_transaction, #repairs_for, #replace_task, #replaced, #respawn, #sibling_on?, #size, #unneeded_events, #unneeded_tasks, #useful_event_component, #useful_events, #useful_task?, #useful_task_component

Methods included from Roby::TaskStructure::ExecutionAgentSpawn

#discovered_tasks

Methods included from Distributed::EventNotifications::PlanCacheCleanup

#finalized_event

Methods included from Distributed::PlanModificationHooks

#discarded, #discovered_events, discovered_objects, #discovered_tasks, #finalized_event, finalized_object, #finalized_task, #inserted, #replaced

Methods included from Roby::Transactions::PlanUpdates

#finalized_event, finalized_object, #finalized_task

Methods included from Propagation::RemoveDelayedOnFinalized

#finalized_event

Methods included from Log::PlanHooks

#added_transaction, #discarded, #discovered_events, #discovered_tasks, #finalized_event, #finalized_task, #garbage, #inserted, #removed_transaction, #replaced

Methods included from EventGenerator::FinalizedEventHook

#finalized_event

Methods inherited from BasicObject

#add_sibling_for, #distribute?, distribute?, #forget_peer, #has_sibling_on?, #initialize_copy, local_only, #read_write?, #remotely_useful?, #remove_sibling_for, #self_owned?, #sibling_of, #sibling_on, #subscribe, #subscribed?, #update_on?, #updated?, #updated_by?, #updated_peers

Methods included from Log::BasicObjectHooks

#added_owner, #removed_owner

Constructor Details

#initialize(plan, options = {}) ⇒ Transaction

Creates a new transaction which applies on plan



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/roby/transactions.rb', line 206

def initialize(plan, options = {})
    options = validate_options options, 
  :conflict_solver => :invalidate

    @options = options
    self.conflict_solver = options[:conflict_solver]
    super()

    @plan = plan

    @proxy_objects      = Hash.new
    @removed_objects    = ValueSet.new
    @discarded_tasks    = ValueSet.new
    @auto_tasks         = ValueSet.new

    Roby::Control.synchronize do
  plan.transactions << self
  plan.added_transaction(self)
    end
end

Instance Attribute Details

#auto_tasksObject (readonly)

The list of permanent tasks that have been auto’ed



184
185
186
# File 'lib/roby/transactions.rb', line 184

def auto_tasks
  @auto_tasks
end

#conflict_solverObject

Returns the value of attribute conflict_solver.



190
191
192
# File 'lib/roby/transactions.rb', line 190

def conflict_solver
  @conflict_solver
end

#discarded_tasksObject (readonly)

The list of discarded



180
181
182
# File 'lib/roby/transactions.rb', line 180

def discarded_tasks
  @discarded_tasks
end

#optionsObject (readonly)

Returns the value of attribute options.



191
192
193
# File 'lib/roby/transactions.rb', line 191

def options
  @options
end

#planObject (readonly)

The plan this transaction applies on



186
187
188
# File 'lib/roby/transactions.rb', line 186

def plan
  @plan
end

#proxy_objectsObject (readonly)

The proxy objects built for this transaction



188
189
190
# File 'lib/roby/transactions.rb', line 188

def proxy_objects
  @proxy_objects
end

#removed_objectsObject (readonly)

The list of removed tasks and events



182
183
184
# File 'lib/roby/transactions.rb', line 182

def removed_objects
  @removed_objects
end

Instance Method Details

#adding_plan_relation(parent, child, relations, info) ⇒ Object



68
69
70
71
72
73
74
75
76
# File 'lib/roby/transactions/updates.rb', line 68

def adding_plan_relation(parent, child, relations, info)
    missing_relations = relations.find_all do |rel|
  !parent.child_object?(child, rel)
    end
    unless missing_relations.empty?
  invalidate("plan added a relation #{parent} -> #{child} in #{relations} with info #{info}")
  conflict_solver.adding_plan_relation(self, parent, child, relations, info)
    end
end

#auto(t) ⇒ Object



267
268
269
270
271
272
273
274
275
276
277
# File 'lib/roby/transactions.rb', line 267

def auto(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?
    if proxy = self[t, false]
  super(proxy)
    end

    t = may_unwrap(t)
    if t.plan == self.plan
  auto_tasks.insert(t)
    end
end

#check_valid_transactionObject

Raises:



309
310
311
312
313
314
315
316
317
318
319
# File 'lib/roby/transactions.rb', line 309

def check_valid_transaction
    return if valid_transaction?

    unless transactions.empty?
  raise InvalidTransaction, "there is still transactions on top of this one"
    end
    message = invalidation_reasons.map do |reason, trace|
  "#{trace[0]}: #{reason}\n  #{trace[1..-1].join("\n  ")}"
    end.join("\n")
    raise InvalidTransaction, "invalid transaction: #{message}"
end

#clearObject



453
454
455
456
457
458
459
# File 'lib/roby/transactions.rb', line 453

def clear
    removed_objects.clear
    discarded_tasks.clear
    proxy_objects.each_value { |proxy| proxy.clear_relations }
    proxy_objects.clear
    super
end

#commit_transactionObject

Commit all modifications that have been registered in this transaction



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
# File 'lib/roby/transactions.rb', line 323

def commit_transaction
    # if !Roby.control.running?
    #     raise "#commit_transaction requires the presence of a control thread"
    # end

    check_valid_transaction
    freezed!

    Roby.execute do
  auto_tasks.each      { |t| plan.auto(t) }
  discarded_tasks.each { |t| plan.discard(t) }
  removed_objects.each do |obj| 
      plan.remove_object(obj) if plan.include?(obj)
  end

  discover_tasks  = ValueSet.new
  discover_events  = ValueSet.new
  insert    = ValueSet.new
  permanent = ValueSet.new
  known_tasks.dup.each do |t|
      unwrapped = if t.kind_of?(Transactions::Proxy)
          finalized_task(t)
          t.__getobj__
      else
          known_tasks.delete(t)
          t
      end

      if missions.include?(t) && t.self_owned?
    missions.delete(t)
    insert << unwrapped
      elsif keepalive.include?(t) && t.self_owned?
    keepalive.delete(t)
    permanent << unwrapped
      end

      discover_tasks << unwrapped
  end

  free_events.dup.each do |ev|
      unwrapped = if ev.kind_of?(Transactions::Proxy)
          finalized_event(ev)
          ev.__getobj__
      else
          free_events.delete(ev)
          ev
      end

      discover_events << unwrapped
  end

  new_tasks = plan.discover_task_set(discover_tasks)
  new_tasks.each do |task|
      if task.respond_to?(:commit_transaction)
    task.commit_transaction
      end
  end

  new_events = plan.discover_event_set(discover_events)
  new_events.each do |event|
      if event.respond_to?(:commit_transaction)
    event.commit_transaction
      end
  end

  # Set the plan to nil in known tasks to avoid having the checks on
  # #plan to raise an exception
  proxy_objects.each_value { |proxy| proxy.commit_transaction }
  proxy_objects.each_value { |proxy| proxy.clear_relations  }

  insert.each    { |t| plan.insert(t) }
  permanent.each { |t| plan.permanent(t) }

  proxies     = proxy_objects.dup
  clear
  # Replace proxies by forwarder objects
  proxies.each do |object, proxy|
      forwarder = Proxy.forwarder(object)
      forwarder.freeze
      Kernel.swap! proxy, forwarder
  end

  committed_transaction
  plan.remove_transaction(self)
  @plan = nil

  yield if block_given?
    end
end

#committed_transactionObject



412
# File 'lib/roby/transactions.rb', line 412

def committed_transaction; super if defined? super end

#copy_object_relations(object, proxy) ⇒ Object

This method copies on proxy all relations of object for which both ends of the relation are already in the transaction.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/roby/transactions.rb', line 40

def copy_object_relations(object, proxy)
    Roby::Control.synchronize do
  # Create edges between the neighbours that are really in the transaction
  object.each_relation do |rel|
      object.each_parent_object(rel) do |parent|
    if parent_proxy = self[parent, false]
        parent_proxy.add_child_object(proxy, rel, parent[object, rel])
    end
      end

      object.each_child_object(rel) do |child|
    if child_proxy = self[child, false]
        proxy.add_child_object(child_proxy, rel, object[child, rel])
    end
      end
  end
    end
end

#disable_proxyingObject



416
417
418
419
420
421
422
423
424
425
# File 'lib/roby/transactions.rb', line 416

def disable_proxying
    @disable_proxying = true
    if block_given?
  begin
      yield
  ensure
      @disable_proxying = false
  end
    end
end

#discard(t) ⇒ Object



279
280
281
282
283
284
285
286
287
288
289
# File 'lib/roby/transactions.rb', line 279

def discard(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?
    if proxy = self[t, false]
  super(proxy)
    end

    t = may_unwrap(t)
    if t.plan == self.plan
  discarded_tasks.insert(t)
    end
end

#discard_modifications(object) ⇒ Object

Remove proxy from this transaction. While #remove_object is also removing the object from the plan itself, this method only removes it from the transaction, forgetting all modifications that have been done on object in the transaction



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/roby/transactions.rb', line 96

def discard_modifications(object)
    object = may_unwrap(object)
    if object.respond_to?(:each_plan_child)
  object.each_plan_child do |child|
      discard_modifications(child)
  end
    end
    removed_objects.delete(object)
    discarded_tasks.delete(object)
    auto_tasks.delete(object)

    return unless proxy = proxy_objects.delete(object)
    proxy.clear_vertex

    missions.delete(proxy)
    known_tasks.delete(proxy)
    free_events.delete(proxy)
end

#discard_transactionObject

Discard all the modifications that have been registered in this transaction



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/roby/transactions.rb', line 430

def discard_transaction
#    if !Roby.control.running?
# raise "#commit_transaction requires the presence of a control thread"
    if !transactions.empty?
  raise InvalidTransaction, "there is still transactions on top of this one"
    end

    freezed!
    proxy_objects.each_value { |proxy| proxy.discard_transaction }
    clear

    discarded_transaction
    Roby.execute do
  plan.remove_transaction(self)
    end
    @plan = nil
end

#discarded_transactionObject



447
# File 'lib/roby/transactions.rb', line 447

def discarded_transaction; super if defined? super end

#discover(objects) ⇒ Object



261
262
263
264
265
# File 'lib/roby/transactions.rb', line 261

def discover(objects)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?
    super(self[objects, true])
    self
end

#discover_neighborhood(object) ⇒ Object



227
228
229
230
231
232
233
# File 'lib/roby/transactions.rb', line 227

def discover_neighborhood(object)
    self[object]
    object.each_relation do |rel|
  object.each_parent_object(rel) { |obj| self[obj] }
  object.each_child_object(rel)  { |obj| self[obj] }
    end
end

#do_wrap(object, do_include = false) ⇒ Object

:nodoc:



20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/roby/transactions.rb', line 20

def do_wrap(object, do_include = false) # :nodoc:
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?

    proxy = proxy_objects[object] = Proxy.proxy_class(object).new(object, self)
    if do_include && object.root_object?
  proxy.plan = self
  discover(proxy)
    end

    copy_object_relations(object, proxy)
    proxy
end

#editObject



34
35
36
# File 'lib/roby/transactions.rb', line 34

def edit
    yield if block_given?
end

#enable_proxyingObject



415
# File 'lib/roby/transactions.rb', line 415

def enable_proxying; @disable_proxying = false end

#executable?Boolean

A transaction is not an executable plan

Returns:

  • (Boolean)


17
# File 'lib/roby/transactions.rb', line 17

def executable?; false end

#finalized?Boolean

Returns:

  • (Boolean)


413
# File 'lib/roby/transactions.rb', line 413

def finalized?; !plan end

#finalized_plan_event(event) ⇒ Object



62
63
64
65
66
# File 'lib/roby/transactions/updates.rb', line 62

def finalized_plan_event(event)
    invalidate("event #{event} has been removed from the plan")
    discard_modifications(event)
    conflict_solver.finalized_plan_event(self, event)
end

#finalized_plan_task(task) ⇒ Object



56
57
58
59
60
# File 'lib/roby/transactions/updates.rb', line 56

def finalized_plan_task(task)
    invalidate("task #{task} has been removed from the plan")
    discard_modifications(task)
    conflict_solver.finalized_plan_task(self, task)
end

#freezed!Object



449
450
451
# File 'lib/roby/transactions.rb', line 449

def freezed!
    @freezed = true
end

#freezed?Boolean

Returns:

  • (Boolean)


18
# File 'lib/roby/transactions.rb', line 18

def freezed?; @freezed end

#insert(t) ⇒ Object



247
248
249
250
251
252
253
# File 'lib/roby/transactions.rb', line 247

def insert(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?
    if proxy = self[t, false]
  discarded_tasks.delete(may_unwrap(proxy))
    end
    super(self[t, true]) 
end

#invalid=(flag) ⇒ Object



293
294
295
296
297
298
# File 'lib/roby/transactions.rb', line 293

def invalid=(flag)
    if !flag
  invalidation_reasons.clear
    end
    @invalid = flag
end

#invalid?Boolean

Returns:

  • (Boolean)


300
# File 'lib/roby/transactions.rb', line 300

def invalid?; @invalid end

#invalidate(reason = nil) ⇒ Object



302
303
304
305
306
307
308
# File 'lib/roby/transactions.rb', line 302

def invalidate(reason = nil)
    self.invalid = true
    invalidation_reasons << [reason, caller(1)] if reason
    Roby.debug do
  "invalidating #{self}: #{reason}"
    end
end

#may_unwrap(object) ⇒ Object

may_unwrap may return objects from transaction



166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/roby/transactions.rb', line 166

def may_unwrap(object)
    if object.respond_to?(:plan) 
  if object.plan == self && object.respond_to?(:__getobj__)
      object.__getobj__
  elsif object.plan == self.plan
      object
  else
      object
  end
    else object
    end
end

#may_wrap(object, create = true) ⇒ Object



161
162
163
# File 'lib/roby/transactions.rb', line 161

def may_wrap(object, create = true)
    (wrap(object, create) || object) rescue object 
end

#merged_generated_subgraphs(relation, plan_seeds, transaction_seeds) ⇒ Object

Returns two sets of tasks, [plan, transaction]. The union of the two is the component that would be returned by relation.generated_subgraphs(*seeds) if the transaction was committed



514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/roby/query.rb', line 514

def merged_generated_subgraphs(relation, plan_seeds, transaction_seeds)
    plan_set        = ValueSet.new
    transaction_set = ValueSet.new
    plan_seeds        = plan_seeds.to_value_set
    transaction_seeds = transaction_seeds.to_value_set

    loop do
  old_transaction_set = transaction_set.dup
  transaction_set.merge(transaction_seeds)
  for new_set in relation.generated_subgraphs(transaction_seeds, false)
      transaction_set.merge(new_set)
  end

  if old_transaction_set.size != transaction_set.size
      for o in (transaction_set - old_transaction_set)
    if o.respond_to?(:__getobj__)
        o.__getobj__.each_child_object(relation) do |child|
      plan_seeds << child unless self[child, false]
        end
    end
      end
  end
  transaction_seeds.clear

  plan_set.merge(plan_seeds)
  plan_seeds.each do |seed|
      relation.each_dfs(seed, BGL::Graph::TREE) do |_, dest, _, kind|
    next if plan_set.include?(dest)
    if self[dest, false]
        proxy = wrap(dest, false)
        unless transaction_set.include?(proxy)
      transaction_seeds << proxy
        end
        relation.prune # transaction branches must be developed inside the transaction
    else
        plan_set << dest
    end
      end
  end
  break if transaction_seeds.empty?

  plan_seeds.clear
    end

    [plan_set, transaction_set]
end

#permanent(t) ⇒ Object



254
255
256
257
258
259
260
# File 'lib/roby/transactions.rb', line 254

def permanent(t)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?
    if proxy = self[t, false]
  auto_tasks.delete(may_unwrap(proxy))
    end
    super(self[t, true]) 
end

#proposeObject



33
# File 'lib/roby/transactions.rb', line 33

def propose; end

#proxying?Boolean

Returns:

  • (Boolean)


426
# File 'lib/roby/transactions.rb', line 426

def proxying?; !@freezed && !@disable_proxying end

#query_each(result_set) ⇒ Object

Yields tasks in the result set of query. Unlike Query#result_set, all the tasks are included in the transaction



576
577
578
579
580
# File 'lib/roby/query.rb', line 576

def query_each(result_set)
    plan_set, trsc_set = result_set
    plan_set.each { |task| yield(self[task]) }
    trsc_set.each { |task| yield(task) }
end

#query_result_set(matcher) ⇒ Object

Returns [plan_set, transaction_set], where the first is the set of plan tasks matching matcher and the second the set of transaction tasks matching it. The two sets are disjoint.



564
565
566
567
568
569
570
571
572
# File 'lib/roby/query.rb', line 564

def query_result_set(matcher)
    plan_set = ValueSet.new
    for task in plan.query_result_set(matcher)
  plan_set << task unless self[task, false]
    end
    
    transaction_set = super
    [plan_set, transaction_set]
end

#query_roots(result_set, relation) ⇒ Object

Given the result set of query, returns the subset of tasks which have no parent in query



584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
# File 'lib/roby/query.rb', line 584

def query_roots(result_set, relation)
    plan_set      , trsc_set      = *result_set
    plan_result   , trsc_result   = ValueSet.new     , ValueSet.new
    plan_children , trsc_children = ValueSet.new     , ValueSet.new

    for task in plan_set
  next if plan_children.include?(task)
  task_plan_children, task_trsc_children = 
      merged_generated_subgraphs(relation, [task], [])

  plan_result -= task_plan_children
  trsc_result -= task_trsc_children
  plan_children.merge(task_plan_children)
  trsc_children.merge(task_trsc_children)

  plan_result << task
    end

    for task in trsc_set
  next if trsc_children.include?(task)
  task_plan_children, task_trsc_children = 
      merged_generated_subgraphs(relation, [], [task])

  plan_result -= task_plan_children
  trsc_result -= task_trsc_children
  plan_children.merge(task_plan_children)
  trsc_children.merge(task_trsc_children)

  trsc_result << task
    end

    [plan_result, trsc_result]
end

#remove_object(object) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/roby/transactions.rb', line 142

def remove_object(object)
    raise "transaction #{self} has been either committed or discarded. No modification allowed" if freezed?

    object = may_unwrap(object)
    proxy = proxy_objects[object] || object

    # removing the proxy may trigger some discovery (event relations
    # for instance, if proxy is a task). Do it first, or #discover
    # will be called and the modifications of internal structures
    # nulled (like #removed_objects) ...
    remove_plan_object(proxy)
    proxy_objects.delete(object)

    if object.plan == self.plan
  # +object+ is new in the transaction
  removed_objects.insert(object)
    end
end

#remove_plan_objectObject



141
# File 'lib/roby/transactions.rb', line 141

alias :remove_plan_object :remove_object

#removing_plan_relation(parent, child, relations) ⇒ Object



78
79
80
81
82
83
84
85
86
# File 'lib/roby/transactions/updates.rb', line 78

def removing_plan_relation(parent, child, relations)
    present_relations = relations.find_all do |rel|
  parent.child_object?(child, rel)
    end
    unless present_relations.empty?
  invalidate("plan removed the #{parent} -> #{child} relation in #{relations}")
  conflict_solver.removing_plan_relation(self, parent, child, relations)
    end
end

#replace(from, to) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
# File 'lib/roby/transactions.rb', line 235

def replace(from, to)
    # Make sure +from+, its events and all the related tasks and events
    # are in the transaction
    from = may_unwrap(from)
    discover_neighborhood(from)
    from.each_event do |ev|
  discover_neighborhood(ev)
    end

    super(self[from], self[to])
end

#restore_relation(proxy, relation) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/roby/transactions.rb', line 115

def restore_relation(proxy, relation)
    object = proxy.__getobj__

    Control.synchronize do
  proxy_children = proxy.child_objects(relation)
  object.child_objects(relation).each do |object_child| 
      next unless proxy_child = wrap(object_child, false)
      if proxy_children.include?(proxy_child)
    relation.unlink(proxy, proxy_child)
      end
  end

  proxy_parents = proxy.parent_objects(relation)
  object.parent_objects(relation).each do |object_parent| 
      next unless proxy_parent = wrap(object_parent, false)
      if proxy_parents.include?(proxy_parent)
    relation.unlink(parent, proxy_parent)
      end
  end
    end

    discovered_objects.delete(proxy)
    proxy.discovered_relations.delete(relation)
    proxy.do_discover(relation, false)
end

#valid_transaction?Boolean

Returns:

  • (Boolean)


301
# File 'lib/roby/transactions.rb', line 301

def valid_transaction?; transactions.empty? && !invalid? end

#wrap(object, create = true) ⇒ Object Also known as: []

Get the transaction proxy for object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/roby/transactions.rb', line 60

def wrap(object, create = true)
    if object.kind_of?(PlanObject)
  if object.plan == self then return object
  elsif proxy = proxy_objects[object] then return proxy
  end

  if create
      if !object.plan
    object.plan = self
    discover(object)
    return object
      elsif object.plan == self.plan
    wrapped = do_wrap(object, true)
    if plan.mission?(object)
        insert(wrapped)
    elsif plan.permanent?(object)
        permanent(wrapped)
    end
    return wrapped
      else
    raise ArgumentError, "#{object} is in #{object.plan}, this transaction #{self} applies on #{self.plan}"
      end
  end
  nil
    elsif object.respond_to?(:each) 
  object.map { |o| wrap(o, create) }
    else
  raise TypeError, "don't know how to wrap #{object || 'nil'} of type #{object.class.ancestors}"
    end
end