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 +1 -0
- data/.travis.yml +1 -0
- data/README.md +1 -8
- data/Rakefile +1 -1
- data/db/.gitkeep +0 -0
- data/double_entry.gemspec +1 -0
- data/lib/active_record/locking_extensions.rb +30 -8
- data/lib/double_entry/account.rb +15 -8
- data/lib/double_entry/errors.rb +0 -2
- data/lib/double_entry/reporting.rb +2 -1
- data/lib/double_entry/version.rb +1 -1
- data/script/jack_hammer +8 -2
- data/spec/active_record/locking_extensions_spec.rb +8 -0
- data/spec/double_entry/account_spec.rb +69 -22
- data/spec/double_entry/locking_spec.rb +37 -34
- data/spec/spec_helper.rb +3 -2
- data/spec/support/accounts.rb +2 -8
- data/spec/support/database.example.yml +5 -0
- data/spec/support/database.travis.yml +6 -0
- metadata +19 -2
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
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 =
|
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
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
|
-
|
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
|
-
|
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.
|
data/lib/double_entry/account.rb
CHANGED
@@ -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
|
data/lib/double_entry/errors.rb
CHANGED
@@ -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}"
|
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]
|
data/lib/double_entry/version.rb
CHANGED
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']
|
22
|
-
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
|
-
|
20
|
-
|
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
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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 "
|
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 "
|
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 "
|
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 "
|
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 "
|
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 "
|
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 "
|
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 "
|
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 "
|
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 "
|
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
|
-
|
134
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
156
|
-
|
154
|
+
threads.each(&:join)
|
155
|
+
end.to_not raise_error
|
156
|
+
end
|
157
157
|
|
158
|
-
|
159
|
-
threads
|
160
|
-
|
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
|
-
|
163
|
-
|
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']
|
7
|
-
|
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
|
data/spec/support/accounts.rb
CHANGED
@@ -1,16 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
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)
|
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
|
+
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-
|
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
|