currency_spy 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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