double_entry 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/.rubocop.yml +55 -0
  4. data/.travis.yml +23 -12
  5. data/README.md +5 -1
  6. data/Rakefile +8 -3
  7. data/double_entry.gemspec +4 -3
  8. data/lib/active_record/locking_extensions.rb +28 -40
  9. data/lib/active_record/locking_extensions/log_subscriber.rb +4 -4
  10. data/lib/double_entry.rb +0 -2
  11. data/lib/double_entry/account.rb +13 -16
  12. data/lib/double_entry/account_balance.rb +0 -4
  13. data/lib/double_entry/balance_calculator.rb +4 -5
  14. data/lib/double_entry/configurable.rb +0 -2
  15. data/lib/double_entry/configuration.rb +2 -3
  16. data/lib/double_entry/errors.rb +2 -2
  17. data/lib/double_entry/line.rb +13 -16
  18. data/lib/double_entry/locking.rb +13 -18
  19. data/lib/double_entry/reporting.rb +2 -3
  20. data/lib/double_entry/reporting/aggregate.rb +90 -88
  21. data/lib/double_entry/reporting/aggregate_array.rb +58 -58
  22. data/lib/double_entry/reporting/day_range.rb +37 -35
  23. data/lib/double_entry/reporting/hour_range.rb +40 -37
  24. data/lib/double_entry/reporting/line_aggregate.rb +27 -28
  25. data/lib/double_entry/reporting/month_range.rb +67 -67
  26. data/lib/double_entry/reporting/time_range.rb +40 -38
  27. data/lib/double_entry/reporting/time_range_array.rb +3 -5
  28. data/lib/double_entry/reporting/week_range.rb +77 -78
  29. data/lib/double_entry/reporting/year_range.rb +27 -27
  30. data/lib/double_entry/transfer.rb +14 -15
  31. data/lib/double_entry/validation/line_check.rb +92 -86
  32. data/lib/double_entry/version.rb +1 -1
  33. data/lib/generators/double_entry/install/install_generator.rb +1 -2
  34. data/lib/generators/double_entry/install/templates/migration.rb +0 -2
  35. data/script/jack_hammer +1 -1
  36. data/spec/active_record/locking_extensions_spec.rb +45 -38
  37. data/spec/double_entry/account_balance_spec.rb +4 -5
  38. data/spec/double_entry/account_spec.rb +43 -44
  39. data/spec/double_entry/balance_calculator_spec.rb +6 -8
  40. data/spec/double_entry/configuration_spec.rb +14 -16
  41. data/spec/double_entry/line_spec.rb +25 -26
  42. data/spec/double_entry/locking_spec.rb +34 -39
  43. data/spec/double_entry/reporting/aggregate_array_spec.rb +8 -10
  44. data/spec/double_entry/reporting/aggregate_spec.rb +84 -44
  45. data/spec/double_entry/reporting/line_aggregate_spec.rb +7 -6
  46. data/spec/double_entry/reporting/month_range_spec.rb +109 -103
  47. data/spec/double_entry/reporting/time_range_array_spec.rb +145 -135
  48. data/spec/double_entry/reporting/time_range_spec.rb +36 -35
  49. data/spec/double_entry/reporting/week_range_spec.rb +82 -76
  50. data/spec/double_entry/reporting_spec.rb +9 -13
  51. data/spec/double_entry/transfer_spec.rb +13 -15
  52. data/spec/double_entry/validation/line_check_spec.rb +73 -79
  53. data/spec/double_entry_spec.rb +65 -68
  54. data/spec/generators/double_entry/install/install_generator_spec.rb +7 -10
  55. data/spec/spec_helper.rb +68 -10
  56. data/spec/support/accounts.rb +2 -4
  57. data/spec/support/double_entry_spec_helper.rb +4 -4
  58. data/spec/support/gemfiles/Gemfile.rails-3.2.x +1 -0
  59. metadata +31 -2
@@ -2,119 +2,125 @@
2
2
  require 'set'
3
3
 
4
4
  module DoubleEntry
5
- module Validation
6
- class LineCheck < ActiveRecord::Base
5
+ module Validation
6
+ class LineCheck < ActiveRecord::Base
7
+ default_scope -> { order('created_at') }
7
8
 
