double_entry 0.10.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +5 -1
- data/README.md +16 -0
- data/double_entry.gemspec +35 -0
- data/lib/double_entry.rb +1 -0
- data/lib/double_entry/line.rb +1 -0
- data/lib/double_entry/line_metadata.rb +18 -0
- data/lib/double_entry/locking.rb +11 -0
- data/lib/double_entry/reporting.rb +26 -14
- data/lib/double_entry/reporting/aggregate.rb +11 -15
- data/lib/double_entry/reporting/aggregate_array.rb +1 -1
- data/lib/double_entry/reporting/line_aggregate.rb +2 -23
- data/lib/double_entry/reporting/line_aggregate_filter.rb +81 -0
- data/lib/double_entry/transfer.rb +42 -25
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/templates/migration.rb +10 -0
- data/spec/double_entry/locking_spec.rb +29 -0
- data/spec/double_entry/performance/double_entry_performance_spec.rb +32 -0
- data/spec/double_entry/performance/reporting/aggregate_performance_spec.rb +50 -0
- data/spec/double_entry/reporting/aggregate_array_spec.rb +10 -10
- data/spec/double_entry/reporting/aggregate_spec.rb +57 -114
- data/spec/double_entry/reporting/line_aggregate_filter_spec.rb +90 -0
- data/spec/double_entry/reporting/line_aggregate_spec.rb +35 -7
- data/spec/double_entry/reporting_spec.rb +136 -0
- data/spec/double_entry/transfer_spec.rb +58 -1
- data/spec/support/performance_helper.rb +26 -0
- data/spec/support/schema.rb +9 -0
- metadata +124 -4
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
RSpec.describe DoubleEntry::Reporting::LineAggregateFilter do
|
3
|
+
describe '.filter' do
|
4
|
+
let(:function) { :sum }
|
5
|
+
let(:account) { :account }
|
6
|
+
let(:code) { :transfer_code }
|
7
|
+
let(:filter_criteria) { nil }
|
8
|
+
let(:start) { Time.parse('2014-07-27 10:55:44 +1000') }
|
9
|
+
let(:finish) { Time.parse('2015-07-27 10:55:44 +1000') }
|
10
|
+
let(:range) do
|
11
|
+
instance_double(DoubleEntry::Reporting::MonthRange, :start => start, :finish => finish)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:lines_scope) { spy(DoubleEntry::Line) }
|
15
|
+
|
16
|
+
subject(:filter) do
|
17
|
+
DoubleEntry::Reporting::LineAggregateFilter.new(account, code, range, filter_criteria)
|
18
|
+
end
|
19
|
+
|
20
|
+
before do
|
21
|
+
stub_const('DoubleEntry::Line', lines_scope)
|
22
|
+
|
23
|
+
allow(DoubleEntry::LineMetadata).to receive(:table_name).and_return('double_entry_line_metadata')
|
24
|
+
|
25
|
+
allow(lines_scope).to receive(:where).and_return(lines_scope)
|
26
|
+
allow(lines_scope).to receive(:joins).and_return(lines_scope)
|
27
|
+
allow(lines_scope).to receive(:ten_dollar_purchases).and_return(lines_scope)
|
28
|
+
allow(lines_scope).to receive(:ten_dollar_purchases_by_category).and_return(lines_scope)
|
29
|
+
|
30
|
+
filter.filter
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with named scopes specified' do
|
34
|
+
let(:filter_criteria) do
|
35
|
+
[
|
36
|
+
# an example of calling a named scope called with arguments
|
37
|
+
{
|
38
|
+
:scope => {
|
39
|
+
:name => :ten_dollar_purchases_by_category,
|
40
|
+
:arguments => [:cat_videos, :cat_pictures],
|
41
|
+
},
|
42
|
+
},
|
43
|
+
# an example of calling a named scope with no arguments
|
44
|
+
{
|
45
|
+
:scope => {
|
46
|
+
:name => :ten_dollar_purchases,
|
47
|
+
},
|
48
|
+
},
|
49
|
+
# an example of providing a single metadatum criteria to filter on
|
50
|
+
{
|
51
|
+
:metadata => {
|
52
|
+
:meme => 'business_cat',
|
53
|
+
},
|
54
|
+
},
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'filters by all the scopes provided' do
|
59
|
+
expect(DoubleEntry::Line).to have_received(:ten_dollar_purchases)
|
60
|
+
expect(DoubleEntry::Line).to have_received(:ten_dollar_purchases_by_category).
|
61
|
+
with(:cat_videos, :cat_pictures)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'filters by all the metadata provided' do
|
65
|
+
expect(DoubleEntry::Line).to have_received(:joins).with(:metadata)
|
66
|
+
expect(DoubleEntry::Line).to have_received(:where).
|
67
|
+
with(:double_entry_line_metadata => { :key => :meme, :value => 'business_cat' })
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with a code specified' do
|
72
|
+
let(:code) { :transfer_code }
|
73
|
+
|
74
|
+
it 'retrieves the appropriate lines for aggregation' do
|
75
|
+
expect(DoubleEntry::Line).to have_received(:where).with(:account => account)
|
76
|
+
expect(DoubleEntry::Line).to have_received(:where).with(:created_at => start..finish)
|
77
|
+
expect(DoubleEntry::Line).to have_received(:where).with(:code => code)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'with no code specified' do
|
82
|
+
let(:code) { nil }
|
83
|
+
|
84
|
+
it 'retrieves the appropriate lines for aggregation' do
|
85
|
+
expect(DoubleEntry::Line).to have_received(:where).with(:account => account)
|
86
|
+
expect(DoubleEntry::Line).to have_received(:where).with(:created_at => start..finish)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,11 +1,39 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
RSpec.describe DoubleEntry::Reporting::LineAggregate do
|
3
|
+
describe '.table_name' do
|
4
|
+
subject { DoubleEntry::Reporting::LineAggregate.table_name }
|
5
|
+
it { should eq('double_entry_line_aggregates') }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#aggregate' do
|
9
|
+
let(:line_relation) { spy }
|
10
|
+
let(:filter) do
|
11
|
+
instance_double(DoubleEntry::Reporting::LineAggregateFilter, :filter => line_relation)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:function) { :sum }
|
15
|
+
let(:account) { spy }
|
16
|
+
let(:code) { spy }
|
17
|
+
let(:named_scopes) { spy }
|
18
|
+
let(:range) { spy }
|
19
|
+
|
20
|
+
subject(:aggregate) do
|
21
|
+
DoubleEntry::Reporting::LineAggregate.aggregate(function, account, code, range, named_scopes)
|
22
|
+
end
|
23
|
+
|
24
|
+
before do
|
25
|
+
allow(DoubleEntry::Reporting::LineAggregateFilter).to receive(:new).and_return(filter)
|
26
|
+
aggregate
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'applies the specified filters' do
|
30
|
+
expect(DoubleEntry::Reporting::LineAggregateFilter).to have_received(:new).
|
31
|
+
with(account, code, range, named_scopes)
|
32
|
+
expect(filter).to have_received(:filter)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'performs the aggregation on the filtered lines' do
|
36
|
+
expect(line_relation).to have_received(:sum).with(:amount)
|
9
37
|
end
|
10
38
|
end
|
11
39
|
end
|
@@ -42,4 +42,140 @@ RSpec.describe DoubleEntry::Reporting do
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
describe '::aggregate' do
|
47
|
+
before do
|
48
|
+
# get rid of "helpful" predefined config
|
49
|
+
@config_accounts = DoubleEntry.configuration.accounts
|
50
|
+
@config_transfers = DoubleEntry.configuration.transfers
|
51
|
+
DoubleEntry.configuration.accounts = DoubleEntry::Account::Set.new
|
52
|
+
DoubleEntry.configuration.transfers = DoubleEntry::Transfer::Set.new
|
53
|
+
|
54
|
+
DoubleEntry.configure do |config|
|
55
|
+
config.define_accounts do |accounts|
|
56
|
+
accounts.define(:identifier => :savings)
|
57
|
+
accounts.define(:identifier => :cash)
|
58
|
+
accounts.define(:identifier => :credit)
|
59
|
+
end
|
60
|
+
|
61
|
+
config.define_transfers do |transfers|
|
62
|
+
transfers.define(:from => :savings, :to => :cash, :code => :spend)
|
63
|
+
transfers.define(:from => :cash, :to => :savings, :code => :save)
|
64
|
+
transfers.define(:from => :cash, :to => :credit, :code => :bill)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
cash = DoubleEntry.account(:cash)
|
69
|
+
savings = DoubleEntry.account(:savings)
|
70
|
+
credit = DoubleEntry.account(:credit)
|
71
|
+
DoubleEntry.transfer(Money.new(10_00), :from => cash, :to => savings, :code => :save, :metadata => { :reason => 'payday' })
|
72
|
+
DoubleEntry.transfer(Money.new(10_00), :from => cash, :to => savings, :code => :save, :metadata => { :reason => 'payday' })
|
73
|
+
DoubleEntry.transfer(Money.new(20_00), :from => cash, :to => savings, :code => :save)
|
74
|
+
DoubleEntry.transfer(Money.new(20_00), :from => cash, :to => savings, :code => :save)
|
75
|
+
DoubleEntry.transfer(Money.new(30_00), :from => cash, :to => credit, :code => :bill)
|
76
|
+
DoubleEntry.transfer(Money.new(40_00), :from => cash, :to => credit, :code => :bill)
|
77
|
+
DoubleEntry.transfer(Money.new(50_00), :from => savings, :to => cash, :code => :spend)
|
78
|
+
DoubleEntry.transfer(Money.new(60_00), :from => savings, :to => cash, :code => :spend, :metadata => { :category => 'entertainment' })
|
79
|
+
end
|
80
|
+
|
81
|
+
after do
|
82
|
+
# restore "helpful" predefined config
|
83
|
+
DoubleEntry.configuration.accounts = @config_accounts
|
84
|
+
DoubleEntry.configuration.transfers = @config_transfers
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'filter solely on transaction identifiers and time' do
|
88
|
+
let(:function) { :sum }
|
89
|
+
let(:account) { :savings }
|
90
|
+
let(:code) { :save }
|
91
|
+
let(:range) { DoubleEntry::Reporting::MonthRange.current }
|
92
|
+
|
93
|
+
subject(:aggregate) do
|
94
|
+
DoubleEntry::Reporting.aggregate(function, account, code, range)
|
95
|
+
end
|
96
|
+
|
97
|
+
specify 'Total attempted to save' do
|
98
|
+
expect(aggregate).to eq(Money.new(60_00))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'filter by named scope that does not take arguments' do
|
103
|
+
let(:function) { :sum }
|
104
|
+
let(:account) { :savings }
|
105
|
+
let(:code) { :save }
|
106
|
+
let(:range) { DoubleEntry::Reporting::MonthRange.current }
|
107
|
+
|
108
|
+
subject(:aggregate) do
|
109
|
+
DoubleEntry::Reporting.aggregate(
|
110
|
+
function, account, code, range,
|
111
|
+
:filter => [
|
112
|
+
:scope => {
|
113
|
+
:name => :ten_dollar_transfers,
|
114
|
+
},
|
115
|
+
]
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
before do
|
120
|
+
DoubleEntry::Line.class_eval do
|
121
|
+
scope :ten_dollar_transfers, -> { where(:amount => Money.new(10_00).fractional) }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
specify 'Total amount of $10 transfers attempted to save' do
|
126
|
+
expect(aggregate).to eq(Money.new(20_00))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'filter by named scope that takes arguments' do
|
131
|
+
let(:function) { :sum }
|
132
|
+
let(:account) { :savings }
|
133
|
+
let(:code) { :save }
|
134
|
+
let(:range) { DoubleEntry::Reporting::MonthRange.current }
|
135
|
+
|
136
|
+
subject(:aggregate) do
|
137
|
+
DoubleEntry::Reporting.aggregate(
|
138
|
+
function, account, code, range,
|
139
|
+
:filter => [
|
140
|
+
:scope => {
|
141
|
+
:name => :specific_transfer_amount,
|
142
|
+
:arguments => [Money.new(20_00)],
|
143
|
+
},
|
144
|
+
]
|
145
|
+
)
|
146
|
+
end
|
147
|
+
|
148
|
+
before do
|
149
|
+
DoubleEntry::Line.class_eval do
|
150
|
+
scope :specific_transfer_amount, ->(amount) { where(:amount => amount.fractional) }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
specify 'Total amount of transfers of $20 attempted to save' do
|
155
|
+
expect(aggregate).to eq(Money.new(40_00))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe 'filter by metadata' do
|
160
|
+
let(:function) { :sum }
|
161
|
+
let(:account) { :savings }
|
162
|
+
let(:code) { :save }
|
163
|
+
let(:range) { DoubleEntry::Reporting::MonthRange.current }
|
164
|
+
|
165
|
+
subject(:aggregate) do
|
166
|
+
DoubleEntry::Reporting.aggregate(
|
167
|
+
function, account, code, range,
|
168
|
+
:filter => [
|
169
|
+
:metadata => {
|
170
|
+
:reason => 'payday',
|
171
|
+
},
|
172
|
+
]
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
specify 'Total amount of transfers saved because payday' do
|
177
|
+
expect(aggregate).to eq(Money.new(20_00))
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
45
181
|
end
|
@@ -17,13 +17,70 @@ module DoubleEntry
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
describe '::transfer' do
|
21
|
+
let(:amount) { Money.new(10_00) }
|
22
|
+
let(:user) { User.make! }
|
23
|
+
let(:test) { DoubleEntry.account(:test, :scope => user) }
|
24
|
+
let(:savings) { DoubleEntry.account(:savings, :scope => user) }
|
25
|
+
let(:new_lines) { Line.all[-2..-1] }
|
26
|
+
|
27
|
+
subject(:transfer) { Transfer.transfer(amount, options) }
|
28
|
+
|
29
|
+
context 'without metadata' do
|
30
|
+
let(:options) { { :from => test, :to => savings, :code => :bonus } }
|
31
|
+
|
32
|
+
it 'creates lines' do
|
33
|
+
expect { transfer }.to change { Line.count }.by 2
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not create metadata lines' do
|
37
|
+
expect { transfer }.not_to change { LineMetadata.count }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'with metadata' do
|
42
|
+
let(:options) { { :from => test, :to => savings, :code => :bonus, :metadata => { :country => 'AU', :tax => 'GST' } } }
|
43
|
+
let(:new_metadata) { LineMetadata.all[-4..-1] }
|
44
|
+
|
45
|
+
it 'creates metadata lines' do
|
46
|
+
expect { transfer }.to change { LineMetadata.count }.by 4
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'associates the metadata lines with the transfer lines' do
|
50
|
+
transfer
|
51
|
+
expect(new_metadata.count { |meta| meta.line == new_lines.first }).to be 2
|
52
|
+
expect(new_metadata.count { |meta| meta.line == new_lines.last }).to be 2
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'stores the first key/value pair' do
|
56
|
+
transfer
|
57
|
+
countries = new_metadata.select { |meta| meta.key == :country }
|
58
|
+
expect(countries.size).to be 2
|
59
|
+
expect(countries.count { |meta| meta.value == 'AU' }).to be 2
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'associates the first key/value pair with both lines' do
|
63
|
+
transfer
|
64
|
+
countries = new_metadata.select { |meta| meta.key == :country }
|
65
|
+
expect(countries.map(&:line).uniq.size).to be 2
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'stores another key/value pair' do
|
69
|
+
transfer
|
70
|
+
taxes = new_metadata.select { |meta| meta.key == :tax }
|
71
|
+
expect(taxes.size).to be 2
|
72
|
+
expect(taxes.count { |meta| meta.value == 'GST' }).to be 2
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
20
77
|
describe Transfer::Set do
|
21
78
|
describe '#define' do
|
22
79
|
before do
|
23
80
|
subject.define(
|
24
81
|
:code => 'code',
|
25
82
|
:from => double(:identifier => 'from'),
|
26
|
-
:to
|
83
|
+
:to => double(:identifier => 'to'),
|
27
84
|
)
|
28
85
|
end
|
29
86
|
its(:first) { should be_a Transfer }
|
@@ -0,0 +1,26 @@
|
|
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
CHANGED
@@ -55,6 +55,15 @@ ActiveRecord::Schema.define do
|
|
55
55
|
t.timestamps :null => false
|
56
56
|
end
|
57
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
|
+
|
58
67
|
# test table only
|
59
68
|
create_table "users", :force => true do |t|
|
60
69
|
t.string "username", :null => false
|
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: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Sellitti
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2015-
|
18
|
+
date: 2015-08-04 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: money
|
@@ -241,6 +241,90 @@ dependencies:
|
|
241
241
|
- - "~>"
|
242
242
|
- !ruby/object:Gem::Version
|
243
243
|
version: 0.32.0
|
244
|
+
- !ruby/object:Gem::Dependency
|
245
|
+
name: pry
|
246
|
+
requirement: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
251
|
+
type: :development
|
252
|
+
prerelease: false
|
253
|
+
version_requirements: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - ">="
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '0'
|
258
|
+
- !ruby/object:Gem::Dependency
|
259
|
+
name: pry-doc
|
260
|
+
requirement: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - ">="
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '0'
|
265
|
+
type: :development
|
266
|
+
prerelease: false
|
267
|
+
version_requirements: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - ">="
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '0'
|
272
|
+
- !ruby/object:Gem::Dependency
|
273
|
+
name: pry-byebug
|
274
|
+
requirement: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - ">="
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '0'
|
279
|
+
type: :development
|
280
|
+
prerelease: false
|
281
|
+
version_requirements: !ruby/object:Gem::Requirement
|
282
|
+
requirements:
|
283
|
+
- - ">="
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: '0'
|
286
|
+
- !ruby/object:Gem::Dependency
|
287
|
+
name: pry-stack_explorer
|
288
|
+
requirement: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - ">="
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: '0'
|
293
|
+
type: :development
|
294
|
+
prerelease: false
|
295
|
+
version_requirements: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - ">="
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: '0'
|
300
|
+
- !ruby/object:Gem::Dependency
|
301
|
+
name: awesome_print
|
302
|
+
requirement: !ruby/object:Gem::Requirement
|
303
|
+
requirements:
|
304
|
+
- - ">="
|
305
|
+
- !ruby/object:Gem::Version
|
306
|
+
version: '0'
|
307
|
+
type: :development
|
308
|
+
prerelease: false
|
309
|
+
version_requirements: !ruby/object:Gem::Requirement
|
310
|
+
requirements:
|
311
|
+
- - ">="
|
312
|
+
- !ruby/object:Gem::Version
|
313
|
+
version: '0'
|
314
|
+
- !ruby/object:Gem::Dependency
|
315
|
+
name: ruby-prof
|
316
|
+
requirement: !ruby/object:Gem::Requirement
|
317
|
+
requirements:
|
318
|
+
- - ">="
|
319
|
+
- !ruby/object:Gem::Version
|
320
|
+
version: '0'
|
321
|
+
type: :development
|
322
|
+
prerelease: false
|
323
|
+
version_requirements: !ruby/object:Gem::Requirement
|
324
|
+
requirements:
|
325
|
+
- - ">="
|
326
|
+
- !ruby/object:Gem::Version
|
327
|
+
version: '0'
|
244
328
|
description:
|
245
329
|
email:
|
246
330
|
- anthony.sellitti@envato.com
|
@@ -275,6 +359,7 @@ files:
|
|
275
359
|
- lib/double_entry/configuration.rb
|
276
360
|
- lib/double_entry/errors.rb
|
277
361
|
- lib/double_entry/line.rb
|
362
|
+
- lib/double_entry/line_metadata.rb
|
278
363
|
- lib/double_entry/locking.rb
|
279
364
|
- lib/double_entry/reporting.rb
|
280
365
|
- lib/double_entry/reporting/aggregate.rb
|
@@ -282,6 +367,7 @@ files:
|
|
282
367
|
- lib/double_entry/reporting/day_range.rb
|
283
368
|
- lib/double_entry/reporting/hour_range.rb
|
284
369
|
- lib/double_entry/reporting/line_aggregate.rb
|
370
|
+
- lib/double_entry/reporting/line_aggregate_filter.rb
|
285
371
|
- lib/double_entry/reporting/month_range.rb
|
286
372
|
- lib/double_entry/reporting/time_range.rb
|
287
373
|
- lib/double_entry/reporting/time_range_array.rb
|
@@ -302,8 +388,11 @@ files:
|
|
302
388
|
- spec/double_entry/configuration_spec.rb
|
303
389
|
- spec/double_entry/line_spec.rb
|
304
390
|
- spec/double_entry/locking_spec.rb
|
391
|
+
- spec/double_entry/performance/double_entry_performance_spec.rb
|
392
|
+
- spec/double_entry/performance/reporting/aggregate_performance_spec.rb
|
305
393
|
- spec/double_entry/reporting/aggregate_array_spec.rb
|
306
394
|
- spec/double_entry/reporting/aggregate_spec.rb
|
395
|
+
- spec/double_entry/reporting/line_aggregate_filter_spec.rb
|
307
396
|
- spec/double_entry/reporting/line_aggregate_spec.rb
|
308
397
|
- spec/double_entry/reporting/month_range_spec.rb
|
309
398
|
- spec/double_entry/reporting/time_range_array_spec.rb
|
@@ -323,12 +412,38 @@ files:
|
|
323
412
|
- spec/support/gemfiles/Gemfile.rails-3.2.x
|
324
413
|
- spec/support/gemfiles/Gemfile.rails-4.1.x
|
325
414
|
- spec/support/gemfiles/Gemfile.rails-4.2.x
|
415
|
+
- spec/support/performance_helper.rb
|
326
416
|
- spec/support/reporting_configuration.rb
|
327
417
|
- spec/support/schema.rb
|
328
418
|
homepage: https://github.com/envato/double_entry
|
329
419
|
licenses: []
|
330
420
|
metadata: {}
|
331
|
-
post_install_message:
|
421
|
+
post_install_message: |
|
422
|
+
Please note the following changes in DoubleEntry:
|
423
|
+
- New table `double_entry_line_metadata` has been introduced and is *required* for
|
424
|
+
aggregate reporting filtering to work. Existing applications must manually manage
|
425
|
+
this change via a migration similar to the following:
|
426
|
+
|
427
|
+
class CreateDoubleEntryLineMetadata < ActiveRecord::Migration
|
428
|
+
def self.up
|
429
|
+
create_table "#{DoubleEntry.table_name_prefix}line_metadata", :force => true do |t|
|
430
|
+
t.integer "line_id", :null => false
|
431
|
+
t.string "key", :limit => 48, :null => false
|
432
|
+
t.string "value", :limit => 64, :null => false
|
433
|
+
t.timestamps :null => false
|
434
|
+
end
|
435
|
+
|
436
|
+
add_index "#{DoubleEntry.table_name_prefix}line_metadata",
|
437
|
+
["line_id", "key", "value"],
|
438
|
+
:name => "lines_meta_line_id_key_value_idx"
|
439
|
+
end
|
440
|
+
|
441
|
+
def self.down
|
442
|
+
drop_table "#{DoubleEntry.table_name_prefix}line_metadata"
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
Please ensure that you update your database accordingly.
|
332
447
|
rdoc_options: []
|
333
448
|
require_paths:
|
334
449
|
- lib
|
@@ -344,7 +459,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
344
459
|
version: '0'
|
345
460
|
requirements: []
|
346
461
|
rubyforge_project:
|
347
|
-
rubygems_version: 2.
|
462
|
+
rubygems_version: 2.2.2
|
348
463
|
signing_key:
|
349
464
|
specification_version: 4
|
350
465
|
summary: Tools to build your double entry financial ledger
|
@@ -356,8 +471,11 @@ test_files:
|
|
356
471
|
- spec/double_entry/configuration_spec.rb
|
357
472
|
- spec/double_entry/line_spec.rb
|
358
473
|
- spec/double_entry/locking_spec.rb
|
474
|
+
- spec/double_entry/performance/double_entry_performance_spec.rb
|
475
|
+
- spec/double_entry/performance/reporting/aggregate_performance_spec.rb
|
359
476
|
- spec/double_entry/reporting/aggregate_array_spec.rb
|
360
477
|
- spec/double_entry/reporting/aggregate_spec.rb
|
478
|
+
- spec/double_entry/reporting/line_aggregate_filter_spec.rb
|
361
479
|
- spec/double_entry/reporting/line_aggregate_spec.rb
|
362
480
|
- spec/double_entry/reporting/month_range_spec.rb
|
363
481
|
- spec/double_entry/reporting/time_range_array_spec.rb
|
@@ -377,5 +495,7 @@ test_files:
|
|
377
495
|
- spec/support/gemfiles/Gemfile.rails-3.2.x
|
378
496
|
- spec/support/gemfiles/Gemfile.rails-4.1.x
|
379
497
|
- spec/support/gemfiles/Gemfile.rails-4.2.x
|
498
|
+
- spec/support/performance_helper.rb
|
380
499
|
- spec/support/reporting_configuration.rb
|
381
500
|
- spec/support/schema.rb
|
501
|
+
has_rdoc:
|