ydim 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env ruby
2
+ # RootSession -- ydim -- 10.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'drb'
5
+ require 'encoding/character/utf-8'
6
+ require 'ydim/autoinvoicer'
7
+ require 'ydim/debitor'
8
+ require 'ydim/invoice'
9
+ require 'ydim/item'
10
+ require 'ydim/mail'
11
+ require 'odba'
12
+
13
+ module YDIM
14
+ class RootSession
15
+ attr_accessor :serv, :client
16
+ def initialize(user)
17
+ @user = user
18
+ end
19
+ def add_items(invoice_id, items, invoice_key=:invoice)
20
+ @serv.logger.debug(whoami) {
21
+ size = (items.respond_to?(:size)) ? items.size : nil
22
+ "add_items(#{invoice_id}, #{items.class}[#{size}], #{invoice_key})" }
23
+ invoice = self.send(invoice_key, invoice_id)
24
+ rate = invoice.suppress_vat ? 0 : @serv.config.vat_rate
25
+ items.each { |data|
26
+ item = Item.new({:vat_rate => rate}.update(data))
27
+ invoice.add_item(item)
28
+ }
29
+ invoice.odba_store
30
+ invoice.items
31
+ end
32
+ def autoinvoice(invoice_id)
33
+ @serv.logger.debug(whoami) { "autoinvoice #{invoice_id}" }
34
+ AutoInvoice.find_by_unique_id(invoice_id.to_s) \
35
+ or begin
36
+ msg = "invalid invoice_id: #{invoice_id}"
37
+ @serv.logger.error(whoami) { msg }
38
+ raise IndexError, msg
39
+ end
40
+ end
41
+ def collect_garbage(debitor_id=nil)
42
+ @serv.logger.info(whoami) { "collect_garbage" }
43
+ deleted = []
44
+ Invoice.odba_extent { |inv|
45
+ if([nil, inv.debitor_id].include?(debitor_id) && inv.deleted)
46
+ deleted.push(inv.info)
47
+ inv.odba_delete
48
+ end
49
+ }
50
+ deleted unless(deleted.empty?)
51
+ end
52
+ def create_autoinvoice(debitor_id)
53
+ @serv.logger.debug(whoami) { "create_autoinvoice(#{debitor_id})" }
54
+ ODBA.transaction {
55
+ @serv.factory.create_autoinvoice(debitor(debitor_id))
56
+ }
57
+ end
58
+ def create_debitor
59
+ @serv.logger.info(whoami) { "create_debitor" }
60
+ ODBA.transaction {
61
+ id = @serv.id_server.next_id(:debitor)
62
+ Debitor.new(id).odba_store
63
+ }
64
+ end
65
+ def create_invoice(debitor_id)
66
+ @serv.logger.debug(whoami) { "create_invoice(#{debitor_id})" }
67
+ ODBA.transaction {
68
+ @serv.factory.create_invoice(debitor(debitor_id))
69
+ }
70
+ end
71
+ def currency_converter
72
+ @serv.logger.debug(whoami) { "currency_converter" }
73
+ @serv.currency_converter.drb_dup
74
+ end
75
+ def debitor(debitor_id)
76
+ @serv.logger.debug(whoami) { "debitor #{debitor_id}" }
77
+ Debitor.find_by_unique_id(debitor_id.to_s)\
78
+ or begin
79
+ msg = "invalid debitor_id: #{debitor_id}"
80
+ @serv.logger.error(whoami) { msg }
81
+ raise IndexError, msg
82
+ end
83
+ end
84
+ def debitors
85
+ @serv.logger.debug(whoami) { "debitors" }
86
+ Debitor.odba_extent
87
+ end
88
+ def delete_autoinvoice(invoice_id)
89
+ @serv.logger.debug(whoami) {
90
+ "delete_autoinvoice(#{invoice_id})" }
91
+ if(invoice = autoinvoice(invoice_id))
92
+ invoice.odba_delete
93
+ end
94
+ end
95
+ def delete_item(invoice_id, index, invoice_key=:invoice)
96
+ @serv.logger.debug(whoami) {
97
+ "delete_item(#{invoice_id}, #{index}, #{invoice_key})" }
98
+ invoice = self.send(invoice_key, invoice_id)
99
+ invoice.items.delete_if { |item| item.index == index }
100
+ invoice.odba_store
101
+ invoice.items
102
+ end
103
+ def generate_invoice(invoice_id)
104
+ @serv.logger.info(whoami) { "generate_invoice(#{invoice_id})" }
105
+ invoice = autoinvoice(invoice_id)
106
+ AutoInvoicer.new(@serv).generate(invoice)
107
+ end
108
+ def invoice(invoice_id)
109
+ @serv.logger.debug(whoami) { "invoice #{invoice_id}" }
110
+ Invoice.find_by_unique_id(invoice_id.to_s) \
111
+ or begin
112
+ msg = "invalid invoice_id: #{invoice_id}"
113
+ @serv.logger.error(whoami) { msg }
114
+ raise IndexError, msg
115
+ end
116
+ end
117
+ def invoice_infos(status=nil)
118
+ @serv.logger.debug(whoami) { "invoice_infos(#{status})" }
119
+ Invoice.search_by_status(status).collect { |inv| inv.info }
120
+ end
121
+ def search_debitors(email_or_name)
122
+ @serv.logger.debug(whoami) { "search_debitors(#{email_or_name})" }
123
+ Debitor.search_by_exact_email(email_or_name) |
124
+ Debitor.search_by_exact_name(email_or_name)
125
+ end
126
+ def send_invoice(invoice_id, sort_args={})
127
+ @serv.logger.info(whoami) { "send_invoice(#{invoice_id})" }
128
+ Mail.send_invoice(@serv.config, invoice(invoice_id), sort_args)
129
+ end
130
+ def update_item(invoice_id, index, data, invoice_key=:invoice)
131
+ @serv.logger.debug(whoami) {
132
+ "update_item(#{invoice_id}, #{index}, #{data.inspect})" }
133
+ invoice = self.send(invoice_key, invoice_id)
134
+ item = invoice.item(index)
135
+ item.update(data)
136
+ invoice.odba_store
137
+ item
138
+ end
139
+ def whoami
140
+ @user.unique_id.to_s
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # User -- ydim -- 10.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'rrba/user'
5
+ require 'odba/drbwrapper'
6
+ require 'ydim/root_session'
7
+
8
+ module YDIM
9
+ class RootUser < RRBA::User
10
+ def new_session
11
+ ODBA::DRbWrapper.new(RootSession.new(self))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,165 @@
1
+ #!/usr/bin/env ruby
2
+ # Server -- ydim -- 10.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'logger'
5
+ require 'needle'
6
+ require 'odba/id_server'
7
+ require 'rrba/server'
8
+ require 'ydim/autoinvoicer'
9
+ require 'ydim/client'
10
+ require 'ydim/currency_converter'
11
+ require 'ydim/currency_updater'
12
+ require 'ydim/factory'
13
+ require 'ydim/root_user'
14
+ require 'ydim/util'
15
+
16
+ module YDIM
17
+ class Server
18
+ ydim_default_dir = File.join(ENV['HOME'], '.ydim')
19
+ default_config_files = [
20
+ File.join(ydim_default_dir, 'ydimd.yml'),
21
+ '/etc/ydim/ydimd.yml',
22
+ ]
23
+ defaults = {
24
+ 'autoinvoice_hour' => 1,
25
+ 'config' => default_config_files,
26
+ 'conf_dir' => File.join(ydim_default_dir, 'conf'),
27
+ 'currencies' => ['CHF', 'EUR', 'USD'],
28
+ 'currency_update_hour' => 2,
29
+ 'data_dir' => File.join(ydim_default_dir, 'data'),
30
+ 'server_url' => 'druby://localhost:12375',
31
+ 'db_driver_url' => 'DBI:Pg:ydim',
32
+ 'db_user' => 'ydim',
33
+ 'db_auth' => '',
34
+ 'detach' => false,
35
+ 'home_country' => 'Schweiz',
36
+ 'invoice_number_start' => 10000,
37
+ 'log_level' => 'INFO',
38
+ 'log_file' => STDOUT,
39
+ 'mail_body' => "%s %s\n%s",
40
+ 'mail_charset' => 'iso-8859-1',
41
+ 'mail_from' => '',
42
+ 'mail_recipients' => [],
43
+ 'root_name' => 'Root',
44
+ 'root_email' => '',
45
+ 'root_key' => 'root_dsa',
46
+ 'salutation' => {
47
+ '' => 'Sehr geehrter Herr',
48
+ 'Herr' => 'Sehr geehrter Herr',
49
+ 'Frau' => 'Sehr geehrte Frau',
50
+ },
51
+ 'smtp_from' => '',
52
+ 'smtp_authtype' => :plain,
53
+ 'smtp_domain' => 'ywesee.com',
54
+ 'smtp_pass' => nil,
55
+ 'smtp_port' => 587,
56
+ 'smtp_server' => 'localhost',
57
+ 'smtp_user' => 'ydim@ywesee.com',
58
+ 'vat_rate' => 7.6,
59
+ }
60
+ config = RCLConf::RCLConf.new(ARGV, defaults)
61
+ config.load(config.config)
62
+ CONFIG = config
63
+ SECONDS_IN_DAY = 24*60*60
64
+ def Server.config
65
+ CONFIG
66
+ end
67
+ def initialize(config, logger)
68
+ @serv = Needle::Registry.new
69
+ @serv.register(:auth_server) {
70
+ auth = RRBA::Server.new
71
+ root = RootUser.new(:root)
72
+ root.name = config.root_name
73
+ root.email = config.root_email
74
+ root_key = config.root_key
75
+ path = File.expand_path(root_key, config.conf_dir)
76
+ path_or_key = File.exist?(path) ? path : root_key
77
+ root.public_key = Util.load_key(path_or_key)
78
+ auth.root = root
79
+ auth
80
+ }
81
+ @serv.register(:clients) {
82
+ ClientHandler.new(@serv)
83
+ }
84
+ @serv.register(:config) {
85
+ config
86
+ }
87
+ @serv.register(:currency_converter) {
88
+ ODBA.cache.fetch_named('currency_converter', self) {
89
+ CurrencyConverter.new
90
+ }
91
+ }
92
+ @serv.register(:factory) {
93
+ Factory.new(@serv)
94
+ }
95
+ @serv.register(:id_server) {
96
+ ODBA.cache.fetch_named('id_server', self) {
97
+ ODBA::IdServer.new
98
+ }
99
+ }
100
+ @serv.register(:logger) {
101
+ logger
102
+ }
103
+ if(hour = config.autoinvoice_hour)
104
+ @autoinvoicer = repeat_at(hour, 'AutoInvoicer') {
105
+ AutoInvoicer.new(@serv).run
106
+ }
107
+ end
108
+ if(hour = config.currency_update_hour)
109
+ if(@serv.currency_converter.known_currencies \
110
+ < @serv.config.currencies.size)
111
+ CurrencyUpdater.new(@serv).run
112
+ end
113
+ @currency_updater = repeat_at(hour, 'CurrencyUpdater') {
114
+ CurrencyUpdater.new(@serv).run
115
+ }
116
+ end
117
+ @status_updater = repeat_at(1, 'StatusUpdater') {
118
+ Invoice.all { |inv| inv.save }
119
+ }
120
+ @sessions = []
121
+ end
122
+ def login(client, name=nil, &block)
123
+ @serv.logger.debug(client.__drburi) { 'attempting login' }
124
+ session = @serv.auth_server.authenticate(name, &block)
125
+ session.serv = @serv
126
+ session.client = client
127
+ @serv.logger.info(session.whoami) { 'login' }
128
+ @sessions.push(session)
129
+ session
130
+ rescue Exception => error
131
+ @serv.logger.error('unknown user') {
132
+ [error.class, error.message].join(' - ') }
133
+ raise
134
+ end
135
+ def logout(session)
136
+ @serv.logger.info(session.whoami) { 'logout' }
137
+ @sessions.delete(session)
138
+ nil
139
+ end
140
+ def ping
141
+ true
142
+ end
143
+ private
144
+ def repeat_at(hour, thread_name)
145
+ Thread.new {
146
+ Thread.current.abort_on_exception = true
147
+ loop {
148
+ now = Time.now
149
+ next_run = Time.local(now.year, now.month, now.day, hour)
150
+ sleepy_time = next_run - now
151
+ if(sleepy_time < 0)
152
+ sleepy_time += SECONDS_IN_DAY
153
+ next_run += SECONDS_IN_DAY
154
+ end
155
+ @serv.logger.info(thread_name) {
156
+ sprintf("next run %s, sleeping %i seconds",
157
+ next_run.strftime("%c"), sleepy_time)
158
+ }
159
+ sleep(sleepy_time)
160
+ yield
161
+ }
162
+ }
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,58 @@
1
+ require "openssl"
2
+ require "net/smtp"
3
+
4
+ Net::SMTP.class_eval do
5
+ private
6
+ def do_start(helodomain, user, secret, authtype)
7
+ raise IOError, 'SMTP session already started' if @started
8
+ check_auth_args user, secret, authtype if user or secret
9
+
10
+ sock = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
11
+ @socket = Net::InternetMessageIO.new(sock)
12
+ @socket.read_timeout = 60 #@read_timeout
13
+ @socket.debug_output = STDERR #@debug_output
14
+
15
+ check_response(critical { recv_response() })
16
+ do_helo(helodomain)
17
+
18
+ raise 'openssl library not installed' unless defined?(OpenSSL)
19
+ starttls
20
+ ssl = OpenSSL::SSL::SSLSocket.new(sock)
21
+ ssl.sync_close = true
22
+ ssl.connect
23
+ @socket = Net::InternetMessageIO.new(ssl)
24
+ @socket.read_timeout = 60 #@read_timeout
25
+ @socket.debug_output = STDERR #@debug_output
26
+ do_helo(helodomain)
27
+
28
+ authenticate user, secret, authtype if user
29
+ @started = true
30
+ ensure
31
+ unless @started
32
+ # authentication failed, cancel connection.
33
+ @socket.close if not @started and @socket and not @socket.closed?
34
+ @socket = nil
35
+ end
36
+ end
37
+
38
+ def do_helo(helodomain)
39
+ begin
40
+ if @esmtp
41
+ ehlo helodomain
42
+ else
43
+ helo helodomain
44
+ end
45
+ rescue Net::ProtocolError
46
+ if @esmtp
47
+ @esmtp = false
48
+ @error_occured = false
49
+ retry
50
+ end
51
+ raise
52
+ end
53
+ end
54
+
55
+ def starttls
56
+ getok('STARTTLS')
57
+ end
58
+ end
data/lib/ydim/util.rb ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # Util -- ydim -- 10.01.2006 -- hwyss@ywesee.com
3
+
4
+ require 'openssl'
5
+ require 'fileutils'
6
+
7
+ module YDIM
8
+ module Util
9
+ def Util.load_key(key)
10
+ if(File.exist?(key))
11
+ key = File.read(key)
12
+ end
13
+ key = OpenSSL::PKey::DSA.new(key)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,58 @@
1
+ <html><head><meta HTTP-EQUIV="content-type" CONTENT="text/html; charset=UTF-8"><title>1 USD in CHF - Google Search</title><style><!--
2
+ body,td,div,.p,a{font-family:arial,sans-serif }
3
+ div,td{color:#000}
4
+ .f{color:#6f6f6f}
5
+ .fl:link{color:#77c}
6
+ a:link,.w,a.w:link,.w a:link{color:#00c}
7
+ a:visited,.fl:visited{color:#551a8b}
8
+ a:active,.fl:active{color:#f00}
9
+ .t a:link,.t a:active,.t a:visited,.t{color:#000}
10
+ .t{background-color:#e5ecf9}
11
+ .k{background-color:#36c}
12
+ .j{width:34em}
13
+ .h{color:#36c;font-size:14px}
14
+ .i,.i:link{color:#a90a08}
15
+ .a,.a:link{color:#008000}
16
+ .z{display:none}
17
+ div.n{margin-top:1ex}
18
+ .n a{font-size:10pt;color:#000}
19
+ .n .i{font-size:10pt;font-weight:bold}
20
+ .q:visited,.q:link,.q:active,.q{color:#00c;}
21
+ .b{font-size:12pt;color:#00c;font-weight:bold}
22
+ .ch{cursor:pointer;cursor:hand}
23
+ .sem{display:inline;margin:0;font-size:100%;font-weight:inherit}
24
+ .e{margin-top:.75em;margin-bottom:.75em}
25
+ .g{margin-top:1em;margin-bottom:1em}
26
+ .m{color:#666666;font-size:83%}
27
+ .ml:link{color:#551a8b;}
28
+ .sm{display:block;margin-top:0px;margin-bottom:0px;margin-left:40px}
29
+ .bl{display:none}
30
+ .fl2,.fl2:link,.fl2:visited{color:#7777CC}
31
+ .fl2:active{color:#f00}
32
+ .gh{background-color:#FFFF88;}
33
+ -->
34
+ </style>
35
+ <script>
36
+ <!--
37
+ if(document.images){new Image().src="/url?sa=Q&q=1+USD+in+CHF&ct=q&ei=I8_gQ4bMF9DqiwHQ9JHlCQ&sig2=UD3Tzz5it4Ox-8P_bqGNQQ";}
38
+ function ss(w,id){window.status=w;return true;}
39
+ function cs(){window.status='';}
40
+ function rwt(el,ct,cd,sg){el.href="/url?sa=t&ct="+escape(ct)+"&cd="+escape(cd)+"&url="+escape(el.href).replace(/\+/g,"%2B")+"&ei=I8_gQ4bMF9DqiwHQ9JHlCQ"+sg;el.onmousedown="";return true;}
41
+ var q="1+USD+in+CHF";var eid="I8_gQ4bMF9DqiwHQ9JHlCQ";document.write("<style type=text/css>.bl{ display:inline !important;}</style>");function _inv(){if(window.location.hash=="#invalid"){var a=window.location.href;window.location.replace(a.substring(0,a.indexOf("#")));if(window.location.hash=="#invalid"){window.location.hash=""}if(window.location.hash==""||window.location.hash=="#"){window.location.reload(true)}}}_inv();function ga(o,e) {if (document.getElementById) {var a = o.id.substring(1); var p = "", r = "", t, f, h;var g = e.target;if (g) { t = g.id;f = g.parentNode;if (f) {p = f.id;h = f.parentNode;if (h)r = h.id;}} else {h = e.srcElement;f = h.parentNode;if (f)p = f.id;t = h.id;}if (t==a || p==a || r==a)return true;document.getElementById(a).href += "&ct=bg";top.location.href=document.getElementById(a).href}}
42
+ //-->
43
+ </script>
44
+ </head><body bgcolor=#ffffff onload="document.gs.reset();" topmargin=3 marginheight=3><table border=0 cellspacing=0 cellpadding=0 width=100%><tr><td align=right nowrap><font size=-1><b>hannes.wyss@gmail.com</b>&nbsp;|&nbsp;<a href="/searchhistory/?hl=en">Search History</a>&nbsp;|&nbsp;<a href="https://www.google.com/accounts/ManageAccount">My Account</a>&nbsp;|&nbsp;<a href="http://www.google.com/accounts/Logout?continue=http://www.google.com/search%3Fq%3D1%2BUSD%2Bin%2BCHF">Sign out</a></font></td></tr><tr height=4><td><img alt="" width=1 height=1></td></tr></table><table border=0 cellpadding=0 cellspacing=0 width=100%><tr><form name=gs method=GET action=/search><td valign=top><a href="http://www.google.com/webhp?hl=en"><img src="/images/logo_sm.gif" width=150 height=55 alt="Go to Google Home" border=0 vspace=12></a></td><td>&nbsp;&nbsp;</td><td valign=top width=100%><table cellpadding=0 cellspacing=0 border=0><tr><td height=14 valign=bottom><script><!--
45
+ function qs(el) {if (window.RegExp && window.encodeURIComponent) {var ue=el.href;var qe=encodeURIComponent(document.gs.q.value);if(ue.indexOf("q=")!=-1){el.href=ue.replace(new RegExp("q=[^&$]*"),"q="+qe);}else{el.href=ue+"&q="+qe;}}return 1;}
46
+ // -->
47
+ </script><table border=0 cellpadding=4 cellspacing=0><tr><td><font size=-1><b>Web</b>&nbsp;&nbsp;&nbsp;&nbsp;<a id=t1a class=q href="http://images.google.com/images?q=1+USD+in+CHF&sa=N&tab=wi" onClick="return qs(this);">Images</a>&nbsp;&nbsp;&nbsp;&nbsp;<a id=t2a class=q href="http://groups.google.com/groups?q=1+USD+in+CHF&sa=N&tab=wg" onClick="return qs(this);">Groups</a>&nbsp;&nbsp;&nbsp;&nbsp;<a id=t4a class=q href="http://news.google.com/news?q=1+USD+in+CHF&sa=N&tab=wn" onClick="return qs(this);">News</a>&nbsp;&nbsp;&nbsp;&nbsp;<a id=t5a class=q href="http://froogle.google.com/froogle?q=1+USD+in+CHF&sa=N&tab=wf" onClick="return qs(this);">Froogle</a>&nbsp;&nbsp;&nbsp;&nbsp;<a id=t7a class=q href="http://local.google.com/local?q=1+USD+in+CHF&sa=N&tab=wl" onClick="return qs(this);">Local</a>&nbsp;&nbsp;&nbsp;&nbsp;<b><a href="/intl/en/options/" class=q>more&nbsp;&raquo;</a></b></font></td></tr></table></td></tr><tr><td><table border=0 cellpadding=0 cellspacing=0><tr><td nowrap><input type=hidden name=hl value="en"><input type=hidden name=lr value=""><input type=hidden name=c2coff value=1><input type=text name=q size=41 maxlength=2048 value="1 USD in CHF" title="Search"><font size=-1> <input type=submit name="btnG" value="Search"><span id=hf></span></font></td><td nowrap><font size=-2>&nbsp;&nbsp;<a href=/advanced_search?q=1+USD+in+CHF&hl=en&lr=&c2coff=1>Advanced Search</a><br>&nbsp;&nbsp;<a href=/preferences?q=1+USD+in+CHF&hl=en&lr=&c2coff=1>Preferences</a>&nbsp;&nbsp;&nbsp;&nbsp;</font></td></tr></table></td></tr></table><table cellpadding=0 cellspacing=0 border=0><tr><td><font size=-1></font></td></tr><tr><td height=7><img width=1 height=1 alt=""></td></tr></table></td></form></tr></table><table width=100% border=0 cellpadding=0 cellspacing=0><tr><td bgcolor=#3366cc><img width=1 height=1 alt=""></td></tr></table><table width=100% border=0 cellpadding=0 cellspacing=0 bgcolor=#e5ecf9><tr><td bgcolor=#e5ecf9 width=1% nowrap><font size=+1>&nbsp;<b>Web</b></font>&nbsp;</td></tr></table>
48
+ <script><!--
49
+ var tt = document.gs.q;
50
+ tt.focus();
51
+ var r = tt.createTextRange;
52
+ if (r) {
53
+ var s = r();
54
+ s.collapse(false);
55
+ s.select(); }
56
+ // -->
57
+ </script>
58
+ <p><table><tr><td><img src=/images/calc_img.gif></td><td>&nbsp;</td><td nowrap><font size=+1><b>1 U.S. dollar = 1.2864003 Swiss francs</b></td></tr><tr><td>&nbsp;</td><td>&nbsp;</td><td><font size=-1><nobr>Rates provided for information only - <a href="/help/currency_disclaimer.html">see disclaimer</a>.</nobr>&nbsp;<a href=/help/features.html#currency>More about currency conversion.</a></td></tr></table><br><table><tr><td><font size=+0>Search for documents containing the terms <a href=/search?hl=en&lr=&c2coff=1&q=%2B1+USD+in+CHF&nocalc=1><b><i>1 USD in CHF</i></b></a>.</td></tr></table><br><br><br clear=all><center><p><hr class=z><table width=100% border=0 cellpadding=0 cellspacing=0><tr><td bgcolor=#3366cc colspan=2><img width=1 height=1 alt=""></td></tr></table><table width=100% cellpadding=2 cellspacing=0 border=0><tr><td align=center class=t><font size=-1><a href="http://www.google.com/">Google&nbsp;Home</a> - <a href="/ads/">Advertising&nbsp;Programs</a> - <a href="/services/">Business&nbsp;Solutions</a> - <a href=/about.html>About Google</a></font></table><br><font size=-1 class=p>&copy;2006 Google</font></center></body></html>
data/test/stub/odba.rb ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # ODBA Stub -- ydim -- 24.01.2005 -- hwyss@ywesee.com
3
+
4
+ require 'odba/odba'
5
+
6
+ module ODBA
7
+ def ODBA.transaction(&block)
8
+ block.call
9
+ end
10
+ module Persistable
11
+ attr_reader :odba_stored
12
+ def odba_store
13
+ @odba_stored = @odba_stored.to_i.next
14
+ end
15
+ end
16
+ class Cache
17
+ def fetch_named(*args, &block)
18
+ block.call
19
+ end
20
+ end
21
+ end
data/test/suite.rb ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # TestSuite -- ydim -- 27.01.2005 -- hwyss@ywesee.com
3
+
4
+ $: << File.dirname(File.expand_path(__FILE__))
5
+
6
+ Dir.foreach(File.dirname(__FILE__)) { |file|
7
+ if /^test_.*\.rb$/o.match(file)
8
+ require file
9
+ end
10
+ }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+ # TestAutoInvoicer -- ydim -- 01.02.2007 -- hwyss@ywesee.com
3
+
4
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
5
+
6
+ require 'test/unit'
7
+ require 'flexmock'
8
+ require 'ydim/autoinvoicer'
9
+ require 'ydim/invoice'
10
+
11
+ module YDIM
12
+ class TestAutoInvoicer < Test::Unit::TestCase
13
+ include FlexMock::TestCase
14
+ def setup
15
+ @serv = flexmock('Registry')
16
+ @autoinvoicer = AutoInvoicer.new(@serv)
17
+ end
18
+ def test_run
19
+ deb1 = flexmock('Debitor1')
20
+ deb1.should_receive(:autoinvoices).and_return([])
21
+ deb2 = flexmock('Debitor2')
22
+ inv1 = flexmock('AutoInvoice1')
23
+ inv1.should_receive(:total_netto).and_return(1)
24
+ inv1.should_receive(:date).and_return(Date.today)
25
+ inv2 = flexmock('AutoInvoice2')
26
+ inv2.should_receive(:total_netto).and_return(1)
27
+ inv2.should_receive(:date).and_return(Date.today >> 1)
28
+ inv3 = flexmock('AutoInvoice3')
29
+ inv3.should_receive(:total_netto).and_return(0)
30
+ deb2.should_receive(:autoinvoices).and_return([inv1, inv2, inv3])
31
+ debitors = { 0 => deb1, 1 => deb2 }
32
+ flexstub(Debitor).should_receive(:odba_extent).and_return { |blk|
33
+ debitors.each_value(&blk)
34
+ }
35
+ @serv.should_receive(:config).and_return('configuration')
36
+ factory = flexmock('Factory')
37
+ factory.should_receive(:generate_invoice).with(inv1)\
38
+ .times(1).and_return(:generated_invoice)
39
+ @serv.should_receive(:factory).and_return(factory)
40
+ ODBA.cache = cache = flexmock('ODBA')
41
+ cache.should_receive(:transaction).and_return { |bl| bl.call }
42
+ mail = flexstub(Mail)
43
+ mail.should_receive(:send_invoice)\
44
+ .with('configuration', :generated_invoice)\
45
+ .and_return { assert(true) }
46
+ mail.should_receive(:send_reminder).with('configuration', inv2)\
47
+ .and_return { assert(true) }
48
+ @autoinvoicer.run
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+ # TestCurrencyConverter -- ydim -- 01.02.2006 -- hwyss@ywesee.com
3
+
4
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
5
+
6
+ require 'test/unit'
7
+ require 'ydim/currency_converter'
8
+
9
+ module YDIM
10
+ class TestCurrencyConverter < Test::Unit::TestCase
11
+ def setup
12
+ @converter = CurrencyConverter.new
13
+ end
14
+ def test_known_currencies
15
+ assert_equal(0, @converter.known_currencies)
16
+ @converter.store('EUR', 'CHF', 1.55474)
17
+ assert_equal(2, @converter.known_currencies)
18
+ @converter.store('USD', 'CHF', 1.28640)
19
+ assert_equal(3, @converter.known_currencies)
20
+ end
21
+ def test_convert
22
+ @converter.store('EUR', 'CHF', 1.55474)
23
+ assert_equal(1.55474, @converter.convert(1, 'EUR', 'CHF'))
24
+ assert_equal(1, @converter.convert(1, 'CHF', 'CHF'))
25
+ assert_equal(1/1.55474, @converter.convert(1, 'CHF', 'EUR'))
26
+ assert_raises(RuntimeError) {
27
+ @converter.convert(1, 'RND', 'CHF')
28
+ }
29
+ end
30
+ def test_drb_dup
31
+ @converter.store('EUR', 'CHF', 1.55474)
32
+ dup = @converter.drb_dup
33
+ assert_instance_of(MobileCurrencyConverter, dup)
34
+ assert_equal(1.55474, dup.convert(1, 'EUR', 'CHF'))
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env ruby
2
+ # TestCurrencyUpdater -- ydim -- 01.02.2006 -- hwyss@ywesee.com
3
+
4
+
5
+ $: << File.expand_path('../lib', File.dirname(__FILE__))
6
+
7
+ require 'test/unit'
8
+ require 'ydim/currency_updater'
9
+ require 'flexmock'
10
+
11
+ module YDIM
12
+ class TestCurrencyUpdater < Test::Unit::TestCase
13
+ include FlexMock::TestCase
14
+ def setup
15
+ @serv = flexmock('Config')
16
+ @updater = CurrencyUpdater.new(@serv)
17
+ end
18
+ def test_extract_conversion
19
+ html = File.read(File.expand_path('data/search.html',
20
+ File.dirname(__FILE__)))
21
+ assert_equal('1.2864003', @updater.extract_conversion(html))
22
+ end
23
+ def test_run
24
+ resp = flexmock('HttpResponse')
25
+ resp.should_receive(:body)\
26
+ .and_return(File.read(File.expand_path('data/search.html',
27
+ File.dirname(__FILE__))))
28
+ session = flexmock('HttpSession')
29
+ session.should_receive(:get).with('/search?q=1+CHF+in+EUR')\
30
+ .and_return(resp)
31
+ flexstub(Net::HTTP).should_receive(:start)\
32
+ .times(1).and_return { |host, block|
33
+ block.call(session)
34
+ }
35
+ conv = flexmock('Converter')
36
+ conv.should_receive(:odba_store).times(1)
37
+ conv.should_receive(:store).times(1).times(1)
38
+ config = flexmock('Config')
39
+ config.should_receive(:currencies).and_return(['CHF', 'EUR'])
40
+ @serv.should_receive(:currency_converter).and_return(conv)
41
+ @serv.should_receive(:config).and_return(config)
42
+ @updater.run
43
+ end
44
+ end
45
+ end