ledger_web 1.4.2 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -9,3 +9,7 @@ end
9
9
  task :release => :build do
10
10
  system "gem push ledger_web-#{LedgerWeb::VERSION}.gem"
11
11
  end
12
+
13
+ task :test do
14
+ system 'rspec --color --format=documentation test'
15
+ end
data/bin/ledger_web CHANGED
@@ -15,17 +15,17 @@ OptionParser.new do |opts|
15
15
  opts.banner = "Usage: ledger_web [options]"
16
16
 
17
17
  opts.on("-p", "--port PORT", Integer, "Port to expose the web interface") do |p|
18
- CONFIG.set :port, p.to_i
18
+ LedgerWeb::Config.instance.set :port, p.to_i
19
19
  end
20
20
 
21
21
  opts.on("-f", "--ledger-file FILE", String, "Ledger file to watch and load") do |f|
22
- CONFIG.set :ledger_file, f
22
+ LedgerWeb::Config.instance.set :ledger_file, f
23
23
  end
24
24
 
25
25
  opts.on("-d", "--database-url URL", String, "Database URL to load into") do |d|
26
- CONFIG.set :database_url, d
26
+ LedgerWeb::Config.instance.set :database_url, d
27
27
  end
28
28
  end.parse!
29
29
 
30
30
  LedgerWeb::Watcher.run!
31
- LedgerWeb::Application.run!(:port => CONFIG.get(:port))
31
+ LedgerWeb::Application.run!(:port => LedgerWeb::Config.instance.get(:port))
data/ledger_web.gemspec CHANGED
@@ -20,9 +20,12 @@ Gem::Specification.new do |s|
20
20
  s.add_dependency("sinatra")
21
21
  s.add_dependency("sinatra-session")
22
22
  s.add_dependency("sinatra-contrib")
23
+ s.add_dependency("rspec")
24
+ s.add_dependency("database_cleaner")
23
25
 
24
26
  s.bindir = 'bin'
25
27
  s.files = `git ls-files`.split("\n")
28
+ s.test_files = `git ls-files -- test/*`.split("\n")
26
29
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
30
  s.require_paths = ["lib"]
28
31
  end
@@ -6,11 +6,13 @@ require 'sinatra/session'
6
6
  module LedgerWeb
7
7
  class Application < Sinatra::Base
8
8
  register Sinatra::Session
9
- set :session_secret, CONFIG.get(:session_secret)
10
- set :session_expire, CONFIG.get(:session_expire)
11
- set :views, CONFIG.get(:report_directories) + [File.join(File.dirname(__FILE__), 'views')]
9
+ set :session_secret, LedgerWeb::Config.instance.get(:session_secret)
10
+ set :session_expire, LedgerWeb::Config.instance.get(:session_expire)
11
+ set :views, LedgerWeb::Config.instance.get(:report_directories) + [File.join(File.dirname(__FILE__), 'views')]
12
12
  set :reload_templates, true
13
13
 
14
+ LedgerWeb::Database.connect
15
+
14
16
  helpers Sinatra::Capture
15
17
  helpers LedgerWeb::Helpers
16
18
 
@@ -55,7 +57,7 @@ module LedgerWeb
55
57
  end
56
58
 
57
59
  get '/' do
58
- index_report = CONFIG.get :index_report
60
+ index_report = LedgerWeb::Config.instance.get :index_report
59
61
  if index_report
60
62
  redirect "/reports/#{index_report.to_s}"
61
63
  else
@@ -3,6 +3,17 @@ module LedgerWeb
3
3
  class Config
4
4
  attr_reader :vars, :hooks
5
5
 
6
+ @@should_load_user_config = true
7
+ @@instance = nil
8
+
9
+ def self.should_load_user_config
10
+ @@should_load_user_config
11
+ end
12
+
13
+ def self.should_load_user_config=(val)
14
+ @@should_load_user_config = val
15
+ end
16
+
6
17
  def initialize
7
18
  @vars = {}
8
19
  @hooks = {}
@@ -55,40 +66,43 @@ module LedgerWeb
55
66
  return eval(file.read, nil, filename)
56
67
  end
57
68
  end
58
- end
59
- end
60
-
61
- CONFIG = LedgerWeb::Config.new do |config|
62
- config.set :database_url, "postgres://localhost/ledger"
63
- config.set :port, "9090"
64
- config.set :ledger_file, ENV['LEDGER_FILE']
65
- config.set :report_directories, ["#{File.dirname(__FILE__)}/reports"]
66
- config.set :session_secret, 'SomethingSecretThisWayPassed'
67
- config.set :session_expire, 60*60
68
- config.set :watch_interval, 5
69
- config.set :watch_stable_count, 3
70
- config.set :ledger_bin_path, "ledger"
71
69
 
