currency_spy 0.0.1 → 0.0.3

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,54 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: dnb_nord_spec.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-01-12 17:01:27 +0100</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>spec_helper</li>
35
+
36
+ </ul>
37
+ </dd>
38
+
39
+
40
+
41
+ </dl>
42
+ </div>
43
+
44
+ <div id="documentation">
45
+
46
+ <div class="description">
47
+ <h2>Description</h2>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
54
+
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: nbp_spec.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-01-10 10:11:18 +0100</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>spec_helper</li>
35
+
36
+ </ul>
37
+ </dd>
38
+
39
+
40
+
41
+ </dl>
42
+ </div>
43
+
44
+ <div id="documentation">
45
+
46
+ <div class="description">
47
+ <h2>Description</h2>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
54
+
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: scraper_spec.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-01-10 10:11:18 +0100</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>spec_helper</li>
35
+
36
+ </ul>
37
+ </dd>
38
+
39
+
40
+
41
+ </dl>
42
+ </div>
43
+
44
+ <div id="documentation">
45
+
46
+ <div class="description">
47
+ <h2>Description</h2>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
54
+
@@ -0,0 +1,56 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: spec_helper.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-01-08 13:47:42 +0100</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>rspec</li>
35
+
36
+ <li>currency_spy</li>
37
+
38
+ </ul>
39
+ </dd>
40
+
41
+
42
+
43
+ </dl>
44
+ </div>
45
+
46
+ <div id="documentation">
47
+
48
+ <div class="description">
49
+ <h2>Description</h2>
50
+
51
+ </div>
52
+
53
+ </div>
54
+ </body>
55
+ </html>
56
+
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: walutomat_spec.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2011-01-10 10:11:18 +0100</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>spec_helper</li>
35
+
36
+ </ul>
37
+ </dd>
38
+
39
+
40
+
41
+ </dl>
42
+ </div>
43
+
44
+ <div id="documentation">
45
+
46
+ <div class="description">
47
+ <h2>Description</h2>
48
+
49
+ </div>
50
+
51
+ </div>
52
+ </body>
53
+ </html>
54
+
data/lib/currency_spy.rb CHANGED
@@ -1,11 +1,27 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
2
  require 'mechanize'
3
3
  require 'currency_spy/scraper'
4
- require 'currency_spy/nbp'
5
4
 
5
+ scrapers = %w(nbp walutomat dnb_nord)
6
+ scrapers.each do |scraper|
7
+ require "currency_spy/#{scraper}"
8
+ end
9
+
10
+ # This gem is a set of scrapers to fetch currencies from various
11
+ # institutions' websites. It's primary use is to monitor the changes of
12
+ # currency rates across different institutions to calculate spreads (difference
13
+ # between the buying rate and the selling rate. It fetches the rates in PLN
14
+ # (Polish Zloty) however if there's a demand I could extend the gem to be more
15
+ # flexible.
16
+ #
17
+ # Author:: Łukasz Badura (mailto:lukasz@niebo.net)
18
+ # Copyright:: Copyright (c) 2011 Łukasz Badura
19
+ # License:: MIT license.
6
20
  module CurrencySpy
21
+ # Default date format used for date presentation across the code
7
22
  DATE_FORMAT = "%d-%m-%Y"
8
23
 
24
+ # Returns a string representation of a given date in the default format
9
25
  def self.datestr(date)
10
26
  return date.strftime(CurrencySpy::DATE_FORMAT)
11
27
  end
