double_entry 0.10.0 → 0.10.1

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.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5979a152f5320a2cb5fc2838a966925abe66fbd6
4
- data.tar.gz: b9486092cdaf973950bd38c27b64379064c34000
3
+ metadata.gz: a89a7b1a2bfa39a0c51b309f107fc625af9643f0
4
+ data.tar.gz: 1ac1862d044333c98bb26e78c6ba7d2c508203b4
5
5
  SHA512:
6
- metadata.gz: c3a79f8cffcb4925ed6146360477d5c97d8c49464ac7b550daf452bc3d4d10aac8c6221c3f2f2fa913181e72851fa16e3735c9adeb1137088ce6e2b50419a9a4
7
- data.tar.gz: 11e7067886e3b813235f1cdae8affbfb7215fea67cc36e4bbec42f6e1bd900baf960ca4dc8ca4dec3e63542c366b3e31587489f5c9e4c24b07f61d3463d0c2e7
6
+ metadata.gz: 235592b9bce67bfdbb4aa474e722a9dd54573ad495797fc1338521d6807e7d51648c7d3eb6258bbde8bdfa8b4b04a5fb6993dca63f21aadd123b9d76d4364dd3
7
+ data.tar.gz: 28f0afca58a850bca2efc4a28bfa967639f54e383b5b16cc9e5959cef65b5eb8f3d8a72efb0e519f745e9491252fe2ff5c7ca86fa90168e6e41da9a2e1152765
data/.rspec CHANGED
@@ -1 +1,2 @@
1
- --colour
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,55 @@
1
+ AllCops:
2
+ Exclude:
3
+ - .bundle/**/*
4
+ - bin/**/*
5
+ - tmp/**/*
6
+ - script/jack_hammer
7
+ - spec/support/schema.rb
8
+ - lib/generators/double_entry/install/templates/**/*
9
+
10
+ Style/AccessModifierIndentation:
11
+ EnforcedStyle: outdent
12
+
13
+ Style/DotPosition:
14
+ EnforcedStyle: trailing
15
+
16
+ Style/GuardClause:
17
+ Enabled: false
18
+
19
+ Style/HashSyntax:
20
+ EnforcedStyle: hash_rockets
21
+
22
+ Style/SpecialGlobalVars:
23
+ Enabled: false
24
+
25
+ Style/TrailingComma:
26
+ EnforcedStyleForMultiline: comma
27
+
28
+ # TODO Enable these checks and fix codes
29
+
30
+ Style/ClassVars:
31
+ Enabled: false
32
+
33
+ Style/Documentation:
34
+ Enabled: false
35
+
36
+ Style/DoubleNegation:
37
+ Enabled: false
38
+
39
+ Style/ModuleFunction:
40
+ Enabled: false
41
+
42
+ Metrics/AbcSize:
43
+ Max: 47 #15
44
+
45
+ Metrics/CyclomaticComplexity:
46
+ Max: 13 #6
47
+
48
+ Metrics/LineLength:
49
+ Max: 185
50
+
51
+ Metrics/MethodLength:
52
+ Max: 30 # 10
53
+
54
+ Metrics/PerceivedComplexity:
55
+ Max: 13 #7
data/.travis.yml CHANGED
@@ -1,12 +1,4 @@
1
1
  language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- - 2.1.5
6
- env:
7
- - DB=mysql
8
- - DB=postgres
9
- - DB=sqlite
10
2
  before_script:
11
3
  - cp spec/support/database.travis.yml spec/support/database.yml
12
4
  - mysql -e 'create database double_entry_test;'
@@ -14,7 +6,26 @@ before_script:
14
6
  script:
15
7
  - rake spec
16
8
  - ruby script/jack_hammer -t 2000
17
- gemfile:
18
- - spec/support/gemfiles/Gemfile.rails-3.2.x
19
- - spec/support/gemfiles/Gemfile.rails-4.1.x
20
- - spec/support/gemfiles/Gemfile.rails-4.2.x
9
+ matrix:
10
+ include:
11
+ - rvm: 1.9.3
12
+ gemfile: spec/support/gemfiles/Gemfile.rails-4.2.x
13
+ env: DB=mysql
14
+ - rvm: 2.0.0
15
+ gemfile: spec/support/gemfiles/Gemfile.rails-4.2.x
16
+ env: DB=mysql
17
+ - rvm: 2.2.0
18
+ gemfile: spec/support/gemfiles/Gemfile.rails-4.1.x
19
+ env: DB=mysql
20
+ - rvm: 2.2.0
21
+ gemfile: spec/support/gemfiles/Gemfile.rails-4.2.x
22
+ env: DB=mysql
23
+ - rvm: 2.2.0
24
+ gemfile: spec/support/gemfiles/Gemfile.rails-4.2.x
25
+ env: DB=sqlite
26
+ - rvm: 2.2.0
27
+ gemfile: spec/support/gemfiles/Gemfile.rails-4.2.x
28
+ env: DB=postgres
29
+ - rvm: 2.1.5
30
+ gemfile: spec/support/gemfiles/Gemfile.rails-3.2.x
31
+ env: DB=mysql
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # DoubleEntry
2
2
 