8
- default_scope -> { order('created_at') }
9
+ def self.perform!
10
+ new.perform
11
+ end
9
12
 
10
- def self.perform!
11
- new.perform
12
- end
13
+ def perform
14
+ log = ''
15
+ current_line_id = nil
13
16
 
14
- def perform
15
- log = ''
16
- current_line_id = nil
17
+ active_accounts = Set.new
18
+ incorrect_accounts = Set.new
17
19
 
18
- active_accounts = Set.new
19
- incorrect_accounts = Set.new
20
+ new_lines_since_last_run.find_each do |line|
21
+ incorrect_accounts << line.account unless running_balance_correct?(line, log)
22
+ active_accounts << line.account
23
+ current_line_id = line.id
24
+ end
20
25
 
21
- new_lines_since_last_run.find_each do |line|
22
- incorrect_accounts << line.account unless running_balance_correct?(line, log)
23
- active_accounts << line.account
24
- current_line_id = line.id
25
- end
26
+ active_accounts.each do |account|
27
+ incorrect_accounts << account unless cached_balance_correct?(account)
28
+ end
29
+
30
+ incorrect_accounts.each { |account| recalculate_account(account) }
26
31
 
27
- active_accounts.each do |account|
28
- incorrect_accounts << account unless cached_balance_correct?(account)
32
+ unless active_accounts.empty?
33
+ LineCheck.create!(
34
+ :errors_found => incorrect_accounts.any?,
35
+ :last_line_id => current_line_id,
36
+ :log => log,
37
+ )
38
+ end
29
39
  end
30
40
 
31
- incorrect_accounts.each { |account| recalculate_account(account) }
41
+ private
32
42
 
33
- unless active_accounts.empty?
34
- LineCheck.create!(
35
- :errors_found => incorrect_accounts.any?,
36
- :last_line_id => current_line_id,
37
- :log => log,
38
- )
43
+ def last_run_line_id
44
+ latest = LineCheck.last
45
+ latest ? latest.last_line_id : 0
39
46
  end
40
- end
41
-
42
- private
43
47
 
44
- def last_run_line_id
45
- latest = LineCheck.last
46
- latest ? latest.last_line_id : 0
47
- end
48
+ def new_lines_since_last_run
49
+ Line.where('id > ?', last_run_line_id)
50
+ end
48
51
 
49
- def new_lines_since_last_run
50
- Line.where('id > ?', last_run_line_id)
51
- end
52
+ def running_balance_correct?(line, log)
53
+ # Another work around for the MySQL 5.1 query optimiser bug that causes the ORDER BY
54
+ # on the query to fail in some circumstances, resulting in an old balance being
55
+ # returned. This was biting us intermittently in spec runs.
56
+ # See http://bugs.mysql.com/bug.php?id=51431
57
+ force_index = if Line.connection.adapter_name.match(/mysql/i)
58
+ 'FORCE INDEX (lines_scope_account_id_idx)'
59
+ else
60
+ ''
61
+ end
62
+
63
+ # yes, it needs to be find_by_sql, because any other find will be affected
64
+ # by the find_each call in perform!
65
+ previous_line = Line.find_by_sql([<<-SQL, line.account.identifier.to_s, line.scope, line.id])
66
+ SELECT * FROM #{Line.quoted_table_name} #{force_index}
67
+ WHERE account = ?
68
+ AND scope = ?
69
+ AND id < ?
70
+ ORDER BY id DESC
71
+ LIMIT 1
72
+ SQL
73
+
74
+ previous_balance = previous_line.length == 1 ? previous_line[0].balance : Money.zero(line.account.currency)
75
+
76
+ if line.balance != (line.amount + previous_balance)
77
+ log << line_error_message(line, previous_line, previous_balance)
78
+ end
52
79
 
