double_entry 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -16,6 +16,7 @@ spec/support/database.yml
16
16
  test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
+ db/*.sqlite3
19
20
  bin/
20
21
  log/
21
22
  /Gemfile.lock
data/.travis.yml CHANGED
@@ -6,6 +6,7 @@ rvm:
6
6
  env:
7
7
  - DB=mysql
8
8
  - DB=postgres
9
+ - DB=sqlite
9
10
  before_script:
10
11
  - cp spec/support/database.travis.yml spec/support/database.yml
11
12
  - mysql -e 'create database double_entry_test;'
data/README.md CHANGED
@@ -169,14 +169,7 @@ require 'double_entry'
169
169
 
170
170
  DoubleEntry.configure do |config|
171
171
  config.define_accounts do |accounts|
172
- user_scope = lambda do |user_identifier|
173
- if user_identifier.is_a?(User)
174
- user_identifier.id
175
- else
176
- user_identifier
177
- end
178
- end
179
-
172
+ user_scope = accounts.active_record_scope_identifier(User)
180
173
  accounts.define(:identifier => :savings, :scope_identifier => user_scope, :positive_only => true)
181
174
  accounts.define(:identifier => :checking, :scope_identifier => user_scope)
182
175
  end
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ RSpec::Core::RakeTask.new(:spec) do |t|
6
6
  end
7
7
 
8
8
  task :default do
9
- %w(mysql postgres).each do |db|
9
+ %w(mysql postgres sqlite).each do |db|
10
10
  puts "Running tests with `DB=#{db}`"
11
11
  ENV['DB'] = db
12
12
  Rake::Task["spec"].execute
data/db/.gitkeep ADDED
File without changes
data/double_entry.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |gem|
28
28
  gem.add_development_dependency 'rake'
29
29
  gem.add_development_dependency 'mysql2'
30
30
  gem.add_development_dependency 'pg'
31
+ gem.add_development_dependency 'sqlite3'
31
32
  gem.add_development_dependency 'rspec'
32
33
  gem.add_development_dependency 'rspec-its'
33
34
  gem.add_development_dependency 'database_cleaner'
@@ -20,7 +20,7 @@ module ActiveRecord
20
20
  begin
21
21
  yield
22
22
  rescue ActiveRecord::StatementInvalid => exception
23
- if exception.message =~ /deadlock/i
23
+ if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
24
24
  raise ActiveRecord::RestartTransaction
25
25
  else
26
26
  raise
@@ -29,18 +29,41 @@ module ActiveRecord
29
29
  end
30
30
 
31
31
  # Create the record, but ignore the exception if there's a duplicate.
32
+ # if there is a deadlock, retry
32
33
  def create_ignoring_duplicates!(*args)
34
+ retry_deadlocks do
35
+ ignoring_duplicates do
36
+ create!(*args)
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def ignoring_duplicates
33
44
  # Error examples:
34
- # PG::Error: ERROR: deadlock detected
35
- # Mysql::Error: Deadlock found when trying to get lock
36
45
  # PG::Error: ERROR: duplicate key value violates unique constraint
37
46
  # Mysql2::Error: Duplicate entry 'keith' for key 'index_users_on_username': INSERT INTO `users...
47
+ # ActiveRecord::RecordNotUnique SQLite3::ConstraintException: column username is not unique: INSERT INTO "users"...
38
48
  begin
39
- create!(*args)
40
- rescue ActiveRecord::StatementInvalid => exception
41
- if exception.message =~ /duplicate/i
49
+ yield
50
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
51
+ if exception.message =~ /duplicate/i || exception.message =~ /(is|are) not unique/i
42
52
  # Just ignore it...someone else has already created the record.
43
- elsif exception.message =~ /deadlock/i
53
+ else
54
+ raise
55
+ end
56
+ end
57
+ end
58
+
59
+ def retry_deadlocks
60
+ # Error examples:
61
+ # PG::Error: ERROR: deadlock detected
62
+ # Mysql::Error: Deadlock found when trying to get lock
63
+ begin
64
+ yield
65
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
66
+ if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
44
67
  # Somebody else is in the midst of creating the record. We'd better
45
68
  # retry, so we ensure they're done before we move on.
46
69
  retry
@@ -49,7 +72,6 @@ module ActiveRecord
49
72
  end
50
73
  end
51
74
  end
52
-
53
75
  end
54
76
 
55
77
  # Raise this inside a restartable_transaction to retry the transaction from the beginning.
@@ -29,23 +29,30 @@ module DoubleEntry
29
29
  super(account)
30
30
  end
31
31
  end
32
+
33
+ def active_record_scope_identifier(active_record_class)
34
+ ActiveRecordScopeFactory.new(active_record_class).scope_identifier
35
+ end
36
+ end
37
+
38
+ class ActiveRecordScopeFactory
39
+ def initialize(active_record_class)
40
+ @active_record_class = active_record_class
41
+ end
42
+
43
+ def scope_identifier
44
+ ->(value) { value.is_a?(@active_record_class) ? value.id : value }
45
+ end
32
46
  end
33
47
 
34
48
  class Instance
35
49
  attr_accessor :account, :scope
50
+ delegate :identifier, :scope_identifier, :scoped?, :positive_only, :to => :account
36
51
 
37
52
  def initialize(attributes)
38
53
  attributes.each { |name, value| send("#{name}=", value) }
39
54
  end
40
55
 
41
- def method_missing(method, *args)
42
- if block_given?
43
- account.send(method, *args, &Proc.new)
44
- else
45
- account.send(method, *args)
46
- end
47
- end
48
-
49
56
  def scope_identity
50
57
  scope_identifier.call(scope).to_s if scoped?
51
58
  end
@@ -4,10 +4,8 @@ module DoubleEntry
4
4
  class UnknownAccount < RuntimeError; end
5
5
  class TransferNotAllowed < RuntimeError; end
6
6
  class TransferIsNegative < RuntimeError; end
7
- class RequiredMetaMissing < RuntimeError; end
8
7
  class DuplicateAccount < RuntimeError; end
9
8
  class DuplicateTransfer < RuntimeError; end
10
- class UserAccountNotLocked < RuntimeError; end
11
9
  class AccountWouldBeSentNegative < RuntimeError; end
12
10
 
13
11
  end
@@ -150,7 +150,8 @@ module DoubleEntry
150
150
  # which they always should.
151
151
  #
152
152
  def reconciled?(account)
153
- scoped_lines = Line.where(:account => "#{account.identifier}", :scope => "#{account.scope}")
153
+ scoped_lines = Line.where(:account => "#{account.identifier}")
154
+ scoped_lines = scoped_lines.where(:scope => "#{account.scope_identity}") if account.scoped?
154
155
  sum_of_amounts = scoped_lines.sum(:amount)
155
156
  final_balance = scoped_lines.order(:id).last[:balance]
156
157
  cached_balance = AccountBalance.find_by_account(account)[:balance]
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module DoubleEntry
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
data/script/jack_hammer CHANGED
@@ -18,8 +18,14 @@ require 'database_cleaner'
18
18
 
19
19
  support = File.expand_path("../../spec/support/", __FILE__)
20
20
 
21
- ENV['DB'] ||= 'mysql'
22
- ActiveRecord::Base.establish_connection YAML.load_file(File.join(support, "database.yml"))[ENV['DB']]
21
+ db_engine = ENV['DB'] || 'mysql'
22
+
23
+ if db_engine == 'sqlite'
24
+ puts "Skipping jackhammer for SQLITE..."
25
+ exit 0
26
+ end
27
+
28
+ ActiveRecord::Base.establish_connection YAML.load_file(File.join(support, "database.yml"))[db_engine]
23
29
  require "#{support}/schema"
24
30
 
25
31
  lib = File.expand_path('../../lib', __FILE__)
@@ -4,6 +4,7 @@ require 'spec_helper'
4
4
  describe ActiveRecord::LockingExtensions do
5
5
  PG_DEADLOCK = ActiveRecord::StatementInvalid.new("PG::Error: ERROR: deadlock detected")
6
6
  MYSQL_DEADLOCK = ActiveRecord::StatementInvalid.new("Mysql::Error: Deadlock found when trying to get lock")
7
+ SQLITE3_LOCK = ActiveRecord::StatementInvalid.new("SQLite3::BusyException: database is locked: UPDATE...")
7
8
 
8
9
  context "#restartable_transaction" do
9
10
  it "keeps running the lock until a ActiveRecord::RestartTransaction isn't raised" do
@@ -49,6 +50,13 @@ describe ActiveRecord::LockingExtensions do
49
50
 
50
51
  expect { User.create_ignoring_duplicates! }.to_not raise_error
51
52
  end
53
+
54
+ it "in sqlite3" do
55
+ expect(User).to receive(:create!).ordered.and_raise(SQLITE3_LOCK)
56
+ expect(User).to receive(:create!).ordered.and_return(true)
57
+
58
+ expect { User.create_ignoring_duplicates! }.to_not raise_error
59
+ end
52
60
  end
53
61
  end
54
62
  end
@@ -1,32 +1,79 @@
1
1
  # encoding: utf-8
2
2
  require 'spec_helper'
3
- describe DoubleEntry::Account do
4
- let(:empty_scope) { lambda {|value| value } }
5
-
6
- it "instances should be sortable" do
7
- account = DoubleEntry::Account.new(:identifier => "savings", :scope_identifier => empty_scope)
8
- a = DoubleEntry::Account::Instance.new(:account => account, :scope => "123")
9
- b = DoubleEntry::Account::Instance.new(:account => account, :scope => "456")
10
- expect([b, a].sort).to eq [a, b]
3
+ module DoubleEntry
4
+ describe Account do
5
+ let(:identity_scope) { ->(value) { value } }
6
+
7
+ it "instances should be sortable" do
8
+ account = Account.new(:identifier => "savings", :scope_identifier => identity_scope)
9
+ a = Account::Instance.new(:account => account, :scope => "123")
10
+ b = Account::Instance.new(:account => account, :scope => "456")
11
+ expect([b, a].sort).to eq [a, b]
12
+ end
13
+
14
+ it "instances should be hashable" do
15
+ account = Account.new(:identifier => "savings", :scope_identifier => identity_scope)
16
+ a1 = Account::Instance.new(:account => account, :scope => "123")
17
+ a2 = Account::Instance.new(:account => account, :scope => "123")
18
+ b = Account::Instance.new(:account => account, :scope => "456")
19
+
20
+ expect(a1.hash).to eq a2.hash
21
+ expect(a1.hash).to_not eq b.hash
22
+ end
11
23
  end
12
24
 
13
- it "instances should be hashable" do
14
- account = DoubleEntry::Account.new(:identifier => "savings", :scope_identifier => empty_scope)
15
- a1 = DoubleEntry::Account::Instance.new(:account => account, :scope => "123")
16
- a2 = DoubleEntry::Account::Instance.new(:account => account, :scope => "123")
17
- b = DoubleEntry::Account::Instance.new(:account => account, :scope => "456")
25
+ describe Account::Set do
26
+ describe "#define" do
27
+ context "given a 'savings' account is defined" do
28
+ before { subject.define(:identifier => "savings") }
29
+ its(:first) { should be_a Account }
30
+ its("first.identifier") { should eq "savings" }
31
+ end
32
+ end
33
+
34
+ describe "#active_record_scope_identifier" do
35
+ subject(:scope) { Account::Set.new.active_record_scope_identifier(ar_class) }
18
36
 
19
- expect(a1.hash).to eq a2.hash
20
- expect(a1.hash).to_not eq b.hash
37
+ context "given ActiveRecordScopeFactory is stubbed" do
38
+ let(:scope_identifier) { double(:scope_identifier) }
39
+ let(:scope_factory) { double(:scope_factory, :scope_identifier => scope_identifier) }
40
+ let(:ar_class) { double(:ar_class) }
41
+ before { allow(Account::ActiveRecordScopeFactory).to receive(:new).with(ar_class).and_return(scope_factory) }
42
+
43
+ it { should eq scope_identifier }
44
+ end
45
+ end
21
46
  end
22
- end
23
47
 
24
- describe DoubleEntry::Account::Set do
25
- describe "#define" do
26
- context "given a 'savings' account is defined" do
27
- before { subject.define(:identifier => "savings") }
28
- its(:first) { should be_a DoubleEntry::Account }
29
- its("first.identifier") { should eq "savings" }
48
+ describe Account::ActiveRecordScopeFactory do
49
+ context "given the class User" do
50
+ subject(:factory) { Account::ActiveRecordScopeFactory.new(User) }
51
+
52
+ describe "#scope_identifier" do
53
+ subject(:scope_identifier) { factory.scope_identifier }
54
+
55
+ describe "#call" do
56
+ subject(:scope) { scope_identifier.call(value) }
57
+
58
+ context "given a User instance with ID 32" do
59
+ let(:value) { User.make(:id => 32) }
60
+
61
+ it { should eq 32 }
62
+ end
63
+
64
+ context "given the String 'I am a bearded lady'" do
65
+ let(:value) { "I am a bearded lady" }
66
+
67
+ it { should eq "I am a bearded lady" }
68
+ end
69
+
70
+ context "given the Integer 42" do
71
+ let(:value) { 42 }
72
+
73
+ it { should eq 42 }
74
+ end
75
+ end
76
+ end
30
77
  end
31
78
  end
32
79
  end
@@ -38,7 +38,7 @@ describe DoubleEntry::Locking do
38
38
  @account_d = DoubleEntry.account(:account_d, :scope => "4")
39
39
  end
40
40
 
41
- it "should create missing account balance records" do
41
+ it "creates missing account balance records" do
42
42
  expect do
43
43
  DoubleEntry::Locking.lock_accounts(@account_a) { }
44
44
  end.to change(DoubleEntry::AccountBalance, :count).by(1)
@@ -48,7 +48,7 @@ describe DoubleEntry::Locking do
48
48
  expect(account_balance.balance).to eq Money.new(0)
49
49
  end
50
50
 
51
- it "should take the balance for new account balance records from the lines table" do
51
+ it "takes the balance for new account balance records from the lines table" do
52
52
  DoubleEntry::Line.create!(:account => @account_a, :amount => Money.new(3_00), :balance => Money.new( 3_00), :code => :test)
53
53
  DoubleEntry::Line.create!(:account => @account_a, :amount => Money.new(7_00), :balance => Money.new(10_00), :code => :test)
54
54
 
@@ -61,7 +61,7 @@ describe DoubleEntry::Locking do
61
61
  expect(account_balance.balance).to eq Money.new(10_00)
62
62
  end
63
63
 
64
- it "should not allow locking inside a regular transaction" do
64
+ it "prohibits locking inside a regular transaction" do
65
65
  expect {
66
66
  DoubleEntry::AccountBalance.transaction do
67
67
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
@@ -70,7 +70,7 @@ describe DoubleEntry::Locking do
70
70
  }.to raise_error(DoubleEntry::Locking::LockMustBeOutermostTransaction)
71
71
  end
72
72
 
73
- it "should not allow a transfer inside a regular transaction" do
73
+ it "prohibits a transfer inside a regular transaction" do
74
74
  expect {
75
75
  DoubleEntry::AccountBalance.transaction do
76
76
  DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
@@ -78,7 +78,7 @@ describe DoubleEntry::Locking do
78
78
  }.to raise_error(DoubleEntry::Locking::LockMustBeOutermostTransaction)
79
79
  end
80
80
 
81
- it "should allow a transfer inside a lock if we've locked the transaction accounts" do
81
+ it "allows a transfer inside a lock if we've locked the transaction accounts" do
82
82
  expect {
83
83
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
84
84
  DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
@@ -86,7 +86,7 @@ describe DoubleEntry::Locking do
86
86
  }.to_not raise_error
87
87
  end
88
88
 
89
- it "should not allow a transfer inside a lock if the right locks aren't held" do
89
+ it "does not allow a transfer inside a lock if the right locks aren't held" do
90
90
  expect {
91
91
  DoubleEntry::Locking.lock_accounts(@account_a, @account_c) do
92
92
  DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
@@ -94,7 +94,7 @@ describe DoubleEntry::Locking do
94
94
  }.to raise_error(DoubleEntry::Locking::LockNotHeld, "No lock held for account: account_b, scope 2")
95
95
  end
96
96
 
97
- it "should allow nested locks if the outer lock locks all the accounts" do
97
+ it "allows nested locks if the outer lock locks all the accounts" do
98
98
  expect do
99
99
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
100
100
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { }
@@ -102,7 +102,7 @@ describe DoubleEntry::Locking do
102
102
  end.to_not raise_error
103
103
  end
104
104
 
105
- it "should not allow nested locks if the out lock doesn't lock all the accounts" do
105
+ it "prohibits nested locks if the out lock doesn't lock all the accounts" do
106
106
  expect do
107
107
  DoubleEntry::Locking.lock_accounts(@account_a) do
108
108
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { }
@@ -110,7 +110,7 @@ describe DoubleEntry::Locking do
110
110
  end.to raise_error(DoubleEntry::Locking::LockNotHeld, "No lock held for account: account_b, scope 2")
111
111
  end
112
112
 
113
- it "should roll back a locking transaction" do
113
+ it "rolls back a locking transaction" do
114
114
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
115
115
  DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
116
116
  raise ActiveRecord::Rollback
@@ -119,7 +119,7 @@ describe DoubleEntry::Locking do
119
119
  expect(DoubleEntry.balance(@account_b)).to eq Money.new(0)
120
120
  end
121
121
 
122
- it "should roll back a locking transaction if there's an exception" do
122
+ it "rolls back a locking transaction if there's an exception" do
123
123
  expect do
124
124
  DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
125
125
  DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
@@ -130,36 +130,39 @@ describe DoubleEntry::Locking do
130
130
  expect(DoubleEntry.balance(@account_b)).to eq Money.new(0)
131
131
  end
132
132
 
133
- it "should allow multiple threads to lock at the same time" do
134
- threads = Array.new
133
+ # sqlite cannot handle these cases so they don't run when DB=sqlite
134
+ describe "concurrent locking", :unless => ENV['DB'] == 'sqlite' do
135
135
 
136
- expect do
137
- threads << Thread.new do
138
- sleep 0.05
139
- DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
140
- DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
141
- end
142
- end
136
+ it "allows multiple threads to lock at the same time" do
137
+ expect do
138
+ threads = Array.new
143
139
 
144
- threads << Thread.new do
145
- DoubleEntry::Locking.lock_accounts(@account_c, @account_d) do
146
- sleep 0.1
147
- DoubleEntry.transfer(Money.new(10_00), :from => @account_c, :to => @account_d, :code => :test)
140
+ threads << Thread.new do
141
+ sleep 0.05
142
+ DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
143
+ DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
144
+ end
148
145
  end
149
- end
150
146
 
151
- threads.each(&:join)
152
- end.to_not raise_error
153
- end
147
+ threads << Thread.new do
148
+ DoubleEntry::Locking.lock_accounts(@account_c, @account_d) do
149
+ sleep 0.1
150
+ DoubleEntry.transfer(Money.new(10_00), :from => @account_c, :to => @account_d, :code => :test)
151
+ end
152
+ end
154
153
 
155
- it "should allow multiple threads to lock accounts without balances at the same time" do
156
- threads = Array.new
154
+ threads.each(&:join)
155
+ end.to_not raise_error
156
+ end
157
157
 
158
- expect do
159
- threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { } }
160
- threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_c, @account_d) { } }
158
+ it "allows multiple threads to lock accounts without balances at the same time" do
159
+ threads = Array.new
160
+ expect do
161
+ threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { sleep 0.1 } }
162
+ threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_c, @account_d) { sleep 0.1 } }
161
163
 
162
- threads.each(&:join)
163
- end.to_not raise_error
164
+ threads.each(&:join)
165
+ end.to_not raise_error
166
+ end
164
167
  end
165
168
  end
data/spec/spec_helper.rb CHANGED
@@ -3,8 +3,9 @@ require 'bundler/setup'
3
3
  require 'active_record'
4
4
  require 'active_support'
5
5
 
6
- ENV['DB'] ||= 'mysql'
7
- ActiveRecord::Base.establish_connection YAML.load_file(File.expand_path("../support/database.yml", __FILE__))[ENV['DB']]
6
+ db_engine = ENV['DB'] || 'mysql'
7
+
8
+ ActiveRecord::Base.establish_connection YAML.load_file(File.expand_path("../support/database.yml", __FILE__))[db_engine]
8
9
 
9
10
  FileUtils.mkdir_p 'log'
10
11
  FileUtils.rm 'log/test.log', :force => true
@@ -1,16 +1,10 @@
1
1
  # encoding: utf-8
2
- user_scope = lambda do |user_identifier|
3
- if user_identifier.is_a?(User)
4
- user_identifier.id
5
- else
6
- user_identifier
7
- end
8
- end
9
-
2
+ require_relative 'blueprints'
10
3
  DoubleEntry.configure do |config|
11
4
 
12
5
  # A set of accounts to test with
13
6
  config.define_accounts do |accounts|
7
+ user_scope = accounts.active_record_scope_identifier(User)
14
8
  accounts.define(:identifier => :savings, :scope_identifier => user_scope, :positive_only => true)
15
9
  accounts.define(:identifier => :checking, :scope_identifier => user_scope, :positive_only => true)
16
10
  accounts.define(:identifier => :test, :scope_identifier => user_scope)
@@ -14,3 +14,8 @@ mysql:
14
14
  pool: 100
15
15
  username: root
16
16
  password:
17
+ sqlite:
18
+ adapter: sqlite3
19
+ encoding: utf8
20
+ database: db/double_entry_test.sqlite3
21
+ pool: 100
@@ -16,3 +16,9 @@ postgres:
16
16
  pool: 100
17
17
  timeout: 5000
18
18
  host: localhost
19
+
20
+ sqlite:
21
+ adapter: sqlite3
22
+ encoding: utf8
23
+ database: db/double_entry_test.sqlite3
24
+ pool: 100
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: double_entry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -16,7 +16,7 @@ authors:
16
16
  autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
- date: 2014-07-17 00:00:00.000000000 Z
19
+ date: 2014-08-01 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: money
@@ -146,6 +146,22 @@ dependencies:
146
146
  - - ! '>='
147
147
  - !ruby/object:Gem::Version
148
148
  version: '0'
149
+ - !ruby/object:Gem::Dependency
150
+ name: sqlite3
151
+ requirement: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ! '>='
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ type: :development
158
+ prerelease: false
159
+ version_requirements: !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ! '>='
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
149
165
  - !ruby/object:Gem::Dependency
150
166
  name: rspec
151
167
  requirement: !ruby/object:Gem::Requirement
@@ -264,6 +280,7 @@ files:
264
280
  - LICENSE.md
265
281
  - README.md
266
282
  - Rakefile
283
+ - db/.gitkeep
267
284
  - double_entry.gemspec
268
285
  - gemfiles/Gemfile.rails-3.2.0
269
286
  - gemfiles/Gemfile.rails-4.0.0