@@ -0,0 +1,57 @@
1
+ module CurrencySpy
2
+ # Class designed to fetch data from DnB Nord's website.
3
+ class DnbNord < CurrencySpy::Scraper
4
+
5
+ attr_accessor :session_date, :session_no
6
+
7
+ # Constructor. Initializes the following:
8
+ # * url is set to nil
9
+ # * institution name
10
+ # * a list of currency codes available from that source
11
+ # * session_no number of the session. There are two sessions. 1 means the first at 8:15 am and 2 means the second one held at 12:15
12
+ def initialize
13
+ super
14
+ @url = nil
15
+ @institution = 'DnB Nord'
16
+ @available_codes = %w(EUR USD GBP CHF DKK NOK SEK CZK JPY HUF CAD AUD LTL)
17
+ @session_no = nil
18
+ end
19
+
20
+ # Determines the correct url of the web page to fetch rates from based of the instance variable session_no
21
+ def url
22
+ dnb_session_no = @session_no ||= 1
23
+ session_time = dnb_session_no == 2 ? "12:15" : "08:15"
24
+ return "http://www.dnbnord.pl/pl/tabela-kursow-walut-dla-kredytow/go:godzina=#{session_time}"
25
+ end
26
+
27
+ # Fetches the selling rate
28
+ def sell_rate
29
+ regexp = Regexp.new(currency_code)
30
+ res = nil
31
+ page.search("//td").each do |td|
32
+ if (regexp.match(td.content))
33
+ res = td.next_element.next_element.content.to_f
34
+ end
35
+ end
36
+ return res
37
+ end
38
+
39
+ # Fetches the buying rate
40
+ def buy_rate
41
+ regexp = Regexp.new(currency_code)
42
+ res = nil
43
+ page.search("//td").each do |td|
44
+ if (regexp.match(td.content))
45
+ res = td.next_element.content.to_f
46
+ end
47
+ end
48
+ return res
49
+ end
50
+
51
+ # Fetches the time of given rates
52
+ def rate_time
53
+ hour = @session_time == 2 ? "12:15" : "08:15"
54
+ DateTime.parse(hour)
55
+ end
56
+ end
57
+ end
@@ -1,16 +1,23 @@
1
1
  module CurrencySpy
2
+ # A class designed to fetch currency information from the website of The Polish National Bank (Narodowy Bank Polski).
2
3
  class Nbp < CurrencySpy::Scraper
3
4
 
5
+ # Constructor method.
6
+ # Initializes the following:
7
+ # * a url of the source
8
+ # * and the name of the source
9
+ # * a list of currency codes available
4
10
  def initialize
5
11
  super
6
12
  @url = 'http://www.nbp.pl/home.aspx?f=/kursy/kursyc.html'
7
- @page = parser.get(url)
13
+ @institution = "Narodowy Bank Polski"
14
+ @available_codes = %w(EUR USD GBP CHF AUS AUD CAD CZK DKK NOK SEK)
8
15
  end
9
16
 
10
- def buy_rate(currency_code = nil)
11
- currency_code = @currency_code ||= 'EUR'
17
+ # Get the buying rate from the parsed website
18
+ def buy_rate
12
19
  regexp = Regexp.new(currency_code)
13
- @page.search('//tr[@valign="middle"]').each do |tr|
20
+ page.search('//tr[@valign="middle"]').each do |tr|
14
21
  tr.search('td').each do |td|
15
22
  if (regexp.match(td.content))
16
23
  return td.next_element.content.sub(',','.').to_f
@@ -20,10 +27,10 @@ module CurrencySpy
20
27
  return nil
21
28
  end
22
29
 
23
- def sell_rate(currency_code = nil)
24
- currency_code = @currency_code ||= 'EUR'
30
+ # Get the selling rate from the parsed website
31
+ def sell_rate
25
32
  regexp = Regexp.new(currency_code)
26
- @page.search('//tr[@valign="middle"]').each do |tr|
33
+ page.search('//tr[@valign="middle"]').each do |tr|
27
34
  tr.search('td').each do |td|
28
35
  if (regexp.match(td.content))
29
36
  return td.next_element.next_element.content.sub(',','.').to_f
@@ -33,10 +40,11 @@ module CurrencySpy
33
40
  return nil
34
41
  end
35
42
 
43
+ # Get the time for this rate (based on the information on the website)
36
44
  def rate_time