72
- config.set :ledger_format, "%(quoted(xact.beg_line)),%(quoted(date)),%(quoted(payee)),%(quoted(account)),%(quoted(commodity)),%(quoted(quantity(scrub(display_amount)))),%(quoted(cleared)),%(quoted(virtual)),%(quoted(join(note | xact.note))),%(quoted(cost))\n"
73
70
 
74
- config.set :price_lookup_skip_symbols, ['$']
75
-
76
- func = Proc.new do |symbol, min_date, max_date|
77
- LedgerWeb::YahooPriceLookup.new(symbol, min_date, max_date).lookup
78
- end
79
- config.set :price_function, func
80
-
81
- ledger_web_dir = "#{ENV['HOME']}/.ledger_web"
82
-
83
- if File.directory? ledger_web_dir
84
- if File.directory? "#{ledger_web_dir}/reports"
85
- dirs = config.get(:report_directories)
86
- dirs.unshift "#{ledger_web_dir}/reports"
87
- config.set :report_directories, dirs
88
- end
89
-
90
- if File.exists? "#{ledger_web_dir}/config.rb"
91
- config.override_with(LedgerWeb::Config.from_file("#{ledger_web_dir}/config.rb"))
71
+ def self.instance
72
+ @@instance ||= LedgerWeb::Config.new do |config|
73
+ config.set :database_url, "postgres://localhost/ledger"
74
+ config.set :port, "9090"
75
+ config.set :ledger_file, ENV['LEDGER_FILE']
76
+ config.set :report_directories, ["#{File.dirname(__FILE__)}/reports"]
77
+ config.set :session_secret, 'SomethingSecretThisWayPassed'
78
+ config.set :session_expire, 60*60
79
+ config.set :watch_interval, 5
80
+ config.set :watch_stable_count, 3
81
+ config.set :ledger_bin_path, "ledger"
82
+
83
+ config.set :ledger_format, "%(quoted(xact.beg_line)),%(quoted(date)),%(quoted(payee)),%(quoted(account)),%(quoted(commodity)),%(quoted(quantity(scrub(display_amount)))),%(quoted(cleared)),%(quoted(virtual)),%(quoted(join(note | xact.note))),%(quoted(cost))\n"
84
+
85
+ config.set :price_lookup_skip_symbols, ['$']
86
+
87
+ func = Proc.new do |symbol, min_date, max_date|
88
+ LedgerWeb::YahooPriceLookup.new(symbol, min_date, max_date).lookup
89
+ end
90
+ config.set :price_function, func
91
+
92
+ ledger_web_dir = "#{ENV['HOME']}/.ledger_web"
93
+
94
+ if LedgerWeb::Config.should_load_user_config && File.directory?(ledger_web_dir)
95
+ if File.directory? "#{ledger_web_dir}/reports"
96
+ dirs = config.get(:report_directories)
97
+ dirs.unshift "#{ledger_web_dir}/reports"
98
+ config.set :report_directories, dirs
99
+ end
100
+
101
+ if File.exists? "#{ledger_web_dir}/config.rb"
102
+ config.override_with(LedgerWeb::Config.from_file("#{ledger_web_dir}/config.rb"))
103
+ end
104
+ end
105
+ end
92
106
  end
93
107
  end
94
108
  end
data/lib/ledger_web/db.rb CHANGED
@@ -1,59 +1,92 @@
1
1
  require 'sequel'
2
2
  require 'sequel/extensions/migration'
3
3
  require 'csv'
4
+ require 'tempfile'
4
5
 
5
- DB = Sequel.connect(CONFIG.get(:database_url))
6
+ module LedgerWeb
7
+ class Database
6
8
 
7
- Sequel::Migrator.apply(DB, File.join(File.dirname(__FILE__), "db/migrate"))
9
+ def self.connect
10
+ @@db = Sequel.connect(LedgerWeb::Config.instance.get(:database_url))
11
+ self.run_migrations()
12
+ end
8
13
 
9
- home_migrations = File.join(ENV['HOME'], '.ledger_web', 'migrate')
10
- if File.directory?(home_migrations)
11
- Sequel::Migrator.run(DB, home_migrations, :table => "user_schema_changes")
12
- end
14
+ def self.close
15
+ @@db.disconnect
16
+ end
13
17
 
14
- module LedgerWeb
15
- class Database
18
+ def self.handle
19
+ @@db
20
+ end
16
21
 
