double_entry 1.0.1 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +432 -0
- data/README.md +36 -9
- data/double_entry.gemspec +20 -48
- data/lib/active_record/locking_extensions.rb +3 -3
- data/lib/active_record/locking_extensions/log_subscriber.rb +1 -1
- data/lib/double_entry/account.rb +38 -45
- data/lib/double_entry/account_balance.rb +18 -1
- data/lib/double_entry/errors.rb +13 -13
- data/lib/double_entry/line.rb +3 -2
- data/lib/double_entry/reporting.rb +26 -38
- data/lib/double_entry/reporting/aggregate.rb +43 -23
- data/lib/double_entry/reporting/aggregate_array.rb +16 -13
- data/lib/double_entry/reporting/line_aggregate.rb +3 -2
- data/lib/double_entry/reporting/line_aggregate_filter.rb +8 -10
- data/lib/double_entry/reporting/line_metadata_filter.rb +33 -0
- data/lib/double_entry/transfer.rb +33 -27
- data/lib/double_entry/validation.rb +1 -0
- data/lib/double_entry/validation/account_fixer.rb +36 -0
- data/lib/double_entry/validation/line_check.rb +22 -40
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/install_generator.rb +7 -1
- data/lib/generators/double_entry/install/templates/migration.rb +27 -25
- metadata +33 -243
- data/.gitignore +0 -32
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -2
- data/Gemfile +0 -2
- data/Rakefile +0 -15
- data/script/jack_hammer +0 -210
- data/script/setup.sh +0 -8
- data/spec/active_record/locking_extensions_spec.rb +0 -110
- data/spec/double_entry/account_balance_spec.rb +0 -7
- data/spec/double_entry/account_spec.rb +0 -130
- data/spec/double_entry/balance_calculator_spec.rb +0 -88
- data/spec/double_entry/configuration_spec.rb +0 -50
- data/spec/double_entry/line_spec.rb +0 -80
- data/spec/double_entry/locking_spec.rb +0 -214
- data/spec/double_entry/performance/double_entry_performance_spec.rb +0 -32
- data/spec/double_entry/performance/reporting/aggregate_performance_spec.rb +0 -50
- data/spec/double_entry/reporting/aggregate_array_spec.rb +0 -123
- data/spec/double_entry/reporting/aggregate_spec.rb +0 -205
- data/spec/double_entry/reporting/line_aggregate_filter_spec.rb +0 -90
- data/spec/double_entry/reporting/line_aggregate_spec.rb +0 -39
- data/spec/double_entry/reporting/month_range_spec.rb +0 -139
- data/spec/double_entry/reporting/time_range_array_spec.rb +0 -169
- data/spec/double_entry/reporting/time_range_spec.rb +0 -45
- data/spec/double_entry/reporting/week_range_spec.rb +0 -103
- data/spec/double_entry/reporting_spec.rb +0 -181
- data/spec/double_entry/transfer_spec.rb +0 -93
- data/spec/double_entry/validation/line_check_spec.rb +0 -99
- data/spec/double_entry_spec.rb +0 -428
- data/spec/generators/double_entry/install/install_generator_spec.rb +0 -30
- data/spec/spec_helper.rb +0 -118
- data/spec/support/accounts.rb +0 -21
- data/spec/support/blueprints.rb +0 -43
- data/spec/support/database.example.yml +0 -21
- data/spec/support/database.travis.yml +0 -24
- data/spec/support/double_entry_spec_helper.rb +0 -27
- data/spec/support/gemfiles/Gemfile.rails-3.2.x +0 -8
- data/spec/support/gemfiles/Gemfile.rails-4.1.x +0 -6
- data/spec/support/gemfiles/Gemfile.rails-4.2.x +0 -5
- data/spec/support/gemfiles/Gemfile.rails-5.0.x +0 -5
- data/spec/support/performance_helper.rb +0 -26
- data/spec/support/reporting_configuration.rb +0 -6
- data/spec/support/schema.rb +0 -74
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'action_controller'
|
2
|
-
require 'generator_spec/test_case'
|
3
|
-
require 'generators/double_entry/install/install_generator'
|
4
|
-
|
5
|
-
RSpec.describe DoubleEntry::Generators::InstallGenerator do
|
6
|
-
include GeneratorSpec::TestCase
|
7
|
-
|
8
|
-
destination File.expand_path('../../../../../tmp', __FILE__)
|
9
|
-
|
10
|
-
before do
|
11
|
-
prepare_destination
|
12
|
-
run_generator
|
13
|
-
end
|
14
|
-
|
15
|
-
specify do
|
16
|
-
expect(destination_root).to have_structure {
|
17
|
-
directory 'db' do
|
18
|
-
directory 'migrate' do
|
19
|
-
migration 'create_double_entry_tables' do
|
20
|
-
contains 'class CreateDoubleEntryTable'
|
21
|
-
contains 'create_table "double_entry_account_balances"'
|
22
|
-
contains 'create_table "double_entry_lines"'
|
23
|
-
contains 'create_table "double_entry_line_aggregates"'
|
24
|
-
contains 'create_table "double_entry_line_checks"'
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
}
|
29
|
-
end
|
30
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,118 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'bundler/setup'
|
3
|
-
require 'active_record'
|
4
|
-
require 'active_support'
|
5
|
-
|
6
|
-
if ActiveRecord::VERSION::MAJOR < 4
|
7
|
-
require 'test-unit'
|
8
|
-
Test::Unit::AutoRunner.need_auto_run = false
|
9
|
-
end
|
10
|
-
|
11
|
-
db_engine = ENV['DB'] || 'mysql'
|
12
|
-
|
13
|
-
FileUtils.mkdir_p 'tmp'
|
14
|
-
FileUtils.mkdir_p 'log'
|
15
|
-
FileUtils.rm 'log/test.log', :force => true
|
16
|
-
|
17
|
-
database_config_file = File.expand_path('../support/database.yml', __FILE__)
|
18
|
-
if File.exist?(database_config_file)
|
19
|
-
ActiveRecord::Base.establish_connection YAML.load_file(database_config_file)[db_engine]
|
20
|
-
else
|
21
|
-
puts 'Please configure your spec/support/database.yml file.'
|
22
|
-
puts 'See spec/support/database.example.yml'
|
23
|
-
exit 1
|
24
|
-
end
|
25
|
-
|
26
|
-
# Buffered Logger was deprecated in ActiveSupport 4.0.0 and was removed in 4.1.0
|
27
|
-
# Logger was added in ActiveSupport 4.0.0
|
28
|
-
if defined? ActiveSupport::Logger
|
29
|
-
ActiveRecord::Base.logger = ActiveSupport::Logger.new('log/test.log')
|
30
|
-
else
|
31
|
-
ActiveRecord::Base.logger = ActiveSupport::BufferedLogger.new('log/test.log')
|
32
|
-
end
|
33
|
-
|
34
|
-
I18n.config.enforce_available_locales = false
|
35
|
-
|
36
|
-
silence_warnings do
|
37
|
-
require 'rspec'
|
38
|
-
require 'rspec/its'
|
39
|
-
require 'database_cleaner'
|
40
|
-
require 'machinist/active_record'
|
41
|
-
require 'timecop'
|
42
|
-
require 'money'
|
43
|
-
end
|
44
|
-
|
45
|
-
require 'double_entry'
|
46
|
-
|
47
|
-
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each { |f| require f }
|
48
|
-
|
49
|
-
RSpec.configure do |config|
|
50
|
-
# rspec-expectations config goes here. You can use an alternate
|
51
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
52
|
-
# assertions if you prefer.
|
53
|
-
config.expect_with :rspec do |expectations|
|
54
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
55
|
-
# and `failure_message` of custom matchers include text for helper methods
|
56
|
-
# defined using `chain`, e.g.:
|
57
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
58
|
-
# # => "be bigger than 2 and smaller than 4"
|
59
|
-
# ...rather than:
|
60
|
-
# # => "be bigger than 2"
|
61
|
-
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
62
|
-
expectations.warn_about_potential_false_positives = false
|
63
|
-
end
|
64
|
-
|
65
|
-
# rspec-mocks config goes here. You can use an alternate test double
|
66
|
-
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
67
|
-
config.mock_with :rspec do |mocks|
|
68
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
69
|
-
# a real object. This is generally recommended, and will default to
|
70
|
-
# `true` in RSpec 4.
|
71
|
-
mocks.verify_partial_doubles = true
|
72
|
-
end
|
73
|
-
|
74
|
-
# These two settings work together to allow you to limit a spec run
|
75
|
-
# to individual examples or groups you care about by tagging them with
|
76
|
-
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
77
|
-
# get run.
|
78
|
-
config.filter_run :focus
|
79
|
-
config.run_all_when_everything_filtered = true
|
80
|
-
|
81
|
-
# Limits the available syntax to the non-monkey patched syntax that is recommended.
|
82
|
-
# For more details, see:
|
83
|
-
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
84
|
-
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
85
|
-
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
86
|
-
config.disable_monkey_patching!
|
87
|
-
|
88
|
-
# Many RSpec users commonly either run the entire suite or an individual
|
89
|
-
# file, and it's useful to allow more verbose output when running an
|
90
|
-
# individual spec file.
|
91
|
-
if config.files_to_run.one?
|
92
|
-
# Use the documentation formatter for detailed output,
|
93
|
-
# unless a formatter has already been configured
|
94
|
-
# (e.g. via a command-line flag).
|
95
|
-
config.default_formatter = 'doc'
|
96
|
-
else
|
97
|
-
config.default_formatter = 'RSpec::Instafail'
|
98
|
-
end
|
99
|
-
|
100
|
-
# Print the 5 slowest examples and example groups at the
|
101
|
-
# end of the spec run, to help surface which specs are running
|
102
|
-
# particularly slow.
|
103
|
-
config.profile_examples = 5
|
104
|
-
|
105
|
-
config.include DoubleEntrySpecHelper
|
106
|
-
|
107
|
-
config.before(:suite) do
|
108
|
-
DatabaseCleaner.strategy = :truncation
|
109
|
-
end
|
110
|
-
|
111
|
-
config.before do
|
112
|
-
DatabaseCleaner.clean
|
113
|
-
end
|
114
|
-
|
115
|
-
config.after do
|
116
|
-
Timecop.return
|
117
|
-
end
|
118
|
-
end
|
data/spec/support/accounts.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require_relative 'blueprints'
|
3
|
-
DoubleEntry.configure do |config|
|
4
|
-
# A set of accounts to test with
|
5
|
-
config.define_accounts do |accounts|
|
6
|
-
user_scope = accounts.active_record_scope_identifier(User)
|
7
|
-
accounts.define(:identifier => :savings, :scope_identifier => user_scope, :positive_only => true)
|
8
|
-
accounts.define(:identifier => :checking, :scope_identifier => user_scope, :positive_only => true)
|
9
|
-
accounts.define(:identifier => :test, :scope_identifier => user_scope)
|
10
|
-
accounts.define(:identifier => :btc_test, :scope_identifier => user_scope, :currency => 'BTC')
|
11
|
-
accounts.define(:identifier => :btc_savings, :scope_identifier => user_scope, :currency => 'BTC')
|
12
|
-
end
|
13
|
-
|
14
|
-
# A set of allowed transfers between accounts
|
15
|
-
config.define_transfers do |transfers|
|
16
|
-
transfers.define(:from => :test, :to => :savings, :code => :bonus)
|
17
|
-
transfers.define(:from => :test, :to => :checking, :code => :pay)
|
18
|
-
transfers.define(:from => :savings, :to => :test, :code => :test_withdrawal)
|
19
|
-
transfers.define(:from => :btc_test, :to => :btc_savings, :code => :btc_test_transfer)
|
20
|
-
end
|
21
|
-
end
|
data/spec/support/blueprints.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
class UserBlueprint < Machinist::ActiveRecord::Blueprint
|
2
|
-
def make!(attributes = {})
|
3
|
-
savings_balance = attributes.delete(:savings_balance)
|
4
|
-
checking_balance = attributes.delete(:checking_balance)
|
5
|
-
bitcoin_balance = attributes.delete(:bitcoin_balance)
|
6
|
-
user = super(attributes)
|
7
|
-
if savings_balance
|
8
|
-
DoubleEntry.transfer(
|
9
|
-
savings_balance,
|
10
|
-
:from => DoubleEntry.account(:test, :scope => user),
|
11
|
-
:to => DoubleEntry.account(:savings, :scope => user),
|
12
|
-
:code => :bonus,
|
13
|
-
)
|
14
|
-
end
|
15
|
-
if checking_balance
|
16
|
-
DoubleEntry.transfer(
|
17
|
-
checking_balance,
|
18
|
-
:from => DoubleEntry.account(:test, :scope => user),
|
19
|
-
:to => DoubleEntry.account(:checking, :scope => user),
|
20
|
-
:code => :pay,
|
21
|
-
)
|
22
|
-
end
|
23
|
-
if bitcoin_balance
|
24
|
-
DoubleEntry.transfer(
|
25
|
-
bitcoin_balance,
|
26
|
-
:from => DoubleEntry.account(:btc_test, :scope => user),
|
27
|
-
:to => DoubleEntry.account(:btc_savings, :scope => user),
|
28
|
-
:code => :btc_test_transfer,
|
29
|
-
)
|
30
|
-
end
|
31
|
-
user
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class User < ActiveRecord::Base
|
36
|
-
def self.blueprint_class
|
37
|
-
UserBlueprint
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
User.blueprint do
|
42
|
-
username { "user#{sn}" }
|
43
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
postgres:
|
2
|
-
host: localhost
|
3
|
-
adapter: postgresql
|
4
|
-
encoding: unicode
|
5
|
-
database: double_entry_test
|
6
|
-
pool: 100
|
7
|
-
username: postgres
|
8
|
-
password:
|
9
|
-
min_messages: warning
|
10
|
-
mysql:
|
11
|
-
adapter: mysql2
|
12
|
-
encoding: utf8
|
13
|
-
database: double_entry_test
|
14
|
-
pool: 100
|
15
|
-
username: root
|
16
|
-
password:
|
17
|
-
sqlite:
|
18
|
-
adapter: sqlite3
|
19
|
-
encoding: utf8
|
20
|
-
database: tmp/double_entry_test.sqlite3
|
21
|
-
pool: 100
|
@@ -1,24 +0,0 @@
|
|
1
|
-
mysql:
|
2
|
-
adapter: mysql2
|
3
|
-
username: root
|
4
|
-
password:
|
5
|
-
database: double_entry_test
|
6
|
-
pool: 100
|
7
|
-
timeout: 5000
|
8
|
-
host: 127.0.0.1
|
9
|
-
|
10
|
-
postgres:
|
11
|
-
adapter: postgresql
|
12
|
-
username: postgres
|
13
|
-
password:
|
14
|
-
database: double_entry_test
|
15
|
-
min_messages: ERROR
|
16
|
-
pool: 100
|
17
|
-
timeout: 5000
|
18
|
-
host: localhost
|
19
|
-
|
20
|
-
sqlite:
|
21
|
-
adapter: sqlite3
|
22
|
-
encoding: utf8
|
23
|
-
database: tmp/double_entry_test.sqlite3
|
24
|
-
pool: 100
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
module DoubleEntrySpecHelper
|
3
|
-
def lines_for_account(account)
|
4
|
-
lines = DoubleEntry::Line.order(:id)
|
5
|
-
lines = lines.where(:scope => account.scope_identity) if account.scoped?
|
6
|
-
lines = lines.where(:account => account.identifier.to_s)
|
7
|
-
lines
|
8
|
-
end
|
9
|
-
|
10
|
-
def perform_deposit(user, amount)
|
11
|
-
DoubleEntry.transfer(
|
12
|
-
Money.new(amount),
|
13
|
-
:from => DoubleEntry.account(:test, :scope => user),
|
14
|
-
:to => DoubleEntry.account(:savings, :scope => user),
|
15
|
-
:code => :bonus,
|
16
|
-
)
|
17
|
-
end
|
18
|
-
|
19
|
-
def perform_btc_deposit(user, amount)
|
20
|
-
DoubleEntry.transfer(
|
21
|
-
Money.new(amount, :btc),
|
22
|
-
:from => DoubleEntry.account(:btc_test, :scope => user),
|
23
|
-
:to => DoubleEntry.account(:btc_savings, :scope => user),
|
24
|
-
:code => :btc_test_transfer,
|
25
|
-
)
|
26
|
-
end
|
27
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module PerformanceHelper
|
2
|
-
require 'ruby-prof'
|
3
|
-
|
4
|
-
def start_profiling(measure_mode = RubyProf::PROCESS_TIME)
|
5
|
-
RubyProf.measure_mode = measure_mode
|
6
|
-
RubyProf.start
|
7
|
-
end
|
8
|
-
|
9
|
-
def stop_profiling(profile_name = nil)
|
10
|
-
result = RubyProf.stop
|
11
|
-
puts "#{profile_name} Time: #{format('%#.3g', total_time(result))}s"
|
12
|
-
unless ENV.fetch('CI', false)
|
13
|
-
if profile_name
|
14
|
-
outdir = './profiles'
|
15
|
-
Dir.mkdir(outdir) unless Dir.exist?(outdir)
|
16
|
-
printer = RubyProf::MultiPrinter.new(result)
|
17
|
-
printer.print(:path => outdir, :profile => profile_name)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
result
|
21
|
-
end
|
22
|
-
|
23
|
-
def total_time(result)
|
24
|
-
result.threads.inject(0) { |time, thread| time + thread.total_time }
|
25
|
-
end
|
26
|
-
end
|
data/spec/support/schema.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
ActiveRecord::Schema.define do
|
2
|
-
self.verbose = false
|
3
|
-
|
4
|
-
create_table "double_entry_account_balances", :force => true do |t|
|
5
|
-
t.string "account", :limit => 31, :null => false
|
6
|
-
t.string "scope", :limit => 23
|
7
|
-
t.integer "balance", :null => false
|
8
|
-
t.timestamps :null => false
|
9
|
-
end
|
10
|
-
|
11
|
-
add_index "double_entry_account_balances", ["account"], :name => "index_account_balances_on_account"
|
12
|
-
add_index "double_entry_account_balances", ["scope", "account"], :name => "index_account_balances_on_scope_and_account", :unique => true
|
13
|
-
|
14
|
-
create_table "double_entry_lines", :force => true do |t|
|
15
|
-
t.string "account", :limit => 31, :null => false
|
16
|
-
t.string "scope", :limit => 23
|
17
|
-
t.string "code", :limit => 47, :null => false
|
18
|
-
t.integer "amount", :null => false
|
19
|
-
t.integer "balance", :null => false
|
20
|
-
t.integer "partner_id"
|
21
|
-
t.string "partner_account", :limit => 31, :null => false
|
22
|
-
t.string "partner_scope", :limit => 23
|
23
|
-
t.integer "detail_id"
|
24
|
-
t.string "detail_type"
|
25
|
-
t.timestamps :null => false
|
26
|
-
end
|
27
|
-
|
28
|
-
add_index "double_entry_lines", ["account", "code", "created_at"], :name => "lines_account_code_created_at_idx"
|
29
|
-
add_index "double_entry_lines", ["account", "created_at"], :name => "lines_account_created_at_idx"
|
30
|
-
add_index "double_entry_lines", ["scope", "account", "created_at"], :name => "lines_scope_account_created_at_idx"
|
31
|
-
add_index "double_entry_lines", ["scope", "account", "id"], :name => "lines_scope_account_id_idx"
|
32
|
-
|
33
|
-
create_table "double_entry_line_aggregates", :force => true do |t|
|
34
|
-
t.string "function", :limit => 15, :null => false
|
35
|
-
t.string "account", :limit => 31, :null => false
|
36
|
-
t.string "code", :limit => 47
|
37
|
-
t.string "scope", :limit => 23
|
38
|
-
t.integer "year"
|
39
|
-
t.integer "month"
|
40
|
-
t.integer "week"
|
41
|
-
t.integer "day"
|
42
|
-
t.integer "hour"
|
43
|
-
t.integer "amount", :null => false
|
44
|
-
t.string "filter"
|
45
|
-
t.string "range_type", :limit => 15, :null => false
|
46
|
-
t.timestamps :null => false
|
47
|
-
end
|
48
|
-
|
49
|
-
add_index "double_entry_line_aggregates", ["function", "account", "code", "year", "month", "week", "day"], :name => "line_aggregate_idx"
|
50
|
-
|
51
|
-
create_table "double_entry_line_checks", :force => true do |t|
|
52
|
-
t.integer "last_line_id", :null => false
|
53
|
-
t.boolean "errors_found", :null => false
|
54
|
-
t.text "log"
|
55
|
-
t.timestamps :null => false
|
56
|
-
end
|
57
|
-
|
58
|
-
create_table "double_entry_line_metadata", :force => true do |t|
|
59
|
-
t.integer "line_id", :null => false
|
60
|
-
t.string "key", :limit => 48, :null => false
|
61
|
-
t.string "value", :limit => 64, :null => false
|
62
|
-
t.timestamps :null => false
|
63
|
-
end
|
64
|
-
|
65
|
-
add_index "double_entry_line_metadata", ["line_id", "key", "value"], :name => "lines_meta_line_id_key_value_idx"
|
66
|
-
|
67
|
-
# test table only
|
68
|
-
create_table "users", :force => true do |t|
|
69
|
-
t.string "username", :null => false
|
70
|
-
t.timestamps :null => false
|
71
|
-
end
|
72
|
-
|
73
|
-
add_index "users", ["username"], :name => "index_users_on_username", :unique => true
|
74
|
-
end
|