momm 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +118 -0
- data/Rakefile +16 -0
- data/bin/momm +72 -0
- data/lib/momm/calculator.rb +129 -0
- data/lib/momm/feeds/ecb.rb +67 -0
- data/lib/momm/memcached.rb +22 -0
- data/lib/momm/redis_store.rb +24 -0
- data/lib/momm/storage.rb +68 -0
- data/lib/momm/version.rb +3 -0
- data/lib/momm/web.rb +28 -0
- data/lib/momm.rb +92 -0
- data/momm.gemspec +30 -0
- data/myapp/.gitignore +16 -0
- data/myapp/Gemfile +10 -0
- data/myapp/README.rdoc +28 -0
- data/myapp/Rakefile +6 -0
- data/myapp/app/assets/images/.keep +0 -0
- data/myapp/app/assets/javascripts/application.js +15 -0
- data/myapp/app/assets/stylesheets/application.css +13 -0
- data/myapp/app/controllers/application_controller.rb +5 -0
- data/myapp/app/controllers/concerns/.keep +0 -0
- data/myapp/app/helpers/application_helper.rb +2 -0
- data/myapp/app/mailers/.keep +0 -0
- data/myapp/app/models/.keep +0 -0
- data/myapp/app/models/concerns/.keep +0 -0
- data/myapp/app/views/layouts/application.html.erb +14 -0
- data/myapp/bin/bundle +3 -0
- data/myapp/bin/rails +4 -0
- data/myapp/bin/rake +4 -0
- data/myapp/config/application.rb +28 -0
- data/myapp/config/boot.rb +4 -0
- data/myapp/config/environment.rb +5 -0
- data/myapp/config/environments/development.rb +26 -0
- data/myapp/config/environments/production.rb +80 -0
- data/myapp/config/environments/test.rb +36 -0
- data/myapp/config/initializers/backtrace_silencers.rb +7 -0
- data/myapp/config/initializers/filter_parameter_logging.rb +4 -0
- data/myapp/config/initializers/inflections.rb +16 -0
- data/myapp/config/initializers/mime_types.rb +5 -0
- data/myapp/config/initializers/momm.rb +2 -0
- data/myapp/config/initializers/secret_token.rb +12 -0
- data/myapp/config/initializers/session_store.rb +3 -0
- data/myapp/config/initializers/wrap_parameters.rb +9 -0
- data/myapp/config/locales/en.yml +23 -0
- data/myapp/config/routes.rb +5 -0
- data/myapp/config.ru +4 -0
- data/myapp/db/seeds.rb +7 -0
- data/myapp/lib/assets/.keep +0 -0
- data/myapp/lib/tasks/.keep +0 -0
- data/myapp/log/.keep +0 -0
- data/myapp/public/404.html +58 -0
- data/myapp/public/422.html +58 -0
- data/myapp/public/500.html +57 -0
- data/myapp/public/favicon.ico +0 -0
- data/myapp/public/robots.txt +5 -0
- data/myapp/vendor/assets/javascripts/.keep +0 -0
- data/myapp/vendor/assets/stylesheets/.keep +0 -0
- data/spec/momm/calculator_spec.rb +79 -0
- data/spec/momm/feeds/ecb_spec.rb +48 -0
- data/spec/momm/memcached_spec.rb +32 -0
- data/spec/momm/momm_spec.rb +21 -0
- data/spec/momm/redis_store_spec.rb +35 -0
- data/spec/momm/storage_spec.rb +37 -0
- data/spec/momm/version_spec.rb +7 -0
- data/spec/momm/web_spec.rb +31 -0
- data/spec/spec_helper.rb +11 -0
- metadata +235 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8bfaf35e8c7c6d5a3df2b10c78e0d840de8903b9
|
4
|
+
data.tar.gz: 46e99175387cb4ab1823d858a0b4e60afbdfdefb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 31d92303e463cd6b831855472f9e8595a75972ef6c579e6288e1487cf1f7ec6973188c20be089419ce8ad71296c5aaf067dcba9a3a1d1d39c3dd699b9b931fba
|
7
|
+
data.tar.gz: e02cb0d88441d39df19ea6a86780d30b24bd1ebf5f932b52df83214ec0ab10f53313ccb34d77c61e056571459a6e9968958d48169dd90e0542fc023c26ee88d6
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jingkai He
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
# Momm
|
2
|
+
|
3
|
+
Money on My Mind - An awesome gem for currency exchange.
|
4
|
+
|
5
|
+
```
|
6
|
+
__ __ __ __
|
7
|
+
| \/ | | \/ |
|
8
|
+
| \ / | ___ _ __ ___ _ _ ___ _ __ | \ / |_ _
|
9
|
+
| |\/| |/ _ \| '_ \ / _ \ | | | / _ \| '_ \ | |\/| | | | |
|
10
|
+
| | | | (_) | | | | __/ |_| | | (_) | | | | | | | | |_| |
|
11
|
+
|_| |_|\___/|_| |_|\___|\__, | \___/|_| |_| |_| |_|\__, |
|
12
|
+
__/ | __/ |
|
13
|
+
|___/ |___/
|
14
|
+
__ __ _ _
|
15
|
+
| \/ (_) | |
|
16
|
+
| \ / |_ _ __ __| |
|
17
|
+
| |\/| | | '_ \ / _` |
|
18
|
+
| | | | | | | | (_| |
|
19
|
+
|_| |_|_|_| |_|\__,_|
|
20
|
+
|
21
|
+
|
22
|
+
Keep calm bro. We'll calculate rate for you.
|
23
|
+
|
24
|
+
```
|
25
|
+
|
26
|
+
## Requirement
|
27
|
+
|
28
|
+
Ensure that [Memcached](http://memcached.org/) or [Redis](http://redis.io/) is installed on your local machine or server or provided by some other cloud service providers. Those are for exchange rate storage, which ensure your fast queries.
|
29
|
+
|
30
|
+
Local storage is not provided, because it might not safe for work on Cloud Platforms such as Heroku.
|
31
|
+
|
32
|
+
## Installation
|
33
|
+
|
34
|
+
Add this line to your application's Gemfile:
|
35
|
+
|
36
|
+
gem 'momm'
|
37
|
+
|
38
|
+
And then execute:
|
39
|
+
|
40
|
+
$ bundle
|
41
|
+
|
42
|
+
Or install it yourself as:
|
43
|
+
|
44
|
+
$ gem install momm
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### Off Rails
|
49
|
+
|
50
|
+
#### Command Line Tool
|
51
|
+
|
52
|
+
The storage engine by default is Redis, ensure the socket is opened.
|
53
|
+
|
54
|
+
```
|
55
|
+
$ momm rate GBP CNY # Exchange rate by default is today.
|
56
|
+
$ momm exchange 100 GBP USD 2014-3-1 # Exchange rate at 2013-3-1
|
57
|
+
```
|
58
|
+
|
59
|
+
If you want to change the storage strategies, edit it in your ~/.mom/config.yml file.
|
60
|
+
|
61
|
+
#### Ruby
|
62
|
+
|
63
|
+
``` ruby
|
64
|
+
require 'momm'
|
65
|
+
|
66
|
+
Momm.exchange_rate 'GBP', 'USD' # By default Today
|
67
|
+
Momm.exchange_rate 'GBP', 'USD', date: Date.today
|
68
|
+
Momm.exchange_rate_from_gbp_to_usd, date: "2014-3-4"
|
69
|
+
|
70
|
+
Momm.exchange 100, 'GBP', 'USD'
|
71
|
+
Momm.exchange 100, 'GBP', 'USD', date: Date.today
|
72
|
+
Momm.exchange_from_gbp_to_usd 100
|
73
|
+
Momm.exchange_from_gbp_to_usd 100, date: "2014-3-4"
|
74
|
+
|
75
|
+
```
|
76
|
+
|
77
|
+
#### Configuration
|
78
|
+
|
79
|
+
``` ruby
|
80
|
+
|
81
|
+
Momm.store :redis_store # Use redis as the default storage
|
82
|
+
|
83
|
+
Momm.fed :ECB # Use ECB as the default currency exchange feeds
|
84
|
+
|
85
|
+
```
|
86
|
+
|
87
|
+
### Momm on Rails
|
88
|
+
|
89
|
+
Web service is provided by Momm on Rails, however you need to install sinatra simply by adding ```gem 'sinatra'``` into your Gemfile. Then require ```momm/web``` module and edit your routes like:
|
90
|
+
|
91
|
+
``` ruby
|
92
|
+
# routes.rb
|
93
|
+
|
94
|
+
require 'momm/web'
|
95
|
+
|
96
|
+
Myapp::Application.routes.draw do
|
97
|
+
mount Momm::Web => '/momm'
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
The default Storage is Memcached, if you want to switch to Redis, you can create an intialzier like:
|
102
|
+
|
103
|
+
``` ruby
|
104
|
+
|
105
|
+
# momm_initialzer.rb
|
106
|
+
Momm.store :redis_store, host: "127.0.0.1"
|
107
|
+
Momm.source :ECB
|
108
|
+
```
|
109
|
+
|
110
|
+
### @TODO: A small widget
|
111
|
+
|
112
|
+
## Contributing
|
113
|
+
|
114
|
+
1. Fork it ( https://github.com/jaxi/momm/fork )
|
115
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
116
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
117
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
118
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
desc "Run an IRB session with Momm preloaded"
|
5
|
+
task :console do
|
6
|
+
exec "irb -I lib -r momm"
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Run the test suite"
|
10
|
+
RSpec::Core::RakeTask.new(:rspec) do |t|
|
11
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
12
|
+
t.rspec_opts = %w|--color|
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: :spec
|
16
|
+
|
data/bin/momm
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << File.dirname(__FILE__) + "/../lib" if $0 == __FILE__
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
require 'momm'
|
6
|
+
|
7
|
+
OPERATIONS = %w{rate exchange}
|
8
|
+
|
9
|
+
option_parser = OptionParser.new do |opts|
|
10
|
+
opts.banner = Momm::BANNER
|
11
|
+
|
12
|
+
opts.separator <<-EOS
|
13
|
+
|
14
|
+
Supported commands:
|
15
|
+
|
16
|
+
rate [country_code] [country_code] [YYYY-MM-DD] Display rate!
|
17
|
+
exchange [money] [country_code] [country_code] [YYYY-MM-DD] Exchange for you!
|
18
|
+
|
19
|
+
Examples:
|
20
|
+
mmom exchange 20 GBP CNY Convert the between currencies
|
21
|
+
mmom rate GBP CNY Simply display the rate
|
22
|
+
|
23
|
+
Support Code:
|
24
|
+
|
25
|
+
#{Momm.currencies.join(" ")}
|
26
|
+
|
27
|
+
To be honest I don't know most of them...
|
28
|
+
|
29
|
+
EOS
|
30
|
+
end
|
31
|
+
|
32
|
+
option_parser.parse!
|
33
|
+
|
34
|
+
|
35
|
+
op = ARGV.shift
|
36
|
+
if OPERATIONS.include?(op)
|
37
|
+
begin
|
38
|
+
case op
|
39
|
+
when "rate"
|
40
|
+
from = ARGV[0].to_sym
|
41
|
+
to = ARGV[1].to_sym
|
42
|
+
date = if ARGV[2]
|
43
|
+
Date.parse(ARGV[2])
|
44
|
+
else
|
45
|
+
Date.today
|
46
|
+
end
|
47
|
+
|
48
|
+
puts Momm.exchange_rate from, to, date: date
|
49
|
+
when "exchange"
|
50
|
+
money = ARGV[0].to_f
|
51
|
+
from = ARGV[1].to_sym
|
52
|
+
to = ARGV[2].to_sym
|
53
|
+
date = if ARGV[3]
|
54
|
+
Date.parse(ARGV[3])
|
55
|
+
else
|
56
|
+
Date.today
|
57
|
+
end
|
58
|
+
|
59
|
+
puts Momm.exchange money, from, to, date: date
|
60
|
+
end
|
61
|
+
|
62
|
+
rescue ArgumentError => ex
|
63
|
+
puts ex.message
|
64
|
+
|
65
|
+
rescue Exception => e
|
66
|
+
puts "Mmmmm, I didn't expect this:"
|
67
|
+
puts e.message
|
68
|
+
puts e.backtrace.join("\n")
|
69
|
+
end
|
70
|
+
else
|
71
|
+
puts option_parser.help
|
72
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Momm
|
2
|
+
class Calculator
|
3
|
+
extend ::Forwardable
|
4
|
+
# Initialise Storage Object
|
5
|
+
#
|
6
|
+
# == Parameters:
|
7
|
+
# client::
|
8
|
+
# A storage, such as memcached, redis
|
9
|
+
# == Returns
|
10
|
+
# self
|
11
|
+
#
|
12
|
+
def initialize(storage = Memcached.new, feed = Feeds::ECB.instance)
|
13
|
+
@storage = storage
|
14
|
+
@feed = feed
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :storage, :feed
|
18
|
+
|
19
|
+
# delegate the client and update method from storage
|
20
|
+
delegate [:client, :update, :set_rate] => :storage
|
21
|
+
|
22
|
+
# delegate the currencies method from feed
|
23
|
+
delegate :currencies => :feed
|
24
|
+
|
25
|
+
# delegate the get rate method with a different naming
|
26
|
+
def_delegator :storage, :get_rate, :get_rate_origin
|
27
|
+
|
28
|
+
# Exchange Rate
|
29
|
+
#
|
30
|
+
# == Parameters
|
31
|
+
# from::
|
32
|
+
# ruby symbol, such as :USD, :GBP
|
33
|
+
# to::
|
34
|
+
# same as above
|
35
|
+
# options::
|
36
|
+
# 0ption parameters, contain today by default
|
37
|
+
#
|
38
|
+
# == Returns
|
39
|
+
# the exchange rate
|
40
|
+
#
|
41
|
+
def exchange_rate(from, to, options = {})
|
42
|
+
date = options[:date] || Date.today
|
43
|
+
date = Date.parse(date) if date.is_a? String
|
44
|
+
|
45
|
+
max_count = 10
|
46
|
+
date_counter = date
|
47
|
+
|
48
|
+
# @TODO Refactoring.
|
49
|
+
# It seems that currency does not have feeds at weekends, so
|
50
|
+
# we simply find the closest day which has currency feeds.
|
51
|
+
while max_count > 0
|
52
|
+
to_rate = get_rate(to, date_counter)
|
53
|
+
from_rate = get_rate(from, date_counter)
|
54
|
+
|
55
|
+
date_counter -=1
|
56
|
+
max_count -= 1
|
57
|
+
next if to_rate == 0 || from_rate == 0
|
58
|
+
|
59
|
+
set_rate(to, to_rate, date)
|
60
|
+
set_rate(from, from_rate, date)
|
61
|
+
|
62
|
+
return (to_rate / from_rate).round(2)
|
63
|
+
end
|
64
|
+
|
65
|
+
0.0 / 0
|
66
|
+
end
|
67
|
+
|
68
|
+
# Exchange Money from one currency to another
|
69
|
+
#
|
70
|
+
# == Parameters
|
71
|
+
# money::
|
72
|
+
# money you have
|
73
|
+
# from::
|
74
|
+
# ruby symbol, such as :USD, :GBP
|
75
|
+
# to::
|
76
|
+
# same as above
|
77
|
+
# options::
|
78
|
+
# option parameters, contain today by default
|
79
|
+
#
|
80
|
+
# == Returns
|
81
|
+
# money exchanged
|
82
|
+
#
|
83
|
+
def exchange(money, from, to, options= {})
|
84
|
+
options[:date] ||= Date.today
|
85
|
+
(money * exchange_rate(from, to, options)).round(2)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Delegate the get_rate method, if the target is missing
|
89
|
+
# Fetching all data from remote
|
90
|
+
#
|
91
|
+
# == Parameters
|
92
|
+
#
|
93
|
+
# date::
|
94
|
+
# Default is Date.today
|
95
|
+
# currency::
|
96
|
+
# Currency passed in
|
97
|
+
# == Returns
|
98
|
+
# the currency rate
|
99
|
+
#
|
100
|
+
def get_rate(from, date = Date.today)
|
101
|
+
res = get_rate_origin(from, date)
|
102
|
+
return res if res != 0 && res
|
103
|
+
|
104
|
+
update(feed.currency_rates)
|
105
|
+
get_rate_origin(from, date)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @TODO: Refactoring
|
109
|
+
def method_missing(meth, *args, &block)
|
110
|
+
meth = meth.to_s
|
111
|
+
case
|
112
|
+
when meth.match(/^exchange_rate_from_(\w+)_to_(\w+)/)
|
113
|
+
exchange_rate($1.upcase.to_sym, $2.upcase.to_sym, *args, &block)
|
114
|
+
when meth.match(/^exchange_from_(\w+)_to_(\w+)/)
|
115
|
+
money, *res = args
|
116
|
+
exchange(money, $1.upcase.to_sym, $2.upcase.to_sym, *res, &block)
|
117
|
+
else
|
118
|
+
super
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def respond_to?(meth, include_private = false)
|
123
|
+
meth = meth.to_s
|
124
|
+
meth.match(/^exchange_rate_from_(\w+)_to_(\w+)/) ||
|
125
|
+
meth.match(/^exchange_from_(\w+)_to_(\w+)/) ||
|
126
|
+
super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'open-uri'
|
3
|
+
|
4
|
+
module Momm
|
5
|
+
module Feeds
|
6
|
+
class ECB
|
7
|
+
|
8
|
+
FETCHING_URL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml".freeze
|
9
|
+
|
10
|
+
#Hard coded for good
|
11
|
+
CURRENCIES = %w{USD JPY BGN CZK DKK GBP HUF LTL PLN RON SEK CHF
|
12
|
+
NOK HRK RUB TRY AUD BRL CAD CNY HKD IDR ILS INR KRW
|
13
|
+
MXN MYR NZD PHP SGD THB ZAR}.freeze
|
14
|
+
|
15
|
+
class << self
|
16
|
+
|
17
|
+
# Eager loading the instance of ECB
|
18
|
+
#
|
19
|
+
# == Returns
|
20
|
+
# self
|
21
|
+
#
|
22
|
+
def instance
|
23
|
+
@instance ||= self.send :new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# should be a singleton class
|
28
|
+
private_class_method :new
|
29
|
+
|
30
|
+
# Parse the XML data by Nokogiri
|
31
|
+
# == Returns
|
32
|
+
# Nokogiri Object
|
33
|
+
#
|
34
|
+
def parsed_content
|
35
|
+
# @TODO Refactoring Bad patterns
|
36
|
+
HTTParty.get(fetching_url, format: :xml)['Envelope']['Cube']['Cube']
|
37
|
+
end
|
38
|
+
|
39
|
+
# convert the nokogiri parsed data to array
|
40
|
+
#
|
41
|
+
# == Returns
|
42
|
+
# looks like [{date: Date.now, currency: :CNY, rate: 1.23} ...]
|
43
|
+
#
|
44
|
+
def currency_rates
|
45
|
+
parsed_content.map do |content|
|
46
|
+
date = Date.parse(content["time"])
|
47
|
+
cubes = content["Cube"]
|
48
|
+
cubes.map do |cube|
|
49
|
+
{
|
50
|
+
date: date,
|
51
|
+
currency: cube["currency"].to_sym,
|
52
|
+
rate: cube["rate"].to_f
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end.flatten
|
56
|
+
end
|
57
|
+
|
58
|
+
def fetching_url
|
59
|
+
FETCHING_URL
|
60
|
+
end
|
61
|
+
|
62
|
+
def currencies
|
63
|
+
CURRENCIES
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'dalli'
|
2
|
+
|
3
|
+
module Momm
|
4
|
+
class Memcached < Storage
|
5
|
+
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
connection: "localhost:11211", namespace: "momm", compress: true
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
attr_reader :connection, :options
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
_options = DEFAULT_OPTIONS.dup.merge options
|
14
|
+
@connection = options.delete(:connection)
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def client
|
19
|
+
@client ||= Dalli::Client.new connection, options
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
module Momm
|
4
|
+
class RedisStore < Storage
|
5
|
+
DEFAULT_OPTIONS = { host: "localhost", port: 6379, namespace: "momm"}
|
6
|
+
|
7
|
+
attr_accessor :options
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@options = DEFAULT_OPTIONS.dup.merge options
|
11
|
+
end
|
12
|
+
|
13
|
+
def client
|
14
|
+
@client ||= begin
|
15
|
+
ns = options.delete(:namespace)
|
16
|
+
|
17
|
+
require 'redis/namespace'
|
18
|
+
native_client = Redis.new options
|
19
|
+
|
20
|
+
Redis::Namespace.new(ns, :redis => native_client)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/momm/storage.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
module Momm
|
2
|
+
class Storage
|
3
|
+
# The presenter model to rule the behavior of storage.
|
4
|
+
|
5
|
+
# Lazy load Client
|
6
|
+
#
|
7
|
+
# == Returns
|
8
|
+
# Client
|
9
|
+
#
|
10
|
+
def client
|
11
|
+
raise NotImplementedError
|
12
|
+
end
|
13
|
+
|
14
|
+
# Insert the currency rate to client
|
15
|
+
#
|
16
|
+
# == Parameters
|
17
|
+
# currency::
|
18
|
+
# Currency passed in, should be a symbol of iso code,
|
19
|
+
# such as :CNY, :USD, or :GBP
|
20
|
+
# rate::
|
21
|
+
# Fixnum represent for the currency rate
|
22
|
+
# from euro to certain currency
|
23
|
+
# date::
|
24
|
+
# Ruby date type, the date of currency rate, today by default
|
25
|
+
#
|
26
|
+
# == Returns
|
27
|
+
# nil
|
28
|
+
#
|
29
|
+
def set_rate(currency, rate, date = Date.today)
|
30
|
+
date = Date.parse(date) if date.is_a? String
|
31
|
+
client.set "#{date}#{currency}", rate
|
32
|
+
end
|
33
|
+
|
34
|
+
# Fetch the currency rate from client
|
35
|
+
#
|
36
|
+
# == Parameters
|
37
|
+
# currency::
|
38
|
+
# Currency passed in
|
39
|
+
# date::
|
40
|
+
# Ruby date type, the date of currency rate, today by default
|
41
|
+
#
|
42
|
+
# == Returns
|
43
|
+
# the currency rate
|
44
|
+
#
|
45
|
+
def get_rate(currency, date = Date.today)
|
46
|
+
date = Date.parse(date) if date.is_a? String
|
47
|
+
client.get("#{date}#{currency}").to_f
|
48
|
+
end
|
49
|
+
|
50
|
+
# update the data to storage
|
51
|
+
#
|
52
|
+
# == parameters
|
53
|
+
# data::
|
54
|
+
# An array looks like [{date: Date.now, currency: :CNY, rate: 1.23} ...]
|
55
|
+
#
|
56
|
+
# == Returns
|
57
|
+
# nil
|
58
|
+
#
|
59
|
+
def update(data)
|
60
|
+
data.each do |d|
|
61
|
+
set_rate d[:currency], d[:rate], d[:date]
|
62
|
+
end
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
NotImplementedError = Class.new(StandardError)
|
67
|
+
end
|
68
|
+
end
|
data/lib/momm/version.rb
ADDED
data/lib/momm/web.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'momm'
|
3
|
+
|
4
|
+
module Momm
|
5
|
+
class Web < ::Sinatra::Base
|
6
|
+
get '/query' do
|
7
|
+
content_type :json
|
8
|
+
|
9
|
+
money = params[:money].to_f
|
10
|
+
from = params[:from].to_sym
|
11
|
+
to = params[:to].to_sym
|
12
|
+
date = params[:date] || Date.today
|
13
|
+
|
14
|
+
|
15
|
+
if money && from && to && date
|
16
|
+
Momm.exchange(money, from, to, date: date).to_json
|
17
|
+
else
|
18
|
+
"N/A"
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/currencies' do
|
24
|
+
content_type :json
|
25
|
+
Momm.currencies.to_json
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|