17
- def self.load_database
18
- ledger_format = CONFIG.get :ledger_format
19
- ledger_bin_path = CONFIG.get :ledger_bin_path
20
- ledger_file = CONFIG.get :ledger_file
21
-
22
- # dump ledger to tempfile
22
+ def self.run_migrations
23
+ Sequel::Migrator.apply(@@db, File.join(File.dirname(__FILE__), "db/migrate"))
24
+
25
+ home_migrations = File.join(ENV['HOME'], '.ledger_web', 'migrate')
26
+ if LedgerWeb::Config.should_load_user_config && File.directory?(home_migrations)
27
+ Sequel::Migrator.run(@@db, home_migrations, :table => "user_schema_changes")
28
+ end
29
+ end
30
+
31
+ def self.dump_ledger_to_csv
32
+ ledger_bin_path = LedgerWeb::Config.instance.get :ledger_bin_path
33
+ ledger_file = LedgerWeb::Config.instance.get :ledger_file
34
+ ledger_format = LedgerWeb::Config.instance.get :ledger_format
35
+
23
36
  print " dumping ledger to file...."
24
37
  file = Tempfile.new('ledger')
25
38
  system "#{ledger_bin_path} -f #{ledger_file} --format='#{ledger_format}' reg > #{file.path}"
39
+ replaced_file = Tempfile.new('ledger')
40
+ replaced_file.write(file.read.gsub('\"', '""'))
41
+ replaced_file.flush
42
+
26
43
  puts "done"
44
+ return replaced_file
45
+ end
46
+
47
+ def self.load_database(file)
27
48
  counter = 0
28
- DB.transaction do
49
+ @@db.transaction do
29
50
 
30
- CONFIG.run_hooks(:before_load, DB)
51
+ LedgerWeb::Config.instance.run_hooks(:before_load, @@db)
31
52
 
32
53
  print " clearing ledger table...."
33
- DB["DELETE FROM ledger"].delete
54
+ @@db["DELETE FROM ledger"].delete
34
55
  puts "done"
35
56
 
36
57
  print " loading into database...."
58
+
37
59
  CSV.foreach(file.path) do |row|
38
60
  counter += 1
39
- row = Hash[*[:xtn_id, :xtn_date, :note, :account, :commodity, :amount, :cleared, :virtual, :tags, :cost].zip(row).flatten]
40
-
61
+ row = Hash[*[
62
+ :xtn_id,
63
+ :xtn_date,
64
+ :note,
65
+ :account,
66
+ :commodity,
67
+ :amount,
68
+ :cleared,
69
+ :virtual,
70
+ :tags,
71
+ :cost
72
+ ].zip(row).flatten]
73
+
41
74
  xtn_date = Date.strptime(row[:xtn_date], '%Y/%m/%d')
42
75
 
43
76
  row[:xtn_month] = xtn_date.strftime('%Y/%m/01')
44
77
  row[:xtn_year] = xtn_date.strftime('%Y/01/01')
45
78
  row[:cost] = row[:cost].gsub(/[^\d\.-]/, '')
46
79
 
47
- row = CONFIG.run_hooks(:before_insert_row, row)
48
- DB[:ledger].insert(row)
49
- CONFIG.run_hooks(:after_insert_row, row)
80
+ row = LedgerWeb::Config.instance.run_hooks(:before_insert_row, row)
81
+ @@db[:ledger].insert(row)
82
+ LedgerWeb::Config.instance.run_hooks(:after_insert_row, row)
50
83
  end
51
84
 
52
85
  puts " Running after_load hooks"
53
- CONFIG.run_hooks(:after_load, DB)
86
+ LedgerWeb::Config.instance.run_hooks(:after_load, @@db)
54
87
  end
55
88
  puts " analyzing ledger table"
56
- DB.fetch('VACUUM ANALYZE ledger').all
89
+ @@db.fetch('VACUUM ANALYZE ledger').all
57
90
  puts "done"
58
91
  counter
59
92
  end
@@ -81,11 +114,11 @@ module LedgerWeb
81
114
  HERE
82
115
 
83
116
  puts "Deleting prices"
84
- DB["DELETE FROM prices"].delete
117
+ @@db["DELETE FROM prices"].delete
85
118
 
86
- rows = DB.fetch(query)
87
- proc = CONFIG.get :price_function
88
- skip = CONFIG.get :price_lookup_skip_symbols
119
+ rows = @@db.fetch(query)
120
+ proc = LedgerWeb::Config.instance.get :price_function
121
+ skip = LedgerWeb::Config.instance.get :price_lookup_skip_symbols
89
122
 
90
123
  puts "Loading prices"
91
124
  rows.each do |row|
@@ -95,11 +128,12 @@ HERE
95
128
 
96
129
  prices = proc.call(row[:commodity], row[:min_date], row[:max_date])