53
- def running_balance_correct?(line, log)
54
- # Another work around for the MySQL 5.1 query optimiser bug that causes the ORDER BY
55
- # on the query to fail in some circumstances, resulting in an old balance being
56
- # returned. This was biting us intermittently in spec runs.
57
- # See http://bugs.mysql.com/bug.php?id=51431
58
- force_index = if Line.connection.adapter_name.match /mysql/i
59
- "FORCE INDEX (lines_scope_account_id_idx)"
60
- else
61
- ""
62
- end
63
-
64
- # yes, it needs to be find_by_sql, because any other find will be affected
65
- # by the find_each call in perform!
66
- previous_line = Line.find_by_sql(["SELECT * FROM #{Line.quoted_table_name} #{force_index} WHERE account = ? AND scope = ? AND id < ? ORDER BY id DESC LIMIT 1", line.account.identifier.to_s, line.scope, line.id])
67
-
68
- previous_balance = previous_line.length == 1 ? previous_line[0].balance : Money.zero(line.account.currency)
69
-
70
- if line.balance != (line.amount + previous_balance)
71
- log << line_error_message(line, previous_line, previous_balance)
80
+ line.balance == previous_balance + line.amount
72
81
  end
73
82
 
74
- line.balance == previous_balance + line.amount
75
- end
76
-
77
- def line_error_message(line, previous_line, previous_balance)
78
- <<-END_OF_MESSAGE.strip_heredoc
83
+ def line_error_message(line, previous_line, previous_balance)
84
+ <<-END_OF_MESSAGE.strip_heredoc
79
85
  *********************************
80
86
  Error on line ##{line.id}: balance:#{line.balance} != #{previous_balance} + #{line.amount}
81
87
  *********************************
82
- #{previous_line.inspect}
83
- #{line.inspect}
88
+ #{previous_line.inspect}
89
+ #{line.inspect}
84
90
 
85
- END_OF_MESSAGE
86
- end
91
+ END_OF_MESSAGE
92
+ end
87
93
 
88
- def cached_balance_correct?(account)
89
- DoubleEntry.lock_accounts(account) do
90
- return AccountBalance.find_by_account(account).balance == account.balance
94
+ def cached_balance_correct?(account)
95
+ DoubleEntry.lock_accounts(account) do
96
+ return AccountBalance.find_by_account(account).balance == account.balance
97
+ end
91
98
  end
92
- end
93
99
 
94
- def recalculate_account(account)
95
- DoubleEntry.lock_accounts(account) do
96
- recalculated_balance = Money.zero(account.currency)
100
+ def recalculate_account(account)
101
+ DoubleEntry.lock_accounts(account) do
102
+ recalculated_balance = Money.zero(account.currency)
97
103
 
98
- lines_for_account(account).each do |line|
99
- recalculated_balance += line.amount
100
- line.update_attribute(:balance, recalculated_balance) if line.balance != recalculated_balance
101
- end
104
+ lines_for_account(account).each do |line|
105
+ recalculated_balance += line.amount
106
+ line.update_attribute(:balance, recalculated_balance) if line.balance != recalculated_balance
107
+ end
102
108
 
103
- update_balance_for_account(account, recalculated_balance)
109
+ update_balance_for_account(account, recalculated_balance)
110
+ end
104
111
  end
105
- end
106
112
 
107
- def lines_for_account(account)
108
- Line.where(
109
- :account => account.identifier.to_s,
110
- :scope => account.scope_identity.to_s
111
- ).order(:id)
112
- end
113
+ def lines_for_account(account)
114
+ Line.where(
115
+ :account => account.identifier.to_s,
116
+ :scope => account.scope_identity.to_s,
117
+ ).order(:id)
118
+ end
113
119
 
114
- def update_balance_for_account(account, balance)
115
- account_balance = Locking.balance_for_locked_account(account)
116
- account_balance.update_attribute(:balance, balance)
120
+ def update_balance_for_account(account, balance)
121
+ account_balance = Locking.balance_for_locked_account(account)
122
+ account_balance.update_attribute(:balance, balance)
123
+ end
117
124
  end
118
125
  end
119
- end
120
126
  end
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module DoubleEntry
4
- VERSION = "0.10.0"
4
+ VERSION = '0.10.1'
5
5
  end
@@ -14,9 +14,8 @@ module DoubleEntry
14
14
  end
15
15
 
16
16
  def copy_migrations
17
- migration_template "migration.rb", "db/migrate/create_double_entry_tables.rb"
17
+ migration_template 'migration.rb', 'db/migrate/create_double_entry_tables.rb'
18
18
  end
19
-
20
19
  end
21
20
  end
22
21
  end