37
45
  regexp = Regexp.new(/\d\d\d\d-\d\d-\d\d/)
38
46
  res = nil
39
- @page.search('//p[@class="nag"]').each do |p|
47
+ page.search('//p[@class="nag"]').each do |p|
40
48
  p.search('b').each do |b|
41
49
  if (regexp.match(b.content))
42
50
  res = b.content
@@ -45,6 +53,6 @@ module CurrencySpy
45
53
  end
46
54
  return DateTime.strptime(res, "%Y-%m-%d")
47
55
  end
48
-
56
+
49
57
  end
50
58
  end
@@ -1,39 +1,68 @@
1
1
  module CurrencySpy
2
+ # A base class for all scrapers. Defines the common logic which
3
+ # is used across all of them.
2
4
  class Scraper
3
- AVAILABLE_CODES = ['EUR', 'CHF', 'GBP'].freeze
4
- attr_accessor :url, :currency_code
5
- attr_reader :available_codes, :parser
5
+ # Holds all data which is being fetched by the scrapers.
6
+
7
+ # Each item has to be implemented as a method in the scraper sub class.
8
+ RATE_DATA = %w(buy_rate sell_rate medium_rate rate_time).freeze
9
+
10
+ # current currency code we're fetching rates for
11
+ attr_accessor :currency_code
12
+ # url of the source web page
13
+ attr_accessor :url
14
+ # a list of codes available from given source
15
+ attr_reader :available_codes
16
+ # name of the source institution
17
+ attr_reader :institution
18
+
19
+ # Base constructor
6
20
  def initialize()
7
- @parser = Mechanize.new
8
21
  @url = nil
22
+ @currency_code = 'EUR'
23
+ end
24
+
25
+ # Returns a Mechanize::Page instance which is the being searched on.
26
+ # If the page was once fetched it's reuse. To reload, call with an argument
27
+ # evaluating to true.
28
+ def page(reload = false)
29
+ return nil if url.nil?
30
+ unless reload
31
+ @page ||= Mechanize.new.get(url)
32
+ else
33
+ @page = Mechanize.new.get(url)
34
+ end
35
+ return @page
9
36
  end
10
37
 
11
- def fetch_rates(currency_code = 'EUR')
38
+ # Method which calls all rate fetching methods from the sub class and returns
39
+ # a Hash with appropriate values.
40
+ def fetch_rates
12
41
  if self.class.superclass.eql?(Object)
13
42
  raise Exception.new("This method should be invoked from CurrencySpy::Scraper sub class")
14
43
  else
15
- if AVAILABLE_CODES.include?(currency_code)
16
- @currency_code = currency_code
17
- return {:buy_rate => buy_rate, :sell_rate => sell_rate, :rate_time => rate_time}
18
- else
19
- raise Exception.new("Unsupported currency code: #{currency_code}")
44
+ check_currency_code_validity
45
+ response = {}
46
+ RATE_DATA.each do |rate|
47
+ symbol = rate.to_sym
48
+ if self.class.instance_methods.include?(symbol)
49
+ value = self.send(symbol)
50
+ response[symbol] = value unless value.nil?
51
+ end
20
52
  end
53
+ return response
21
54
  end
22
55
  end
23
56
 
24
- def buy_rate
25
- # This method should be defined in a sub class
26
- raise NotImplementedError
27
- end
28
-
29
- def sell_rate
30
- # This method should be defined in a sub class
31
- raise NotImplementedError
32
- end
33
-
34
- def rate_time
35
- # This method should be defined in a sub class
36
- raise NotImplementedError
57
+ protected
58
+ # Helper method to check if a given source provides (supports) information
59
+ # about given currency.
60
+ def check_currency_code_validity
61
+ if available_codes.include?(currency_code)
62
+ return true
63
+ else
64
+ raise Exception.new("Unsupported currency code: #{currency_code}")
65
+ end
37
66
  end
38
67
  end
39
68
  end