97
130
  prices.each do |price|
98
- DB[:prices].insert(:commodity => row[:commodity], :price_date => price[0], :price => price[1])
131
+ @@db[:prices].insert(:commodity => row[:commodity], :price_date => price[0], :price => price[1])
99
132
  end
100
133
  end
101
- DB.fetch("analyze prices").all
134
+ @@db.fetch("analyze prices").all
102
135
  puts "Done loading prices"
103
136
  end
104
137
  end
105
138
  end
139
+
@@ -8,6 +8,12 @@ module LedgerWeb
8
8
  @value_type = value_type
9
9
  @span_class = span_class
10
10
  end
11
+
12
+ def ==(other)
13
+ self.title == other.title && \
14
+ self.value_type == other.value_type && \
15
+ self.span_class == other.span_class
16
+ end
11
17
  end
12
18
 
13
19
  class Value
@@ -28,7 +34,10 @@ module LedgerWeb
28
34
 
29
35
  class Report
30
36
 
31
- attr_accessor :error, :fields
37
+ attr_accessor :error, :fields, :rows
38
+
39
+ @@session = {}
40
+ @@params = {}
32
41
 
33
42
  def self.session=(session)
34
43
  @@session = session
@@ -56,7 +65,7 @@ module LedgerWeb
56
65
  params[key.to_sym] = val
57
66
  end
58
67
 
59
- ds = DB.fetch(query, params)
68
+ ds = LedgerWeb::Database.handle.fetch(query, params)
60
69
  report = self.new
61
70
  begin
62
71
  row = ds.first
@@ -1,3 +1,3 @@
1
1
  module LedgerWeb
2
- VERSION = '1.4.2'
2
+ VERSION = '1.4.3'
3
3
  end
@@ -3,23 +3,26 @@ require 'directory_watcher'
3
3
  module LedgerWeb
4
4
  class Watcher
5
5
  def self.run!
6
- directory = CONFIG.get :watch_directory
6
+ directory = LedgerWeb::Config.instance.get :watch_directory
7
7
  glob = "*"
8
8
 
9
9
  if directory.nil?
10
- directory = File.dirname(CONFIG.get :ledger_file)
11
- glob = File.basename(CONFIG.get :ledger_file)
10
+ directory = File.dirname(LedgerWeb::Config.instance.get :ledger_file)
11
+ glob = File.basename(LedgerWeb::Config.instance.get :ledger_file)
12
12
  end
13
13
 
14
14
  @@dw = DirectoryWatcher.new directory, :glob => glob
15
- @@dw.interval = CONFIG.get :watch_interval
16
- @@dw.stable = CONFIG.get :watch_stable_count
15
+ @@dw.interval = LedgerWeb::Config.instance.get :watch_interval
16
+ @@dw.stable = LedgerWeb::Config.instance.get :watch_stable_count
17
17
 
18
18
  @@dw.add_observer do |*args|
19
19
  args.each do |event|
20
20
  if event[0] == :stable
21
21
  puts "Loading database"
22
- count = LedgerWeb::Database.load_database
22
+ LedgerWeb::Database.connect
23
+ LedgerWeb::Database.run_migrations
24
+ file = LedgerWeb::Database.dump_ledger_to_csv
25
+ count = LedgerWeb::Database.load_database(file)
23
26
  puts "Loaded #{count} records"
24
27
  end
25
28
  end