@@ -54,7 +54,6 @@ class CreateDoubleEntryTables < ActiveRecord::Migration
54
54
  t.text "log"
55
55
  t.timestamps :null => false
56
56
  end
57
-
58
57
  end
59
58
 
60
59
  def self.down
@@ -63,5 +62,4 @@ class CreateDoubleEntryTables < ActiveRecord::Migration
63
62
  drop_table "double_entry_lines"
64
63
  drop_table "double_entry_account_balances"
65
64
  end
66
-
67
65
  end
data/script/jack_hammer CHANGED
@@ -68,7 +68,7 @@ end
68
68
  def clean_out_database
69
69
  puts "Cleaning out the database..."
70
70
 
71
- DatabaseCleaner.clean_with(:deletion)
71
+ DatabaseCleaner.clean_with(:truncation)
72
72
  end
73
73
 
74
74
  def create_accounts_and_transfers
@@ -1,12 +1,11 @@
1
1
  # encoding: utf-8
2
- require 'spec_helper'
3
2
 
4
- describe ActiveRecord::LockingExtensions do
5
- PG_DEADLOCK = ActiveRecord::StatementInvalid.new("PG::Error: ERROR: deadlock detected")
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...")
3
+ RSpec.describe ActiveRecord::LockingExtensions do
4
+ PG_DEADLOCK = ActiveRecord::StatementInvalid.new('PG::Error: ERROR: deadlock detected')
5
+ MYSQL_DEADLOCK = ActiveRecord::StatementInvalid.new('Mysql::Error: Deadlock found when trying to get lock')
6
+ SQLITE3_LOCK = ActiveRecord::StatementInvalid.new('SQLite3::BusyException: database is locked: UPDATE...')
8
7
 
9
- context "#restartable_transaction" do
8
+ context '#restartable_transaction' do
10
9
  it "keeps running the lock until a ActiveRecord::RestartTransaction isn't raised" do
11
10
  expect(User).to receive(:create!).ordered.and_raise(ActiveRecord::RestartTransaction)
12
11
  expect(User).to receive(:create!).ordered.and_raise(ActiveRecord::RestartTransaction)
@@ -16,88 +15,96 @@ describe ActiveRecord::LockingExtensions do
16
15
  end
17
16
  end
18
17
 
19
- context "#with_restart_on_deadlock" do
20
- shared_examples "abstract adapter" do
21
- it "raises a ActiveRecord::RestartTransaction error if a deadlock occurs" do
22
- expect { User.with_restart_on_deadlock { raise exception } }.to raise_error(ActiveRecord::RestartTransaction)
18
+ context '#with_restart_on_deadlock' do
19
+ shared_examples 'abstract adapter' do
20
+ it 'raises a ActiveRecord::RestartTransaction error if a deadlock occurs' do
21
+ expect { User.with_restart_on_deadlock { fail exception } }.
22
+ to raise_error(ActiveRecord::RestartTransaction)
23
23
  end
24
24
 
25
- it "publishes a notification" do
26
- expect(ActiveSupport::Notifications).to receive(:publish).with("deadlock_restart.active_record", hash_including(:exception => exception))
27
- expect { User.with_restart_on_deadlock { raise exception } }.to raise_error
25
+ it 'publishes a notification' do
26
+ expect(ActiveSupport::Notifications).
27
+ to receive(:publish).
28
+ with('deadlock_restart.active_record', hash_including(:exception => exception))
29
+ expect { User.with_restart_on_deadlock { fail exception } }.to raise_error
28
30
  end
29
31
  end
30
32
 
31
- context "mysql" do
33
+ context 'mysql' do
32
34
  let(:exception) { MYSQL_DEADLOCK }
33
35
 
34
- it_behaves_like "abstract adapter"
36
+ it_behaves_like 'abstract adapter'
35
37
  end
36
38
 
37
- context "postgres" do
39
+ context 'postgres' do
38
40
  let(:exception) { PG_DEADLOCK }
39
41
 
40
- it_behaves_like "abstract adapter"
42
+ it_behaves_like 'abstract adapter'
41
43
  end
42
44
 
43
- context "sqlite" do
45
+ context 'sqlite' do
44
46
  let(:exception) { SQLITE3_LOCK }
