[
  {
    "path": ".travis.yml",
    "content": "language: ruby\nsudo: false\ncache: bundler\nbundler_args: '--path vendor/bundle'\ngemfile:\n  - gemfiles/32.gemfile\n  - gemfiles/40.gemfile\n  - gemfiles/41.gemfile\n  - gemfiles/42.gemfile\nrvm:\n  - 2.0.0\n  - 2.1.8\n  - 2.2.4\nenv:\n  - REAL=1\n  - TEST=1\nscript: bundle exec rake spec\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"https://rubygems.org\"\ngemspec\n\ngem 'rails-observers'\n"
  },
  {
    "path": "MIT-LICENSE",
    "content": "Copyright (c) 2014 Michael Grosser <michael@grosser.it>\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Rakefile",
    "content": "require 'bundler/setup'\nrequire 'bundler/gem_tasks'\nrequire 'bump/tasks'\nrequire 'wwtd/tasks'\n\ntask :spec do\n  sh \"rspec spec/\"\nend\n\ntask :default => \"wwtd:local\"\n"
  },
  {
    "path": "Readme.md",
    "content": "Make after_commit callbacks fire in tests for Rails 3+ with transactional_fixtures = true.\n\n**Deprecation** this is no longer needed on rails 5.0+ https://github.com/rails/rails/pull/18458\n\nInstall\n=======\n\n    gem install test_after_commit\n\n    # Gemfile (never include in :development !)\n    gem 'test_after_commit', :group => :test\n\nUsage\n=====\nTest that the methods get called or the side-effect of the methods, something like:\n\n```Ruby\nclass Car < ActiveRecord::Base\n  after_commit :foo, :on => :update\n\n  def foo\n    $foo = 1\n  end\nend\n\n...\n\nit \"sets $foo on commit\" do\n  $foo.should == nil\n  car.save!\n  $foo.should == 1\nend\n```\n\n### Temporary disable after commit hooks\n\nIn your test_helper, you can specify the default\n\n```\nTestAfterCommit.enabled = true\n```\n\nThen use blocks in your tests to change the behavior:\n\n```\nTestAfterCommit.with_commits(true) do\n  my_tests\nend\n\nTestAfterCommit.with_commits(false) do\n  my_tests\nend\n```\n\nTIPS\n====\n - hooks do not re-raise errors (with or without this gem) use [after_commit_exception_notification](https://github.com/grosser/after_commit_exception_notification)\n\nAuthor\n======\n\nInspired by https://gist.github.com/1305285\n\n### [Contributors](https://github.com/grosser/test_after_commit/contributors)\n - [James Le Cuirot](https://github.com/chewi)\n - [emirose](https://github.com/emirose)\n - [Brad Gessler](https://github.com/bradgessler)\n - [Rohan McGovern](https://github.com/rohanpm)\n - [lsylvester](https://github.com/lsylvester)\n - [Tony Novak](https://github.com/afn)\n - [Brian Palmer](https://github.com/codekitchen)\n - [Oleg Dashevskii](https://github.com/be9)\n - [Jonathan Spies](https://github.com/jspies)\n - [Nick Sieger](https://github.com/nicksieger)\n\n[Michael Grosser](http://grosser.it)<br/>\nmichael@grosser.it<br/>\nLicense: MIT<br/>\n[![Build Status](https://travis-ci.org/grosser/test_after_commit.png)](https://travis-ci.org/grosser/test_after_commit)\n"
  },
  {
    "path": "gemfiles/32.gemfile",
    "content": "source \"https://rubygems.org\"\n\ngemspec :path=>\"../\"\n\ngem \"activerecord\", \"~> 3.2.18\"\ngem \"i18n\", \"~> 0.6.9\" # 0.7 does not support 1.9\ngem \"bump\", \"0.5.2\" # after that it's 1.9.3 only\n"
  },
  {
    "path": "gemfiles/40.gemfile",
    "content": "source \"https://rubygems.org\"\n\ngemspec :path=>\"../\"\n\ngem \"activerecord\", \"~> 4.0.6\"\ngem \"rails-observers\"\n"
  },
  {
    "path": "gemfiles/41.gemfile",
    "content": "source \"https://rubygems.org\"\n\ngemspec :path=>\"../\"\n\ngem \"activerecord\", \"~> 4.1.2\"\ngem \"rails-observers\"\n"
  },
  {
    "path": "gemfiles/42.gemfile",
    "content": "source \"https://rubygems.org\"\n\ngemspec :path=>\"../\"\n\ngem \"activerecord\", \"~> 4.2.0.beta1\"\ngem \"rails-observers\"\n"
  },
  {
    "path": "lib/test_after_commit/database_statements.rb",
    "content": "module TestAfterCommit::DatabaseStatements\n  def transaction(*)\n    @test_open_transactions ||= 0\n    skip_emulation = ActiveRecord::Base.connection.open_transactions.zero?\n    run_callbacks = false\n    result = nil\n    rolled_back = false\n\n    super do\n      begin\n        @test_open_transactions += 1\n        if ActiveRecord::VERSION::MAJOR == 3\n          @_current_transaction_records.push([]) if @_current_transaction_records.empty?\n        end\n        result = yield\n      rescue Exception\n        rolled_back = true\n        raise\n      ensure\n        @test_open_transactions -= 1\n        if @test_open_transactions == 0 && !rolled_back && !skip_emulation\n          if TestAfterCommit.enabled\n            run_callbacks = true\n          elsif ActiveRecord::VERSION::MAJOR == 3\n            @_current_transaction_records.clear\n          end\n        end\n      end\n    end\n  ensure\n    test_commit_records if run_callbacks\n    result\n  end\n\n  def test_commit_records\n    if ActiveRecord::VERSION::MAJOR == 3\n      commit_transaction_records\n    else\n      # To avoid an infinite loop, we need to copy the transaction locally, and clear out\n      # `records` on the copy that stays in the AR stack. Otherwise new\n      # transactions inside a commit callback will cause an infinite loop.\n      #\n      # This is because we're re-using the transaction on the stack, before\n      # it's been popped off and re-created by the AR code.\n      original = @transaction || @transaction_manager.current_transaction\n\n      return unless original.respond_to?(:records)\n\n      transaction = original.dup\n      transaction.instance_variable_set(:@records, transaction.records.dup) # deep clone of records array\n      original.records.clear                                                # so that this clear doesn't clear out both copies\n      transaction.commit_records\n    end\n  end\nend\n"
  },
  {
    "path": "lib/test_after_commit/version.rb",
    "content": "module TestAfterCommit\n  VERSION = '1.2.2'\nend\n"
  },
  {
    "path": "lib/test_after_commit/with_transaction_state.rb",
    "content": "# disable parts of the sync code that starts looping\nmodule TestAfterCommit\n  module WithTransactionState\n    def sync_with_transaction_state\n      @reflects_state ||= []\n      @reflects_state[0] = true\n      super\n    end\n  end\nend\n"
  },
  {
    "path": "lib/test_after_commit.rb",
    "content": "require 'test_after_commit/version'\n\nif ActiveRecord::VERSION::MAJOR >= 5\n  raise 'after_commit testing is baked into rails 5, you no longer need test_after_commit gem'\nend\n\nif ActiveRecord::VERSION::MAJOR >= 4\n  require 'test_after_commit/with_transaction_state'\n  ActiveRecord::Base.send(:prepend, TestAfterCommit::WithTransactionState)\nend\n\nrequire 'test_after_commit/database_statements'\nActiveRecord::ConnectionAdapters::AbstractAdapter.send(:prepend, TestAfterCommit::DatabaseStatements)\n\nmodule TestAfterCommit\n  @enabled = true\n  class << self\n    attr_accessor :enabled\n\n    def with_commits(value = true)\n      old = enabled\n      self.enabled = value\n      yield\n    ensure\n      self.enabled = old\n    end\n  end\nend\n"
  },
  {
    "path": "spec/database.rb",
    "content": "# setup database\nrequire 'active_record'\n\nif ActiveRecord::VERSION::MAJOR > 3\n  require \"rails/observers/activerecord/active_record\"\nend\n\nif ActiveRecord::VERSION::STRING >= \"4.2.0\"\n  ActiveRecord::Base.raise_in_transactional_callbacks = true\nend\n\nActiveRecord::Base.establish_connection(\n  :adapter  => 'sqlite3',\n  :database => ':memory:'\n)\n\nActiveRecord::Migration.verbose = false\n\nActiveRecord::Schema.define(:version => 1) do\n  create_table \"cars\", :force => true do |t|\n    t.integer :counter, :default => 0, :null => false\n    t.integer :car_id\n    t.timestamps :null => false\n  end\n\n  create_table \"addresses\", :force => true do |t|\n    t.integer :number_of_residents, :default => 0, :null => false\n    t.timestamps :null => false\n  end\n\n  create_table \"people\", :force => true do |t|\n    t.belongs_to :address\n    t.timestamps :null => false\n  end\n\n  create_table \"fu_bears\", :force => true do |t|\n    t.string :name\n    t.timestamps :null => false\n  end\nend\n\nmodule Called\n  def called(x=nil)\n    @called ||= []\n    if x\n      @called << x\n    else\n      @called\n    end\n  end\nend\n\nclass Car < ActiveRecord::Base\n  extend Called\n\n  has_many :cars\n\n  after_commit :simple_after_commit\n  after_commit :simple_after_commit_on_create, :on => :create\n  after_commit :save_once, :on => :create, :if => :do_after_create_save\n  after_commit :simple_after_commit_on_update, :on => :update\n  after_commit :maybe_raise_errors\n  after_commit :save_open_transactions_count\n\n  after_save :trigger_rollback\n\n  attr_accessor :make_rollback, :raise_error, :do_after_create_save\n\n  def trigger_rollback\n    raise ActiveRecord::Rollback if make_rollback\n  end\n\n  def self.returning_method_with_transaction\n    Car.transaction do\n      return Car.create\n    end\n  end\n\n  attr_reader :open_transactions\n\n  private\n\n  def save_open_transactions_count\n    @open_transactions = ActiveRecord::Base.connection.open_transactions\n  end\n\n  def save_once\n    update_attributes(:counter => 3) unless counter == 3\n    self.class.called :save_once\n  end\n\n  def maybe_raise_errors\n    if raise_error\n      # puts \"MAYBE RAISE\" # just debugging, but it really does not work ...\n      raise \"Expected error\"\n    end\n  end\n\n  def simple_after_commit\n    self.class.called :always\n  end\n\n  def simple_after_commit_on_create\n    self.class.called :create\n  end\n\n  def simple_after_commit_on_update\n    self.class.called :update\n  end\nend\n\nclass CarObserver < ActiveRecord::Observer\n  cattr_accessor :recording\n  cattr_accessor :callback\n\n  [:after_commit, :after_rollback].each do |action|\n    define_method action do |record|\n      return unless recording\n      Car.called << \"observed_#{action}\".to_sym\n      Untracked.create!\n      callback.call() if callback\n    end\n  end\nend\n\nCar.observers = :car_observer\nCar.instantiate_observers\n\nclass Bar < ActiveRecord::Base\n  self.table_name = \"cars\"\n  has_many :bars, :foreign_key => :car_id\nend\n\nclass MultiBar < ActiveRecord::Base\n  extend Called\n\n  self.table_name = \"cars\"\n\n  after_commit :one, :on => :create\n  after_commit :two, :on => :create\n\n  def one\n    self.class.called << :one\n  end\n\n  def two\n    self.class.called << :two\n  end\nend\n\nclass Address < ActiveRecord::Base\n  has_many :people\n\n  after_commit :create_residents, :on => :create\n\n  def create_residents\n    if ActiveRecord::VERSION::MAJOR == 3\n      # stupid hack because nested after_commit is broken on rails 3 and loops\n      return if @create_residents\n      @create_residents = true\n    end\n\n    Person.create!(:address => self)\n    Person.create!(:address => self)\n  end\nend\n\nclass Person < ActiveRecord::Base\n  belongs_to :address\n\n  after_commit :update_number_of_residents_on_address, :on => :create\n\n  def update_number_of_residents_on_address\n    address.update_attributes(:number_of_residents => address.number_of_residents + 1)\n  end\nend\n\nclass Untracked < ActiveRecord::Base\n  self.table_name = \"cars\"\nend\n\nclass FuBear < ActiveRecord::Base\n  extend Called\n\n  self.table_name = \"fu_bears\"\n  validates_presence_of :name\n\n  after_commit :simple_after_commit\n\n  def simple_after_commit\n    self.class.called :always\n  end\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "require 'bundler/setup'\nrequire File.expand_path '../database', __FILE__\nI18n.enforce_available_locales = false\n\ndef rails3?\n  ActiveRecord::VERSION::MAJOR == 3\nend\n\ndef rails4?\n  ActiveRecord::VERSION::MAJOR >= 4\nend\n\ndef rails42?\n  rails4? && ActiveRecord::VERSION::MINOR >= 2\nend\n\nrequire 'test_after_commit'\nif ENV['REAL']\n  puts 'using real transactions'\n  TestAfterCommit.enabled = false\nend\n\nmodule ConnectionFinder\n  def connection\n    @connection ||=\n      if rails4?\n        ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection).first\n      else\n        ActiveRecord::Base.connection_handler.connection_pools.values.map(&:connection).first\n      end\n  end\nend\n\nRSpec.configure do |config|\n  config.include ConnectionFinder\n\n  unless ENV['REAL']\n    config.around do |example|\n      # open a transaction without using .transaction as activerecord use_transactional_fixtures does\n      if ActiveRecord::VERSION::MAJOR > 3\n        connection.begin_transaction :joinable => false\n      else\n        connection.increment_open_transactions\n        connection.transaction_joinable = false\n        connection.begin_db_transaction\n      end\n\n      example.call\n\n      connection.rollback_db_transaction\n      if ActiveRecord::VERSION::MAJOR == 3\n        connection.decrement_open_transactions\n      end\n    end\n  end\n\n  config.expect_with(:rspec) { |c| c.syntax = :should }\n  config.mock_with(:rspec) { |c| c.syntax = :should }\nend\n"
  },
  {
    "path": "spec/test_after_commit_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe TestAfterCommit do\n  before do\n    CarObserver.recording = false\n    Car.called.clear\n  end\n\n  after do\n    TestAfterCommit.enabled = true unless ENV[\"REAL\"]\n  end\n\n  it \"has a VERSION\" do\n    TestAfterCommit::VERSION.should =~ /^[\\.\\da-z]+$/\n  end\n\n  it \"fires on create\" do\n    Car.create\n    Car.called.should == [:create, :always]\n  end\n\n  it \"runs callback outside of transaction\" do\n    car = Car.create\n    car.open_transactions.should == ActiveRecord::Base.connection.open_transactions\n  end\n\n  it \"works outside of transaction\" do\n    car = described_class.with_commits(true) { Car.create }\n    car.destroy\n  end if ENV[\"REAL\"]\n\n  it \"fires on update\" do\n    car = Car.create\n    Car.called.clear\n    car.save!\n    Car.called.should == [:update, :always]\n  end\n\n  it \"fires on update_attribute\" do\n    car = Car.create\n    Car.called.clear\n    car.update_attribute :counter, 123\n    Car.called.should == [:update, :always]\n  end\n\n  it \"does not fire on rollback\" do\n    car = Car.new\n    car.make_rollback = true\n    car.save.should == nil\n    Car.called.should == []\n  end\n\n  it \"does not fire on ActiveRecord::RecordInvalid\" do\n    lambda {\n      FuBear.create!\n    }.should raise_exception(ActiveRecord::RecordInvalid)\n    FuBear.called.should == []\n  end\n\n  it \"does not fire multiple times in nested transactions\" do\n    Car.transaction do\n      Car.transaction do\n        Car.create!\n        Car.called.should == []\n      end\n      Car.called.should == []\n    end\n    Car.called.should == [:create, :always]\n  end\n\n  it \"fires when transaction block returns from method\" do\n    Car.returning_method_with_transaction\n    Car.called.should == [:create, :always]\n  end\n\n  if rails42?\n    it \"raises errors\" do\n      car = Car.new\n      car.raise_error = true\n      lambda { car.save! }.should raise_error(RuntimeError)\n    end\n  else\n    it \"does not raises errors\" do\n      car = Car.new\n      car.raise_error = true\n      car.save!\n    end\n  end\n\n  if rails42?\n    context \"with config.active_record.raise_in_transactional_callbacks\" do\n      around do |test|\n        old = ActiveRecord::Base.raise_in_transactional_callbacks\n        ActiveRecord::Base.raise_in_transactional_callbacks = true\n        begin\n          test.call\n        ensure\n          ActiveRecord::Base.raise_in_transactional_callbacks = old\n        end\n      end\n\n      it \"keeps working after an exception is raised\" do\n        car = Car.new\n        car.raise_error = true\n        lambda { car.save! }.should raise_error(RuntimeError)\n\n        car = Car.new\n        car.save!\n        Car.called.should include(:always)\n      end\n    end\n  end\n\n  it \"can do 1 save in after_commit\" do\n    car = Car.new\n    car.do_after_create_save = true\n    car.save!\n\n    expected = if rails4?\n      [:save_once, :create, :always, :save_once, :always]\n    else\n      [:save_once, :create, :always, :save_once, :create, :always]\n    end\n    Car.called.should == expected\n    car.counter.should == 3\n  end\n\n  it \"returns on create and on create of associations\" do\n    Car.create!.class.should == Car\n    Car.create!.cars.create.class.should == Car unless rails4?\n  end\n\n  it \"returns on create and on create of associations without after_commit\" do\n    Bar.create!.class.should == Bar\n    Bar.create!.bars.create.class.should == Bar unless rails4?\n  end\n\n  it \"calls callbacks in correct order\" do\n    MultiBar.create!\n    MultiBar.called.should == [:two, :one]\n  end\n\n  context \"Observer\" do\n    before do\n      CarObserver.recording = true\n    end\n\n    it \"should record commits\" do\n      Car.transaction do\n        Car.create\n      end\n      Car.called.should == [:observed_after_commit, :create, :always]\n    end\n\n    it \"should record rollbacks caused by ActiveRecord::Rollback\" do\n      Car.transaction do\n        Car.create\n        raise ActiveRecord::Rollback\n      end\n      Car.called.should == [:observed_after_rollback]\n    end\n\n    it \"should record rollbacks caused by any type of exception\" do\n      begin\n        Car.transaction do\n          car = Car.create\n          raise Exception, 'simulated error'\n        end\n      rescue Exception => e\n        e.message.should == 'simulated error'\n      end\n      Car.called.should == [:observed_after_rollback]\n    end\n\n    it \"should see the correct number of open transactions during callbacks\" do\n      skip if ENV[\"REAL\"]\n      begin\n        open_txn = nil\n        CarObserver.callback = proc { open_txn = Car.connection.instance_variable_get(:@test_open_transactions) }\n        Car.transaction do\n          Car.create\n        end\n        open_txn.should == 0\n      ensure\n        CarObserver.callback = nil\n      end\n    end\n  end\n\n  context \"block behavior\" do\n    it \"does not fire if turned off\" do\n      TestAfterCommit.enabled = false\n      Car.create\n      Car.called.should == []\n    end\n\n    it \"always fires with when enabled by a block\" do\n      TestAfterCommit.enabled = false\n      TestAfterCommit.with_commits(true) do\n        Car.create\n        Car.called.should == [:create, :always]\n      end\n    end\n\n    it \"defaults to with commits\" do\n      TestAfterCommit.with_commits do\n        Car.create\n        Car.called.should == [:create, :always]\n      end\n    end\n\n    it \"does not fire with without commits\" do\n      TestAfterCommit.with_commits(false) do\n        Car.create\n        Car.called.should == []\n      end\n    end\n  end unless ENV[\"REAL\"]\n\n  context \"nested after_commit\" do\n    it 'is executed' do\n      skip if rails4? # infinite loop in REAL and fails in TEST and lots of noise when left as pending\n\n      @address = Address.create!\n      lambda {\n        Person.create!(:address => @address)\n      }.should change(@address, :number_of_residents).by(1)\n\n      # one from the line above and two from the after_commit\n      @address.people.count.should == 3\n\n      @address.number_of_residents.should == 3\n    end\n  end\nend\n\nif rails3? && !ENV[\"REAL\"]\n  describe TestAfterCommit, \"with mixed TAC enabled specs\" do\n    before do\n      TestAfterCommit.enabled = false\n      Car.called.clear\n    end\n\n    context \"and a test with TAC disabled\" do\n      it \"creates a record\" do\n        Car.new.save!\n        Car.called.should == []\n      end\n\n      it \"verifies that records are empty before each test 1\" do\n        connection.instance_variable_get(:@_current_transaction_records).should be_empty\n      end\n    end\n\n    context \"and a test with TAC enabled\" do\n      before { TestAfterCommit.enabled = true }\n\n      it \"creates a record and fires commit callbacks\" do\n        Car.new.save!\n        Car.called.should == [:create, :always]\n      end\n\n      it \"verifies that records are empty before each test 2\" do\n        connection.instance_variable_get(:@_current_transaction_records).should be_empty\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test_after_commit.gemspec",
    "content": "name = \"test_after_commit\"\nrequire \"./lib/#{name}/version\"\n\nGem::Specification.new name, TestAfterCommit::VERSION do |s|\n  s.summary = \"makes after_commit callbacks testable in Rails 3+ with transactional_fixtures\"\n  s.authors = [\"Michael Grosser\"]\n  s.email = \"michael@grosser.it\"\n  s.homepage = \"https://github.com/grosser/#{name}\"\n  s.files = `git ls-files lib Readme.md MIT-LICENSE`.split(\"\\n\")\n  s.license = 'MIT'\n\n  s.required_ruby_version = '>= 2.0.0'\n  s.add_runtime_dependency \"activerecord\", [\">= 3.2\", \"< 5.0\"]\n  \n  s.add_development_dependency \"wwtd\"\n  s.add_development_dependency \"bump\"\n  s.add_development_dependency \"rake\"\n  s.add_development_dependency \"sqlite3\"\n  s.add_development_dependency \"rspec\"\nend\n"
  }
]