@@ -0,0 +1,65 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+ require 'ledger_web/config'
3
+
4
+ describe LedgerWeb::Config do
5
+ describe "#initialize" do
6
+
7
+ it "should get and set simple values" do
8
+ conf = LedgerWeb::Config.new do |config|
9
+ config.set :key_one, "value one"
10
+ config.set :key_two, "value two"
11
+ end
12
+
13
+ conf.get(:key_one).should eq("value one")
14
+ conf.get(:key_two).should eq("value two")
15
+ end
16
+
17
+ it "should get and run simple hooks" do
18
+ conf = LedgerWeb::Config.new do |config|
19
+ config.add_hook :sample do |val|
20
+ val[:foo] = val[:foo] * 2
21
+ end
22
+ end
23
+
24
+ test_val = { :foo => 2 }
25
+ conf.run_hooks(:sample, test_val)
26
+ test_val[:foo].should eq(4)
27
+ end
28
+ end
29
+
30
+ describe "#override_with" do
31
+ it "should override keys" do
32
+ conf_one = LedgerWeb::Config.new do |config|
33
+ config.set :key_one, "value one"
34
+ end
35
+
36
+ conf_two = LedgerWeb::Config.new do |config|
37
+ config.set :key_one, "value two"
38
+ end
39
+
40
+ conf_one.override_with(conf_two)
41
+
42
+ conf_one.get(:key_one).should eq("value two")
43
+ end
44
+
45
+ it "should append hooks" do
46
+ conf_one = LedgerWeb::Config.new do |config|
47
+ config.add_hook(:sample) do |val|
48
+ val[:list] << 'one'
49
+ end
50
+ end
51
+
52
+ conf_two = LedgerWeb::Config.new do |config|
53
+ config.add_hook(:sample) do |val|
54
+ val[:list] << 'two'
55
+ end
56
+ end
57
+
58
+ conf_one.override_with(conf_two)
59
+
60
+ test_val = {:list => []}
61
+ conf_one.run_hooks(:sample, test_val)
62
+ test_val[:list].should eq(['one', 'two'])
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,222 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+ require 'ledger_web/db'
3
+ require 'ledger_web/config'
4
+ require 'csv'
5
+
6
+ describe LedgerWeb::Database do
7
+ describe "#dump_ledger_to_csv" do
8
+ it "should not die" do
9
+ set_config :ledger_file, fixture('small')
10
+ file = LedgerWeb::Database.dump_ledger_to_csv
11
+ end
12
+
13
+ it "should dump valid csv" do
14
+ set_config :ledger_file, fixture('small')
15
+ file = LedgerWeb::Database.dump_ledger_to_csv
16
+
17
+ rows = CSV.read(file.path)
18
+
19
+ rows.should eq([
20
+ ["1", "2012/01/01", "Transaction One", "Assets:Savings", "$", "100", "true", "false", "", "$100.00"],
21
+ ["1", "2012/01/01", "Transaction One", "Assets:Checking", "$", "200", "true", "false", "", "$200.00"],
22
+ ["1", "2012/01/01", "Transaction One", "Equity:Opening Balances", "$", "-300", "true", "false", "", "$-300.00"],
23
+ ["6", "2012/01/02", "Lunch", "Expenses:Lunch", "$", "10", "true", "false", "", "$10.00"],
24
+ ["6", "2012/01/02", "Lunch", "Assets:Checking", "$", "-10", "true", "false", "", "$-10.00"]
25
+ ])
26
+ end
27
+
28
+ it "should dump valid csv even with quoted commodities" do
29
+ set_config :ledger_file, fixture('quoted')
30
+ file = LedgerWeb::Database.dump_ledger_to_csv
31
+
32
+ rows = CSV.read(file.path)
33
+
34
+ rows.should eq([
35
+ ["1", "2012/01/01", "Transaction One", "Assets:Savings", "\"Foo 123\"", "100", "true", "false", "", "100.00 \"Foo 123\""],
36
+ ["1", "2012/01/01", "Transaction One", "Assets:Checking", "\"Foo 123\"", "200", "true", "false", "", "200.00 \"Foo 123\""],
37
+ ["1", "2012/01/01", "Transaction One", "Equity:Opening Balances", "\"Foo 123\"", "-300", "true", "false", "", "-300.00 \"Foo 123\""],
38
+ ["6", "2012/01/02", "Lunch", "Expenses:Lunch", "\"Foo 123\"", "10", "true", "false", "", "10.00 \"Foo 123\""],
39
+ ["6", "2012/01/02", "Lunch", "Assets:Checking", "\"Foo 123\"", "-10", "true", "false", "", "-10.00 \"Foo 123\""]
40
+ ])
41
+ end
42
+
43
+ end
44
+
45
+ describe "#load_database" do
46
+ it "should load the database from a csv file" do
47
+ set_config :ledger_file, fixture('small')
48
+ file = LedgerWeb::Database.dump_ledger_to_csv
49
+ count = LedgerWeb::Database.load_database(file)
50
+ count.should eq(5)
51
+
52
+ LedgerWeb::Database.handle[:ledger].count().should eq(5)
53
+
54
+ convert_bd_to_string(LedgerWeb::Database.handle[:ledger].all()).should eq([
55
+ {
56
+ :xtn_date => Date.new(2012,1,1),
57
+ :checknum => nil,
58
+ :note => 'Transaction One',
59
+ :account => 'Assets:Savings',
60
+ :commodity => '$',
61
+ :amount => "100.0",
62
+ :tags => '',
63
+ :xtn_month => Date.new(2012,1,1),
64
+ :xtn_year => Date.new(2012,1,1),
65
+ :virtual => false,
66
+ :xtn_id => 1,
67
+ :cleared => true,
68
+ :cost => "100.0"
69
+ },
70
+ {
71
+ :xtn_date => Date.new(2012,1,1),
72
+ :checknum => nil,
73
+ :note => 'Transaction One',
74
+ :account => 'Assets:Checking',
75
+ :commodity => '$',
76
+ :amount => "200.0",
77
+ :tags => '',
78
+ :xtn_month => Date.new(2012,1,1),
79
+ :xtn_year => Date.new(2012,1,1),
80
+ :virtual => false,
81
+ :xtn_id => 1,
82
+ :cleared => true,
83
+ :cost => "200.0"
84
+ },
85
+ {
86
+ :xtn_date => Date.new(2012,1,1),
87
+ :checknum => nil,
88
+ :note => 'Transaction One',
89
+ :account => 'Equity:Opening Balances',
90
+ :commodity => '$',
91
+ :amount => "-300.0",
92
+ :tags => '',
93
+ :xtn_month => Date.new(2012,1,1),
94
+ :xtn_year => Date.new(2012,1,1),
95
+ :virtual => false,
96
+ :xtn_id => 1,
97
+ :cleared => true,
98
+ :cost => "-300.0"
99
+ },
100
+ {
101
+ :xtn_date => Date.new(2012,1,2),
102
+ :checknum => nil,
103
+ :note => 'Lunch',
104
+ :account => 'Expenses:Lunch',
105
+ :commodity => '$',
106
+ :amount => "10.0",
107
+ :tags => '',
108
+ :xtn_month => Date.new(2012,1,1),
109
+ :xtn_year => Date.new(2012,1,1),
110
+ :virtual => false,
111
+ :xtn_id => 6,
112
+ :cleared => true,
113
+ :cost => "10.0"
114
+ },
115
+ {
116
+ :xtn_date => Date.new(2012,1,2),
117
+ :checknum => nil,
118
+ :note => 'Lunch',
119
+ :account => 'Assets:Checking',
120
+ :commodity => '$',
121
+ :amount => "-10.0",
122
+ :tags => '',
123
+ :xtn_month => Date.new(2012,1,1),
124
+ :xtn_year => Date.new(2012,1,1),
125
+ :virtual => false,
126
+ :xtn_id => 6,
127
+ :cleared => true,
128
+ :cost => "-10.0"
129
+ },
130
+ ])
131
+
132
+ end
133
+
134
+ it "should load the database from a csv file containing quoted things" do
135
+ set_config :ledger_file, fixture('quoted')
136
+ file = LedgerWeb::Database.dump_ledger_to_csv
137
+ count = LedgerWeb::Database.load_database(file)
138
+ count.should eq(5)
139
+
140
+ LedgerWeb::Database.handle[:ledger].count().should eq(5)
141
+
142
+ convert_bd_to_string(LedgerWeb::Database.handle[:ledger].all()).should eq([
143
+ {
144
+ :xtn_date => Date.new(2012,1,1),
145
+ :checknum => nil,
146
+ :note => 'Transaction One',
147
+ :account => 'Assets:Savings',
148
+ :commodity => '"Foo 123"',
149
+ :amount => "100.0",
150
+ :tags => '',
151
+ :xtn_month => Date.new(2012,1,1),
152
+ :xtn_year => Date.new(2012,1,1),
153
+ :virtual => false,
154
+ :xtn_id => 1,
155
+ :cleared => true,
156
+ :cost => "100.0"
157
+ },
158
+ {
159
+ :xtn_date => Date.new(2012,1,1),
160
+ :checknum => nil,
161
+ :note => 'Transaction One',
162
+ :account => 'Assets:Checking',
163
+ :commodity => '"Foo 123"',
164
+ :amount => "200.0",
165
+ :tags => '',
166
+ :xtn_month => Date.new(2012,1,1),
167
+ :xtn_year => Date.new(2012,1,1),
168
+ :virtual => false,
169
+ :xtn_id => 1,
170
+ :cleared => true,
171
+ :cost => "200.0"
172
+ },
173
+ {
174
+ :xtn_date => Date.new(2012,1,1),
175
+ :checknum => nil,
176
+ :note => 'Transaction One',
177
+ :account => 'Equity:Opening Balances',
178
+ :commodity => '"Foo 123"',
179
+ :amount => "-300.0",
180
+ :tags => '',
181
+ :xtn_month => Date.new(2012,1,1),
182
+ :xtn_year => Date.new(2012,1,1),
183
+ :virtual => false,
184
+ :xtn_id => 1,
185
+ :cleared => true,
186
+ :cost => "-300.0"
187
+ },
188
+ {
189
+ :xtn_date => Date.new(2012,1,2),
190
+ :checknum => nil,
191
+ :note => 'Lunch',
192
+ :account => 'Expenses:Lunch',
193
+ :commodity => '"Foo 123"',
194
+ :amount => "10.0",
195
+ :tags => '',
196
+ :xtn_month => Date.new(2012,1,1),
197
+ :xtn_year => Date.new(2012,1,1),
198
+ :virtual => false,
199
+ :xtn_id => 6,
200
+ :cleared => true,
201
+ :cost => "10.0"
202
+ },
203
+ {
204
+ :xtn_date => Date.new(2012,1,2),
205
+ :checknum => nil,
206
+ :note => 'Lunch',
207
+ :account => 'Assets:Checking',
208
+ :commodity => '"Foo 123"',
209
+ :amount => "-10.0",
210
+ :tags => '',
211
+ :xtn_month => Date.new(2012,1,1),
212
+ :xtn_year => Date.new(2012,1,1),
213
+ :virtual => false,
214
+ :xtn_id => 6,
215
+ :cleared => true,
216
+ :cost => "-10.0"
217
+ },
218
+ ])
219
+
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,8 @@
1
+ 2012/01/01 * Transaction One
2
+ Assets:Savings 100.00 "Foo 123"
3
+ Assets:Checking 200.00 "Foo 123"
4
+ Equity:Opening Balances
5
+
6
+ 2012/01/02 * Lunch
7
+ Expenses:Lunch 10.00 "Foo 123"
8
+ Assets:Checking
@@ -0,0 +1,8 @@
1
+ 2012/01/01 * Transaction One
2
+ Assets:Savings $100.00
3
+ Assets:Checking $200.00
4
+ Equity:Opening Balances
5
+
6
+ 2012/01/02 * Lunch
7
+ Expenses:Lunch $10.00
8
+ Assets:Checking
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
2
+ require 'ledger_web/report'
3
+
4
+ describe LedgerWeb::Report do
5
+ describe "#from_query" do
6
+ it "should run the query" do
7
+
8
+ LedgerWeb::Report.session = {:from => '2012/01/01', :to => '2012/01/01'}
9
+
10
+ load_fixture('small')
11
+
12
+ report = LedgerWeb::Report.from_query("select count(1) as foo from ledger")
13
+ rows = []
14
+ report.each_row do |row|
15
+ rows << row
16
+ end
17
+
18
+ rows[0][0][0].should eq(5)
19
+ rows[0][0][1].should eq(LedgerWeb::Field.new('foo', 'number', 'pull-right'))
20
+ end
21
+ end
22
+
23
+ describe "#pivot" do
24
+ it "should create the correct fields" do
25
+ LedgerWeb::Report.session = {:from => '2012/01/01', :to => '2012/01/01'}
26
+ load_fixture('small')
27
+
28
+ report = LedgerWeb::Report.from_query("select xtn_month, account, sum(amount) from ledger group by xtn_month, account")
29
+ report = report.pivot("account", "asc")
30
+
31
+ report.fields.should eq([
32
+ string_field('xtn_month'),
33
+ number_field('Assets:Checking'),
34
+ number_field('Assets:Savings'),
35
+ number_field('Equity:Opening Balances'),
36
+ number_field('Expenses:Lunch')
37
+ ])
38
+
39
+ end
40
+
41
+ it "should put the values in the right place" do
42
+ LedgerWeb::Report.session = {:from => '2012/01/01', :to => '2012/01/01'}
43
+ load_fixture('small')
44
+
45
+ report = LedgerWeb::Report.from_query("select xtn_month, account, sum(amount)::integer from ledger group by xtn_month, account")
46
+ report = report.pivot("account", "asc")
47
+
48
+ report.rows.should eq([
49
+ [Date.new(2012,1,1), 190, 100, -300, 10]
50
+ ])
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,73 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
2
+
3
+ require 'rspec'
4
+ require 'ledger_web/config'
5
+ require 'ledger_web/db'
6
+ require 'ledger_web/report'
7
+ require 'database_cleaner'
8
+
9
+ RSpec.configure do |config|
10
+
11
+ config.before(:suite) do
12
+
13
+ system 'createdb ledger-test'
14
+ LedgerWeb::Config.should_load_user_config = false
15
+ LedgerWeb::Config.instance.set :database_url, 'postgres://localhost/ledger-test'
16
+ LedgerWeb::Database.connect
17
+ LedgerWeb::Database.run_migrations
18
+
19
+ DatabaseCleaner.strategy = :truncation
20
+ DatabaseCleaner.clean_with(:truncation)
21
+ end
22
+
23
+ config.before(:each) do
24
+ DatabaseCleaner.start
25
+ end
26
+
27
+ config.after(:each) do
28
+ DatabaseCleaner.clean
29
+ end
30
+
31
+ config.after(:suite) do
32
+ LedgerWeb::Database.close
33
+ system 'dropdb ledger-test'
34
+ end
35
+
36
+ end
37
+
38
+ def set_config(key, val)
39
+ LedgerWeb::Config.instance.set key, val
40
+ end
41
+
42
+ def fixture(name)
43
+ File.join(File.dirname(__FILE__), "fixtures", name + ".dat")
44
+ end
45
+
46
+ def convert_bd_to_string(objs)
47
+ objs.map do |obj|
48
+ obj.each do |k,v|
49
+ if v.is_a? BigDecimal
50
+ obj[k] = v.truncate(2).to_s('F')
51
+ end
52
+ end
53
+ obj
54
+ end
55
+ end
56
+
57
+ def load_fixture(name)
58
+ set_config :ledger_file, fixture(name)
59
+ file = LedgerWeb::Database.dump_ledger_to_csv
60
+ LedgerWeb::Database.load_database(file)
61
+ end
62
+
63
+ def field(name, type, css_class)
64
+ LedgerWeb::Field.new(name, type, css_class)
65
+ end
66
+
67
+ def string_field(name)
68
+ field(name, 'string', 'pull-left')
69
+ end
70
+
71
+ def number_field(name)
72
+ field(name, 'number', 'pull-right')
73
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ledger_web
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 1.4.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-17 00:00:00.000000000Z
12
+ date: 2012-03-18 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pg
16
- requirement: &2152703100 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2152703100
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: sequel
27
- requirement: &2152702540 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *2152702540
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: directory_watcher
38
- requirement: &2152701980 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *2152701980
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rack
49
- requirement: &2152701140 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,10 +69,15 @@ dependencies:
54
69
  version: 1.3.6