45
47
 
46
- it_behaves_like "abstract adapter"
48
+ it_behaves_like 'abstract adapter'
47
49
  end
48
50
  end
49
51
 
50
- context "#create_ignoring_duplicates" do
51
- it "does not raise an error if a duplicate index error is raised in the database" do
52
- User.make! :username => "keith"
52
+ context '#create_ignoring_duplicates' do
53
+ it 'does not raise an error if a duplicate index error is raised in the database' do
54
+ User.make! :username => 'keith'
53
55
 
54
- expect { User.make! :username => "keith" }.to raise_error
55
- expect { User.create_ignoring_duplicates! :username => "keith" }.to_not raise_error
56
+ expect { User.make! :username => 'keith' }.to raise_error
57
+ expect { User.create_ignoring_duplicates! :username => 'keith' }.to_not raise_error
56
58
  end
57
59
 
58
- it "publishes a notification when a duplicate is encountered" do
59
- User.make! :username => "keith"
60
+ it 'publishes a notification when a duplicate is encountered' do
61
+ User.make! :username => 'keith'
60
62
 
61
- expect(ActiveSupport::Notifications).to receive(:publish).with("duplicate_ignore.active_record", hash_including(:exception => kind_of(ActiveRecord::RecordNotUnique)))
63
+ expect(ActiveSupport::Notifications).
64
+ to receive(:publish).
65
+ with('duplicate_ignore.active_record', hash_including(:exception => kind_of(ActiveRecord::RecordNotUnique)))
62
66
 
63
- expect { User.create_ignoring_duplicates! :username => "keith" }.to_not raise_error
67
+ expect { User.create_ignoring_duplicates! :username => 'keith' }.to_not raise_error
64
68
  end
65
69
 
66
- shared_examples "abstract adapter" do
67
- it "retries the creation if a deadlock error is raised from the database" do
70
+ shared_examples 'abstract adapter' do
71
+ it 'retries the creation if a deadlock error is raised from the database' do
68
72
  expect(User).to receive(:create!).ordered.and_raise(exception)
69
73
  expect(User).to receive(:create!).ordered.and_return(true)
70
74
 
71
75
  expect { User.create_ignoring_duplicates! }.to_not raise_error
72
76
  end
73
77
 
74
- it "publishes a notification on each retry" do
78
+ it 'publishes a notification on each retry' do
75
79
  expect(User).to receive(:create!).ordered.and_raise(exception)
76
80
  expect(User).to receive(:create!).ordered.and_raise(exception)
77
81
  expect(User).to receive(:create!).ordered.and_return(true)
78
82
 
79
- expect(ActiveSupport::Notifications).to receive(:publish).with("deadlock_retry.active_record", hash_including(:exception => exception)).twice
83
+ expect(ActiveSupport::Notifications).
84
+ to receive(:publish).
85
+ with('deadlock_retry.active_record', hash_including(:exception => exception)).
86
+ twice
80
87
 
81
88
  expect { User.create_ignoring_duplicates! }.to_not raise_error
82
89
  end
83
90
  end
84
91
 
85
- context "mysql" do
92
+ context 'mysql' do
86
93
  let(:exception) { MYSQL_DEADLOCK }
87
94
 
88
- it_behaves_like "abstract adapter"
95
+ it_behaves_like 'abstract adapter'
89
96
  end
90
97
 
91
- context "postgres" do
98
+ context 'postgres' do
92
99
  let(:exception) { PG_DEADLOCK }
93
100
 
94
- it_behaves_like "abstract adapter"
101
+ it_behaves_like 'abstract adapter'
95
102
  end
96
103
 
97
- context "sqlite" do
104
+ context 'sqlite' do
98
105
  let(:exception) { SQLITE3_LOCK }
99
106
 
100
- it_behaves_like "abstract adapter"
107
+ it_behaves_like 'abstract adapter'
101
108
  end
102
109
  end
103
110
  end
@@ -1,8 +1,7 @@
1
1
  # encoding: utf-8
