Class: DataMapper::Transaction

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

Defined Under Namespace

Modules: Adapter, Model, Repository, Resource

Instance Method Summary collapse

Methods included from Chainable

chainable, extendable

Constructor Details

#initialize(*things) ⇒ Transaction

Create a new Transaction

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

See Also:



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

def initialize(*things)
  @transaction_primitives = {}
  @state = :none
  @adapters = {}
  link(*things)
  if block_given?
    warn "Passing block to #{self.class.name}.new is deprecated (#{caller[0]})"
    commit { |*block_args| yield(*block_args) }
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

TODO: document



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/dm-core/transaction.rb', line 177

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

Instance Method Details

#beginObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Begin the transaction

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



80
81
82
83
84
85
86
87
88
# File 'lib/dm-core/transaction.rb', line 80

def begin
  unless @state == :none
    raise "Illegal state for begin: #{@state}"
  end

  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

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Commit the transaction

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

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.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/dm-core/transaction.rb', line 100

def commit
  if block_given?
    unless @state == :none
      raise "Illegal state for commit with block: #{@state}"
    end

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

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Associate this Transaction with some things.

Parameters:

  • things (Object)

    the things you want this Transaction associated with:

    Adapters::AbstractAdapter subclasses will be added as

    adapters as is.
    

    Arrays will have their elements added. Repository will have it’s own @adapters added. Resource subclasses will have all the repositories of all

    their properties added.
    

    Resource instances will have all repositories of all their

    properties added.
    
  • block (Proc)

    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.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/dm-core/transaction.rb', line 46

def link(*things)
  unless @state == :none
    raise "Illegal state for link: #{@state}"
  end

  things.each do |thing|
    case thing
      when DataMapper::Adapters::AbstractAdapter
        @adapters[thing] = :none
      when DataMapper::Repository
        link(thing.adapter)
      when DataMapper::Model
        link(*thing.repositories)
      when DataMapper::Resource
        link(thing.model)
      when Array
        link(*thing)
      else
        raise "Unknown argument to #{self.class}#link: #{thing.inspect} (#{thing.class})"
    end
  end

  if block_given?
    commit { |*block_args| yield(*block_args) }
  else
    self
  end
end

#primitive_for(adapter) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

TODO: document



205
206
207
208
209
210
211
212
213
214
215
# File 'lib/dm-core/transaction.rb', line 205

def primitive_for(adapter)
  unless @adapters.include?(adapter)
    raise "Unknown adapter #{adapter}"
  end

  unless @transaction_primitives.include?(adapter)
    raise "No primitive for #{adapter}"
  end

  @transaction_primitives[adapter]
end

#rollbackObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Rollback the transaction

Will undo all changes made during the transaction.



134
135
136
137
138
139
140
141
# File 'lib/dm-core/transaction.rb', line 134

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

#withinObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Execute a block within this Transaction.

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.

Parameters:

  • block (Block)

    the block of code to execute.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/dm-core/transaction.rb', line 153

def within
  unless block_given?
    raise 'No block provided'
  end

  unless @state == :begin
    raise "Illegal state for within: #{@state}"
  end

  @adapters.each do |adapter, state|
    adapter.push_transaction(self)
  end

  begin
    yield self
  ensure
    @adapters.each do |adapter, state|
      adapter.pop_transaction
    end
  end
end