55
70
  type: :runtime
56
71
  prerelease: false
57
- version_requirements: *2152701140
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.3.6
58
78
  - !ruby/object:Gem::Dependency
59
79
  name: sinatra
60
- requirement: &2152700700 !ruby/object:Gem::Requirement
80
+ requirement: !ruby/object:Gem::Requirement
61
81
  none: false
62
82
  requirements:
63
83
  - - ! '>='
@@ -65,10 +85,15 @@ dependencies:
65
85
  version: '0'
66
86
  type: :runtime
67
87
  prerelease: false
68
- version_requirements: *2152700700
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
69
94
  - !ruby/object:Gem::Dependency
70
95
  name: sinatra-session
71
- requirement: &2152699580 !ruby/object:Gem::Requirement
96
+ requirement: !ruby/object:Gem::Requirement
72
97
  none: false
73
98
  requirements:
74
99
  - - ! '>='
@@ -76,10 +101,15 @@ dependencies:
76
101
  version: '0'
77
102
  type: :runtime
78
103
  prerelease: false
79
- version_requirements: *2152699580
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
80
110
  - !ruby/object:Gem::Dependency
81
111
  name: sinatra-contrib
82
- requirement: &2152699100 !ruby/object:Gem::Requirement
112
+ requirement: !ruby/object:Gem::Requirement
83
113
  none: false
