Class: DataMapper::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/dm-core/transaction.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*things) ⇒ Transaction

Create a new DataMapper::Transaction

In fact, it just calls #link with the given arguments at the end of the constructor.

See Also:



16
17
18
19
20
21
22
# File 'lib/dm-core/transaction.rb', line 16

def initialize(*things)
  @transaction_primitives = {}
  @state = :none
  @adapters = {}
  link(*things)
  commit { |*block_args| yield(*block_args) } if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/dm-core/transaction.rb', line 146

def method_missing(meth, *args, &block)
  if args.size == 1 && args.first.is_a?(DataMapper::Adapters::AbstractAdapter)
    if (match = meth.to_s.match(/^(.*)_if_(none|begin|prepare|rollback|commit)$/))
      if self.respond_to?(match[1], true)
        self.send(match[1], args.first) if state_for(args.first).to_s == match[2]
      else
        super
      end
    elsif (match = meth.to_s.match(/^(.*)_unless_(none|begin|prepare|rollback|commit)$/))
      if self.respond_to?(match[1], true)
        self.send(match[1], args.first) unless state_for(args.first).to_s == match[2]
      else
        super
      end
    else
      super
    end
  else
    super
  end
end

Instance Attribute Details

#adaptersObject (readonly)

Returns the value of attribute adapters.



6
7
8
# File 'lib/dm-core/transaction.rb', line 6

def adapters
  @adapters
end

#stateObject (readonly)

Returns the value of attribute state.



6
7
8
# File 'lib/dm-core/transaction.rb', line 6

def state
  @state
end

#transaction_primitivesObject (readonly)

Returns the value of attribute transaction_primitives.



6
7
8
# File 'lib/dm-core/transaction.rb', line 6

def transaction_primitives
  @transaction_primitives
end

Instance Method Details

#beginObject

Begin the transaction

Before #begin is called, the transaction is not valid and can not be used.



68
69
70
71
72
73
# File 'lib/dm-core/transaction.rb', line 68

def begin
  raise "Illegal state for begin: #{@state}" unless @state == :none
  each_adapter(:connect_adapter, [:log_fatal_transaction_breakage])
  each_adapter(:begin_adapter, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
  @state = :begin
end

#commitObject

Note:

If no block is given, it will simply commit any changes made since the Transaction did #begin.

Commit the transaction

Parameters:

  • block (Block)

    a block (taking the one argument, the Transaction) to execute within this transaction. The transaction will begin and commit around the block, and roll back if an exception is raised.



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/dm-core/transaction.rb', line 86

def commit
  if block_given?
    raise "Illegal state for commit with block: #{@state}" unless @state == :none
    begin
      self.begin
      rval = within { |*block_args| yield(*block_args) }
      self.commit if @state == :begin
      return rval
    rescue Exception => e
      self.rollback if @state == :begin
      raise e
    end
  else
    raise "Illegal state for commit without block: #{@state}" unless @state == :begin
    each_adapter(:prepare_adapter, [:rollback_and_close_adapter_if_begin, :rollback_prepared_and_close_adapter_if_prepare])
    each_adapter(:commit_adapter, [:log_fatal_transaction_breakage])
    each_adapter(:close_adapter, [:log_fatal_transaction_breakage])
    @state = :commit
  end
end

Associate this Transaction with some things.

Parameters:

  • things (any number of Object)

    the things you want this Transaction associated with

  • block (Block)

    a block (taking one argument, the Transaction) to execute within this transaction. The transaction will begin and commit around the block, and rollback if an exception is raised.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/dm-core/transaction.rb', line 42

def link(*things)
  raise "Illegal state for link: #{@state}" unless @state == :none
  things.each do |thing|
    if thing.is_a?(Array)
      link(*thing)
    elsif thing.is_a?(DataMapper::Adapters::AbstractAdapter)
      @adapters[thing] = :none
    elsif thing.is_a?(DataMapper::Repository)
      link(thing.adapter)
    elsif thing.is_a?(Class) && thing.ancestors.include?(DataMapper::Resource)
      link(*thing.repositories)
    elsif thing.is_a?(DataMapper::Resource)
      link(thing.model)
    else
      raise "Unknown argument to #{self}#link: #{thing.inspect}"
    end
  end
  return commit { |*block_args| yield(*block_args) } if block_given?
  return self
end

#primitive_for(adapter) ⇒ Object



168
169
170
171
172
# File 'lib/dm-core/transaction.rb', line 168

def primitive_for(adapter)
  raise "Unknown adapter #{adapter}" unless @adapters.include?(adapter)
  raise "No primitive for #{adapter}" unless @transaction_primitives.include?(adapter)
  @transaction_primitives[adapter]
end

#rollbackObject

Rollback the transaction

Will undo all changes made during the transaction.



112
113
114
115
116
117
118
# File 'lib/dm-core/transaction.rb', line 112

def rollback
  raise "Illegal state for rollback: #{@state}" unless @state == :begin
  each_adapter(:rollback_adapter_if_begin, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
  each_adapter(:rollback_prepared_adapter_if_prepare, [:rollback_prepared_and_close_adapter_if_begin, :close_adapter_if_none])
  each_adapter(:close_adapter_if_open, [:log_fatal_transaction_breakage])
  @state = :rollback
end

#withinObject

Note:

No #begin, #commit or #rollback is performed in #within, but this Transaction will pushed on the per thread stack of transactions for each adapter it is associated with, and it will ensures that it will pop the Transaction away again after the block is finished.

Execute a block within this Transaction.

Parameters:

  • block (Block)

    the block of code to execute.



131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/dm-core/transaction.rb', line 131

def within
  raise "No block provided" unless block_given?
  raise "Illegal state for within: #{@state}" unless @state == :begin
  @adapters.each do |adapter, state|
    adapter.push_transaction(self)
  end
  begin
    return yield(self)
  ensure
    @adapters.each do |adapter, state|
      adapter.pop_transaction
    end
  end
end