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
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