84
114
  requirements:
85
115
  - - ! '>='
@@ -87,7 +117,44 @@ dependencies:
87
117
  version: '0'
88
118
  type: :runtime
89
119
  prerelease: false
90
- version_requirements: *2152699100
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: database_cleaner
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
91
158
  description: Allows arbitrary reporting on your ledger using easy-to-write SQL queries
92
159
  email:
93
160
  - pete@bugsplat.info
@@ -228,6 +295,12 @@ files:
228
295
  - lib/ledger_web/views/table.erb
229
296
  - lib/ledger_web/views/visualization.erb
230
297
  - lib/ledger_web/watcher.rb
298
+ - test/config_spec.rb
299
+ - test/database_load_spec.rb
300
+ - test/fixtures/quoted.dat
301
+ - test/fixtures/small.dat
302
+ - test/report_spec.rb
303
+ - test/spec_helper.rb
231
304
  homepage: https://github.com/peterkeen/ledger-web
232
305
  licenses: []
233
306
  post_install_message:
@@ -248,9 +321,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
248
321
  version: '0'
249
322
  requirements: []
250
323
  rubyforge_project:
251
- rubygems_version: 1.8.13
324
+ rubygems_version: 1.8.19
252
325
  signing_key:
253
326
  specification_version: 3
254
327
  summary: A web-based, sql-backed front-end for the Ledger command-line accounting
255
328
  system
256
- test_files: []
329
+ test_files:
330
+ - test/config_spec.rb
331
+ - test/database_load_spec.rb
332
+ - test/fixtures/quoted.dat
333
+ - test/fixtures/small.dat
334
+ - test/report_spec.rb
335
+ - test/spec_helper.rb