3
+
4
+ [![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/double_entry/blob/master/LICENSE.md)
3
5
  [![Gem Version](https://badge.fury.io/rb/double_entry.svg)](http://badge.fury.io/rb/double_entry)
4
- [![Build Status](https://travis-ci.org/envato/double_entry.svg)](https://travis-ci.org/envato/double_entry)
6
+ [![Build Status](https://travis-ci.org/envato/double_entry.svg?branch=master)](https://travis-ci.org/envato/double_entry)
5
7
  [![Code Climate](https://codeclimate.com/github/envato/double_entry.png)](https://codeclimate.com/github/envato/double_entry)
6
8
 
7
9
  ![Show me the Money](http://24.media.tumblr.com/tumblr_m3bwbqNJIG1rrgbmqo1_500.gif)
@@ -21,7 +23,9 @@ DoubleEntry is tested against:
21
23
 
22
24
  Ruby
23
25
  * 1.9.3
26
+ * 2.0.0
24
27
  * 2.1.5
28
+ * 2.2.0
25
29
 
26
30
  Rails
27
31
  * 3.2.x
data/Rakefile CHANGED
@@ -1,14 +1,19 @@
1
- require "rspec/core/rake_task"
2
- require "bundler/gem_tasks"
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+ require 'rubocop/rake_task'
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec) do |t|
5
6
  t.verbose = false
7
+ t.ruby_opts = '-w'
6
8
  end
7
9
 
10
+ RuboCop::RakeTask.new(:rubocop)
11
+
8
12
  task :default do
9
13
  %w(mysql postgres sqlite).each do |db|
10
14
  puts "Running tests with `DB=#{db}`"
11
15
  ENV['DB'] = db
12
- Rake::Task["spec"].execute
16
+ Rake::Task['spec'].execute
13
17
  end
18
+ Rake::Task['rubocop'].execute
14
19
  end
data/double_entry.gemspec CHANGED
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
-
3
2
  lib = File.expand_path('../lib', __FILE__)
4
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
4
 
@@ -10,12 +9,11 @@ Gem::Specification.new do |gem|
10
9
  gem.version = DoubleEntry::VERSION
11
10
  gem.authors = ['Anthony Sellitti', 'Keith Pitt', 'Martin Jagusch', 'Martin Spickermann', 'Mark Turnley', 'Orien Madgwick', 'Pete Yandall', 'Stephanie Staub']
12
11
  gem.email = ['anthony.sellitti@envato.com', 'me@keithpitt.com', '_@mj.io', 'spickemann@gmail.com', 'mark@envato.com', '_@orien.io', 'pete@envato.com', 'staub.steph@gmail.com']
13
- # gem.description = %q{}
14
12
  gem.summary = 'Tools to build your double entry financial ledger'
15
13
  gem.homepage = 'https://github.com/envato/double_entry'
16
14
 
17
15
  gem.files = `git ls-files`.split($/)
18
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
+ gem.executables = gem.files.grep(%r{bin/}).map { |f| File.basename(f) }
19
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
18
  gem.require_paths = ['lib']
21
19
 
@@ -28,10 +26,13 @@ Gem::Specification.new do |gem|
28
26
  gem.add_development_dependency 'mysql2'
29
27
  gem.add_development_dependency 'pg'
30
28
  gem.add_development_dependency 'sqlite3'
29
+
31
30
  gem.add_development_dependency 'rspec'
32
31
  gem.add_development_dependency 'rspec-its'
32
+ gem.add_development_dependency 'rspec-instafail'
33
33
  gem.add_development_dependency 'database_cleaner'
34
34
  gem.add_development_dependency 'generator_spec'
35
35
  gem.add_development_dependency 'machinist'
36
36
  gem.add_development_dependency 'timecop'
37
+ gem.add_development_dependency 'rubocop'
37
38
  end
@@ -1,34 +1,28 @@
1
1
  # encoding: utf-8
2
- require "active_support/notifications"
2
+ require 'active_support/notifications'
3
3
 
4
4
  module ActiveRecord
5
-
6
5
  # These methods are available as class methods on ActiveRecord::Base.
7
6
  module LockingExtensions
8
-
9
7
  # Execute the given block within a database transaction, and retry the
10
8
  # transaction from the beginning if a RestartTransaction exception is raised.
11
9
  def restartable_transaction(&block)
12
- begin
13
- transaction(&block)
14
- rescue ActiveRecord::RestartTransaction
15
- retry
16
- end
10
+ transaction(&block)
11
+ rescue ActiveRecord::RestartTransaction
12
+ retry
17
13
  end
18
14
 
19
15
  # Execute the given block, and retry the current restartable transaction if a
20
16
  # MySQL deadlock occurs.
21
17
  def with_restart_on_deadlock
22
- begin
23
- yield
24
- rescue ActiveRecord::StatementInvalid => exception
25
- if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
26
- ActiveSupport::Notifications.publish("deadlock_restart.active_record", :exception => exception)
18
+ yield
19
+ rescue ActiveRecord::StatementInvalid => exception
20
+ if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
21
+ ActiveSupport::Notifications.publish('deadlock_restart.active_record', :exception => exception)
27
22
 
28
- raise ActiveRecord::RestartTransaction
29
- else
30
- raise
31
- end
23
+ raise ActiveRecord::RestartTransaction
24
+ else
25
+ raise
32
26
  end
33
27
  end
34
28
 
@@ -42,23 +36,21 @@ module ActiveRecord
42
36
  end
43
37
  end
44
38
 
45
- private
39
+ private
46
40
 
47
41
  def ignoring_duplicates
48
42
  # Error examples:
49
43
  # PG::Error: ERROR: duplicate key value violates unique constraint
50
44
  # Mysql2::Error: Duplicate entry 'keith' for key 'index_users_on_username': INSERT INTO `users...
51
45
  # ActiveRecord::RecordNotUnique SQLite3::ConstraintException: column username is not unique: INSERT INTO "users"...
52
- begin
53
- yield
54
- rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
55
- if exception.message =~ /duplicate/i || exception.message =~ /ConstraintException/
56
- ActiveSupport::Notifications.publish("duplicate_ignore.active_record", :exception => exception)
46
+ yield
47
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
48
+ if exception.message =~ /duplicate/i || exception.message =~ /ConstraintException/
49
+ ActiveSupport::Notifications.publish('duplicate_ignore.active_record', :exception => exception)
57
50
 
58
- # Just ignore it...someone else has already created the record.
59
- else
60
- raise
61
- end
51
+ # Just ignore it...someone else has already created the record.
52
+ else
53
+ raise
62
54
  end
63
55
  end
64
56
 
@@ -66,18 +58,16 @@ module ActiveRecord
66
58
  # Error examples:
67
59
  # PG::Error: ERROR: deadlock detected
68
60
  # Mysql::Error: Deadlock found when trying to get lock
69
- begin
70
- yield
71
- rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
72
- if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
73
- # Somebody else is in the midst of creating the record. We'd better
74
- # retry, so we ensure they're done before we move on.
75
- ActiveSupport::Notifications.publish("deadlock_retry.active_record", :exception => exception)
61
+ yield
62
+ rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
63
+ if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
64
+ # Somebody else is in the midst of creating the record. We'd better
65
+ # retry, so we ensure they're done before we move on.
66
+ ActiveSupport::Notifications.publish('deadlock_retry.active_record', :exception => exception)
76
67
 
77
- retry
78
- else
79
- raise
80
- end
68
+ retry
69
+ else
70
+ raise
81
71
  end
82
72
  end
83
73
  end
@@ -85,8 +75,6 @@ module ActiveRecord
85
75
  # Raise this inside a restartable_transaction to retry the transaction from the beginning.
86
76
  class RestartTransaction < RuntimeError
87
77
  end
88
-
89
78
  end
90
79
 
91
-
92
80
  ActiveRecord::Base.extend(ActiveRecord::LockingExtensions)
@@ -1,21 +1,21 @@
1
1
  # encoding: utf-8
2
- require "active_support/log_subscriber"
2
+ require 'active_support/log_subscriber'
3
3
 
4
4
  module ActiveRecord
5
5
  module LockingExtensions
6
6
  class LogSubscriber < ActiveSupport::LogSubscriber
7
7
  def deadlock_restart(event)
8
- info "Deadlock causing restart"
8
+ info 'Deadlock causing restart'
9
9
  debug event[:exception]
10
10
  end
11
11
 
12
12
  def deadlock_retry(event)
13
- info "Deadlock causing retry"
13
+ info 'Deadlock causing retry'
14
14
  debug event[:exception]
15
15
  end
16
16
 
17
17
  def duplicate_ignore(event)
18
- info "Duplicate ignored"
18
+ info 'Duplicate ignored'
19
19
  debug event[:exception]
20
20
  end
21
21
 
data/lib/double_entry.rb CHANGED
@@ -23,9 +23,7 @@ require 'double_entry/validation'
23
23
  # This module provides the public interfaces for everything to do with
24
24
  # transferring money around the system.
25
25
  module DoubleEntry
26
-
27
26
  class << self
28
-
29
27
  # Get the particular account instance with the provided identifier and
30
28
  # scope.
31
29
  #
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
3
  class Account
4
-
5
4
  class << self
6
5
  attr_writer :accounts, :scope_identifier_max_length, :account_identifier_max_length
7
6
 
@@ -39,18 +38,18 @@ module DoubleEntry
39
38
  end
40
39
 
41
40
  def find(identifier, scoped)
42
- account = detect do |account|
41
+ found_account = detect do |account|
43
42
  account.identifier == identifier && account.scoped? == scoped
44
43
  end
45
- raise UnknownAccount.new("account: #{identifier} scoped?: #{scoped}") unless account
46
- return account
44
+ fail UnknownAccount, "account: #{identifier} scoped?: #{scoped}" unless found_account
45
+ found_account
47
46
  end
48
47
 
49
48
  def <<(account)
50
49
  if any? { |a| a.identifier == account.identifier }
51
- raise DuplicateAccount.new
50
+ fail DuplicateAccount
52
51
  else
53
- super(account)
52
+ super
54
53
  end
55
54
  end
56
55
 
@@ -72,7 +71,7 @@ module DoubleEntry
72
71
  when String, Fixnum
73
72
  value
74
73
  else
75
- raise AccountScopeMismatchError.new("Expected instance of `#{@active_record_class}`, received instance of `#{value.class}`")
74
+ fail AccountScopeMismatchError, "Expected instance of `#{@active_record_class}`, received instance of `#{value.class}`"
76
75
  end
77
76
  end
78
77
  end
@@ -115,8 +114,8 @@ module DoubleEntry
115
114
  self == other
116
115
  end
117
116
 
118
- def <=>(account)
119
- [scope_identity.to_s, identifier.to_s] <=> [account.scope_identity.to_s, account.identifier.to_s]
117
+ def <=>(other)
118
+ [scope_identity.to_s, identifier.to_s] <=> [other.scope_identity.to_s, other.identifier.to_s]
120
119
  end
121
120
 
122
121
  def hash
@@ -135,14 +134,13 @@ module DoubleEntry
135
134
  to_s
136
135
  end
137
136
 
138
- private
137
+ private
139
138
 
140
139
  def ensure_scope_is_valid
141
140
  identity = scope_identity
142
141
  if identity && identity.length > Account.scope_identifier_max_length
143
- raise ScopeIdentifierTooLongError.new(
144
- "scope identifier '#{identity}' is too long. Please limit it to #{Account.scope_identifier_max_length} characters."
145
- )
142
+ fail ScopeIdentifierTooLongError,
143
+ "scope identifier '#{identity}' is too long. Please limit it to #{Account.scope_identifier_max_length} characters."
146
144
  end
147
145
  end
148
146
  end
@@ -156,9 +154,8 @@ module DoubleEntry
156
154
  @negative_only = args[:negative_only]
157
155
  @currency = args[:currency] || Money.default_currency
158
156
  if identifier.length > Account.account_identifier_max_length
159
- raise AccountIdentifierTooLongError.new(
160
- "account identifier '#{identifier}' is too long. Please limit it to #{Account.account_identifier_max_length} characters."
161
- )
157
+ fail AccountIdentifierTooLongError,
158
+ "account identifier '#{identifier}' is too long. Please limit it to #{Account.account_identifier_max_length} characters."
162
159
  end
163
160
  end
164
161
 
@@ -1,6 +1,5 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
-
4
3
  # Account balance records cache the current balance for each account. They
5
4
  # also provide a database representation of an account that we can use to do
6
5
  # DB level locking.
@@ -9,7 +8,6 @@ module DoubleEntry
9
8
  #
10
9
  # Account balances are created on demand when transfers occur.
11
10
  class AccountBalance < ActiveRecord::Base
12
-
13
11
  delegate :currency, :to => :account
14
12
 
15
13
  def balance
@@ -35,7 +33,5 @@ module DoubleEntry
35
33
  scope = scope.lock(true) if options[:lock]
36
34
  scope.first
37
35
  end
38
-
39
36
  end
40
-
41
37
  end