2
- require 'spec_helper'
3
- describe DoubleEntry::AccountBalance do
4
-
5
- it "has a table name prefixed with double_entry_" do
6
- expect(DoubleEntry::AccountBalance.table_name).to eq "double_entry_account_balances"
2
+ RSpec.describe DoubleEntry::AccountBalance do
3
+ describe '.table_name' do
4
+ subject { DoubleEntry::AccountBalance.table_name }
5
+ it { should eq('double_entry_account_balances') }
7
6
  end
8
7
  end
@@ -1,19 +1,18 @@
1
1
  # encoding: utf-8
2
- require 'spec_helper'
3
2
  module DoubleEntry
4
- describe Account do
3
+ RSpec.describe Account do
5
4
  let(:identity_scope) { ->(value) { value } }
6
5
 
7
- describe "::new" do
8
- context "given an identifier 31 characters in length" do
9
- let(:identifier) { "xxxxxxxx 31 characters xxxxxxxx" }
6
+ describe '::new' do
7
+ context 'given an identifier 31 characters in length' do
8
+ let(:identifier) { 'xxxxxxxx 31 characters xxxxxxxx' }
10
9
  specify do
11
10
  expect { Account.new(:identifier => identifier) }.to_not raise_error
12
11
  end
13
12
  end
14
13
 
15
- context "given an identifier 32 characters in length" do
16
- let(:identifier) { "xxxxxxxx 32 characters xxxxxxxxx" }
14
+ context 'given an identifier 32 characters in length' do
15
+ let(:identifier) { 'xxxxxxxx 32 characters xxxxxxxxx' }
17
16
  specify do
18
17
  expect { Account.new(:identifier => identifier) }.to raise_error AccountIdentifierTooLongError, /'#{identifier}'/
19
18
  end
@@ -21,64 +20,64 @@ module DoubleEntry
21
20
  end
22
21
 
23
22
  describe Account::Instance do
24
- it "is sortable" do
25
- account = Account.new(:identifier => "savings", :scope_identifier => identity_scope)
26
- a = Account::Instance.new(:account => account, :scope => "123")
27
- b = Account::Instance.new(:account => account, :scope => "456")
23
+ it 'is sortable' do
24
+ account = Account.new(:identifier => 'savings', :scope_identifier => identity_scope)
25
+ a = Account::Instance.new(:account => account, :scope => '123')
26
+ b = Account::Instance.new(:account => account, :scope => '456')
28
27
  expect([b, a].sort).to eq [a, b]
29
28
  end
30
29
 
31
- it "is hashable" do
32
- account = Account.new(:identifier => "savings", :scope_identifier => identity_scope)
33
- a1 = Account::Instance.new(:account => account, :scope => "123")
34
- a2 = Account::Instance.new(:account => account, :scope => "123")
35
- b = Account::Instance.new(:account => account, :scope => "456")
30
+ it 'is hashable' do
31
+ account = Account.new(:identifier => 'savings', :scope_identifier => identity_scope)
32
+ a1 = Account::Instance.new(:account => account, :scope => '123')
33
+ a2 = Account::Instance.new(:account => account, :scope => '123')
34
+ b = Account::Instance.new(:account => account, :scope => '456')
36
35
 
37
36
  expect(a1.hash).to eq a2.hash
38
37
  expect(a1.hash).to_not eq b.hash
39
38
  end
40
39
 
41
- describe "::new" do
42
- let(:account) { Account.new(:identifier => "x", :scope_identifier => identity_scope) }
40
+ describe '::new' do
41
+ let(:account) { Account.new(:identifier => 'x', :scope_identifier => identity_scope) }
43
42
  subject(:initialize_account_instance) { Account::Instance.new(:account => account, :scope => scope) }
44
43
 
45
- context "given a scope identifier 23 characters in length" do
46
- let(:scope) { "xxxx 23 characters xxxx" }
44
+ context 'given a scope identifier 23 characters in length' do
45
+ let(:scope) { 'xxxx 23 characters xxxx' }
47
46
  specify { expect { initialize_account_instance }.to_not raise_error }
48
47
  end
49
48
 
50
- context "given a scope identifier 24 characters in length" do
51
- let(:scope) { "xxxx 24 characters xxxxx" }
49
+ context 'given a scope identifier 24 characters in length' do
50
+ let(:scope) { 'xxxx 24 characters xxxxx' }
52
51
  specify { expect { initialize_account_instance }.to raise_error ScopeIdentifierTooLongError, /'#{scope}'/ }
53
52
  end
54
53
  end
55
54
  end
56
55
 
57
- describe "currency" do
58
- it "defaults to USD currency" do
59
- account = DoubleEntry::Account.new(:identifier => "savings", :scope_identifier => identity_scope)
60
- expect(DoubleEntry::Account::Instance.new(:account => account).currency).to eq("USD")
56
+ describe 'currency' do
57
+ it 'defaults to USD currency' do
58
+ account = DoubleEntry::Account.new(:identifier => 'savings', :scope_identifier => identity_scope)
59
+ expect(DoubleEntry::Account::Instance.new(:account => account).currency).to eq('USD')
61
60
  end
62
61
 
63
- it "allows the currency to be set" do
64
- account = DoubleEntry::Account.new(:identifier => "savings", :scope_identifier => identity_scope, :currency => "AUD")
65
- expect(DoubleEntry::Account::Instance.new(:account => account).currency).to eq("AUD")
62
+ it 'allows the currency to be set' do
63
+ account = DoubleEntry::Account.new(:identifier => 'savings', :scope_identifier => identity_scope, :currency => 'AUD')
64
+ expect(DoubleEntry::Account::Instance.new(:account => account).currency).to eq('AUD')
66
65
  end
67
66
  end
68
67
 
69
68
  describe Account::Set do
70
- describe "#define" do
69
+ describe '#define' do
71
70
  context "given a 'savings' account is defined" do
72
- before { subject.define(:identifier => "savings") }
71
+ before { subject.define(:identifier => 'savings') }
73
72
  its(:first) { should be_an Account }
74
- its("first.identifier") { should eq "savings" }
73
+ its('first.identifier') { should eq 'savings' }
75
74
  end
76
75
  end
77
76
 
78
- describe "#active_record_scope_identifier" do
77
+ describe '#active_record_scope_identifier' do
79
78
  subject(:scope) { Account::Set.new.active_record_scope_identifier(ar_class) }
80
79
 
81
- context "given ActiveRecordScopeFactory is stubbed" do
80
+ context 'given ActiveRecordScopeFactory is stubbed' do
82
81
  let(:scope_identifier) { double(:scope_identifier) }
83
82
  let(:scope_factory) { double(:scope_factory, :scope_identifier => scope_identifier) }
84
83
  let(:ar_class) { double(:ar_class) }
@@ -90,36 +89,36 @@ module DoubleEntry
90
89
  end
91
90
  end
92
91
 
93
- describe Account::ActiveRecordScopeFactory do
94
- context "given the class User" do
92
+ RSpec.describe Account::ActiveRecordScopeFactory do
93
+ context 'given the class User' do
95
94
  subject(:factory) { Account::ActiveRecordScopeFactory.new(User) }
96
95
 
97
- describe "#scope_identifier" do
96
+ describe '#scope_identifier' do
98
97
  subject(:scope_identifier) { factory.scope_identifier }
99
98
 
100
- describe "#call" do
99
+ describe '#call' do
101
100
  subject(:scope) { scope_identifier.call(value) }
102
101
 
103
- context "given a User instance with ID 32" do
102
+ context 'given a User instance with ID 32' do
104
103
  let(:value) { User.make(:id => 32) }
105
104
 
106
105
  it { should eq 32 }
107
106
  end
108
107
 
109
- context "given differing model instance with ID 32" do
108
+ context 'given differing model instance with ID 32' do
110
109
  let(:value) { double(:id => 32) }
111
- it "raises an error" do
110
+ it 'raises an error' do
112
111
  expect { scope_identifier.call(value) }.to raise_error DoubleEntry::AccountScopeMismatchError
113
112
  end
114
113
  end
115
114
 
116
115
  context "given the String 'I am a bearded lady'" do
117
- let(:value) { "I am a bearded lady" }
116
+ let(:value) { 'I am a bearded lady' }
118
117
 
119
- it { should eq "I am a bearded lady" }
118
+ it { should eq 'I am a bearded lady' }
120
119
  end
121
120
 
122
- context "given the Integer 42" do
121
+ context 'given the Integer 42' do
123
122
  let(:value) { 42 }
124
123
 
125
124
  it { should eq 42 }