simple_currency 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -37,6 +37,15 @@ in the past? Just do this:
37
37
  42.eur.at(Time.parse('2009-09-01')).to_usd
38
38
  # => 60.12
39
39
 
40
+ You can also add an subtract money expressions, which will return a result
41
+ converted to the former currency of the expression:
42
+
43
+ 42.eur + 30.usd
44
+ # => The same as adding 42 and 30.usd.to_eur
45
+
46
+ 10.gbp + 1.eur
47
+ # => The same as adding 10 and 1.eur.to_gbp
48
+
40
49
  ##Installation
41
50
 
42
51
  ###Rails 3
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.2
1
+ 1.3.0
@@ -1,3 +1,3 @@
1
- class Numeric
1
+ class Fixnum
2
2
  include CurrencyConvertible
3
3
  end
@@ -0,0 +1,3 @@
1
+ class Float
2
+ include CurrencyConvertible
3
+ end
@@ -1,2 +1,3 @@
1
1
  require 'simple_currency/currency_convertible'
2
- require 'core_ext/numeric'
2
+ require 'core_ext/fixnum'
3
+ require 'core_ext/float'
@@ -4,53 +4,82 @@ require 'crack/xml'
4
4
 
5
5
  module CurrencyConvertible
6
6
 
7
- def method_missing(method, *args, &block)
8
- return _from(method.to_s) if method.to_s.length == 3 # Presumably a currency ("eur", "gbp"...)
7
+ Operators = {:+ => :add,
8
+ :- => :subtract}
9
9
 
10
- # Now capture methods like to_eur, to_gbp, to_usd...
11
- if @original && !(method.to_s =~ /^to_(utc|int|str|ary)/) && method.to_s =~/^to_/ && method.to_s.length == 6
12
- return _to(method.to_s.gsub('to_',''))
13
- end
10
+ def add_with_currency(arg)
11
+ return add_without_currency(arg) unless arg.is_a? CurrencyConvertible::Proxy
12
+ add_without_currency(arg)
13
+ end
14
14
 
15
- super(method,*args,&block)
15
+ def subtract_with_currency(arg)
16
+ return subtract_without_currency(arg) unless arg.is_a? CurrencyConvertible::Proxy
17
+ subtract_without_currency(arg)
16
18
  end
17
19
 
18
- # Historical exchange lookup
19
- def at(exchange = nil)
20
- begin
21
-
22
- @exchange_date = exchange.send(:to_date)
23
- rescue
24
- raise "Must use 'at' with a time or date object"
25
- end
26
- self
20
+ def self.included(base)
21
+ base.send(:alias_method, :add_without_currency, :+)
22
+ base.send(:undef_method, :+)
23
+ base.send(:alias_method, :+, :add_with_currency)
24
+
25
+ base.send(:alias_method, :subtract_without_currency, :-)
26
+ base.send(:undef_method, :-)
27
+ base.send(:alias_method, :-, :subtract_with_currency)
27
28
  end
28
29
 
29
- private
30
-
31
- # Called from first currency metamethod to set the original currency.
32
- #
33
- # 30.eur # => Calls _from and sets @original to 'eur'
34
- #
35
- def _from(currency)
30
+ def method_missing(method, *args, &block)
31
+ return CurrencyConvertible::Proxy.new(self,method.to_s) if method.to_s.length == 3 # Presumably a currency ("eur", "gbp"...)
32
+ super(method,*args,&block)
33
+ end
34
+
35
+ class Proxy
36
+ attr_reader :numeric
37
+
38
+ def initialize(numeric,currency)
39
+ @numeric = numeric
40
+ @currency = currency
36
41
  @exchange_date = Time.now.send(:to_date)
37
- @original = currency
42
+ end
43
+
44
+ def method_missing(method, *args, &block)
45
+ if !(method.to_s =~ /^to_(utc|int|str|ary)/) && method.to_s =~/^to_/ && method.to_s.length == 6
46
+ return _to(method.to_s.gsub('to_',''))
47
+ end
48
+ @numeric.send(method, *args, &block)
49
+ end
50
+
51
+ # Historical exchange lookup
52
+ def at(exchange = nil)
53
+ begin
54
+ @exchange_date = exchange.send(:to_date)
55
+ rescue
56
+ raise "Must use 'at' with a time or date object"
57
+ end
38
58
  self
39
59
  end
40
60
 
41
- # Called from last currency metamethod to set the target currency.
42
- #
43
- # 30.eur.to_usd
44
- # # => Calls _to and returns the final value, say 38.08
45
- #
61
+ def +(other)
62
+ return @numeric + other unless other.is_a? CurrencyConvertible::Proxy
63
+ converted = other.send(:"to_#{@currency}")
64
+ @numeric + converted
65
+ end
66
+
67
+ def -(other)
68
+ return @numeric - other unless other.is_a? CurrencyConvertible::Proxy
69
+ converted = other.send(:"to_#{@currency}")
70
+ @numeric - converted
71
+ end
72
+
73
+ private
74
+
46
75
  def _to(target)
47
- raise unless @original # Must be called after a _from have set the @original currency
76
+ raise unless @currency
48
77
 
49
- return 0.0 if self == 0 # Obviously
78
+ return 0.0 if @numeric == 0 # Obviously
50
79
 
51
- original = @original
80
+ original = @currency
52
81
 
53
- amount = self
82
+ amount = @numeric
54
83
 
55
84
  # Check if there's a cached exchange rate for today
56
85
  return cached_amount(original, target, amount) if cached_rate(original, target)
@@ -59,16 +88,16 @@ module CurrencyConvertible
59
88
  result = exchange(original, target, amount.abs)
60
89
 
61
90
  # Cache methods
62
- cache_currency_methods(original, target)
91
+ #cache_currency_methods(original, target)
63
92
 
64
93
  result
65
94
  end
66
95
 
67
- # Main method (called by _to) which calls Xavier or Xurrency strategies
96
+ # Main method (called by _to) which calls Xavier API
68
97
  # and returns a nice result.
69
98
  #
70
99
  def exchange(original, target, amount)
71
- negative = (self < 0)
100
+ negative = (@numeric < 0)
72
101
 
73
102
  # Get the result and round it to 2 decimals
74
103
  result = sprintf("%.2f", call_xavier_api(original, target, amount)).to_f
@@ -117,8 +146,10 @@ module CurrencyConvertible
117
146
  uri = URI.parse(api_url)
118
147
  retry
119
148
  else
120
- raise "404 Not Found"
149
+ raise NotFoundError.new("404 Not Found")
121
150
  end
151
+ rescue SocketError
152
+ raise NotFoundError.new("Socket Error")
122
153
  end
123
154
 
124
155
  return nil unless xml_response && parsed_response = Crack::XML.parse(xml_response)
@@ -152,20 +183,6 @@ module CurrencyConvertible
152
183
  rate.first['rate'].to_f
153
184
  end
154
185
 
155
- # Caches currency methods to avoid method missing abuse.
156
- #
157
- def cache_currency_methods(original, target)
158
- # Cache the _from method for faster reuse
159
- self.class.send(:define_method, original.to_sym) do
160
- _from(original)
161
- end unless self.respond_to?(original.to_sym)
162
-
163
- # Cache the _to method for faster reuse
164
- self.class.send(:define_method, :"to_#{target}") do
165
- _to(target)
166
- end unless self.respond_to?(:"to_#{target}")
167
- end
168
-
169
186
  ##
170
187
  # Cache helper methods (only useful in a Rails app)
171
188
  ##
@@ -209,6 +226,7 @@ module CurrencyConvertible
209
226
  end
210
227
  nil
211
228
  end
229
+ end
212
230
 
213
231
  end
214
232
 
@@ -222,4 +240,7 @@ end
222
240
  class NoRatesFoundException < StandardError
223
241
  end
224
242
 
243
+ class NotFoundError < StandardError
244
+ end
245
+
225
246
 
@@ -5,45 +5,47 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{simple_currency}
8
- s.version = "1.2.2"
8
+ s.version = "1.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Oriol Gual", "Josep M. Bach", "Josep Jaume Rey"]
12
- s.date = %q{2010-10-14}
12
+ s.date = %q{2010-10-24}
13
13
  s.description = %q{A really simple currency converter using XavierMedia API. It's Ruby 1.8, 1.9 and JRuby compatible, and it also takes advantage of Rails cache when available.}
14
14
  s.email = %q{info@codegram.com}
15
15
  s.extra_rdoc_files = [
16
16
  "LICENSE",
17
- "README.md"
17
+ "README.md"
18
18
  ]
19
19
  s.files = [
20
20
  ".autotest",
21
- ".bundle/config",
22
- ".gitignore",
23
- ".rspec",
24
- ".rvmrc",
25
- "Gemfile",
26
- "Gemfile.lock",
27
- "LICENSE",
28
- "README.md",
29
- "Rakefile",
30
- "VERSION",
31
- "autotest/discover.rb",
32
- "lib/core_ext/numeric.rb",
33
- "lib/simple_currency.rb",
34
- "lib/simple_currency/currency_convertible.rb",
35
- "simple_currency.gemspec",
36
- "spec/simple_currency_spec.rb",
37
- "spec/spec_helper.rb",
38
- "spec/support/xavier.xml"
21
+ ".bundle/config",
22
+ ".gitignore",
23
+ ".rspec",
24
+ ".rvmrc",
25
+ "Gemfile",
26
+ "Gemfile.lock",
27
+ "LICENSE",
28
+ "README.md",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "autotest/discover.rb",
32
+ "lib/core_ext/fixnum.rb",
33
+ "lib/core_ext/float.rb",
34
+ "lib/simple_currency.rb",
35
+ "lib/simple_currency/currency_convertible.rb",
36
+ "simple_currency.gemspec",
37
+ "spec/simple_currency_spec.rb",
38
+ "spec/spec_helper.rb",
39
+ "spec/support/xavier.xml"
39
40
  ]
40
41
  s.homepage = %q{http://github.com/codegram/simple_currency}
42
+ s.rdoc_options = ["--charset=UTF-8"]
41
43
  s.require_paths = ["lib"]
42
44
  s.rubygems_version = %q{1.3.7}
43
45
  s.summary = %q{A really simple currency converter using XavierMedia API.}
44
46
  s.test_files = [
45
47
  "spec/simple_currency_spec.rb",
46
- "spec/spec_helper.rb"
48
+ "spec/spec_helper.rb"
47
49
  ]
48
50
 
49
51
  if s.respond_to? :specification_version then
@@ -51,7 +53,6 @@ Gem::Specification.new do |s|
51
53
  s.specification_version = 3
52
54
 
53
55
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
- s.add_runtime_dependency(%q<crack>, [">= 0"])
55
56
  s.add_runtime_dependency(%q<crack>, [">= 0.1.8"])
56
57
  s.add_development_dependency(%q<jeweler>, [">= 1.4.0"])
57
58
  s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.22"])
@@ -59,7 +60,6 @@ Gem::Specification.new do |s|
59
60
  s.add_development_dependency(%q<rails>, [">= 3.0.0"])
60
61
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
61
62
  else
62
- s.add_dependency(%q<crack>, [">= 0"])
63
63
  s.add_dependency(%q<crack>, [">= 0.1.8"])
64
64
  s.add_dependency(%q<jeweler>, [">= 1.4.0"])
65
65
  s.add_dependency(%q<rspec>, [">= 2.0.0.beta.22"])
@@ -68,7 +68,6 @@ Gem::Specification.new do |s|
68
68
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
69
69
  end
70
70
  else
71
- s.add_dependency(%q<crack>, [">= 0"])
72
71
  s.add_dependency(%q<crack>, [">= 0.1.8"])
73
72
  s.add_dependency(%q<jeweler>, [">= 1.4.0"])
74
73
  s.add_dependency(%q<rspec>, [">= 2.0.0.beta.22"])
@@ -10,6 +10,44 @@ describe "SimpleCurrency" do
10
10
  0.usd.to_eur.should == 0.0
11
11
  end
12
12
 
13
+ describe "operators" do
14
+
15
+ let(:today) { Time.now }
16
+
17
+ before(:each) do
18
+ mock_xavier_api(today)
19
+ end
20
+
21
+ describe "#+" do
22
+ it "adds two money expressions" do
23
+ (1.eur + 1.27.usd).should == 2
24
+ (1.27.usd + 1.eur).should == 2.54
25
+ (1.eur + 1.eur).should == 2
26
+ end
27
+
28
+ it "does not affect non-money expressions" do
29
+ (1 + 1.27).should == 2.27
30
+ (38.eur + 1.27).should == 39.27
31
+ (1.27.usd + 38).should == 39.27
32
+ end
33
+ end
34
+
35
+ describe "#-" do
36
+ it "subtracts two money expressions" do
37
+ (1.eur - 1.27.usd).should == 0
38
+ (1.27.usd - 1.eur).should == 0
39
+ (1.eur - 1.eur).should == 0
40
+ end
41
+
42
+ it "does not affect non-money expressions" do
43
+ (1 - 1.27).should == -0.27
44
+ (38.eur - 1.27).should == 36.73
45
+ (1.27.usd - 38).should == -36.73
46
+ end
47
+ end
48
+
49
+ end
50
+
13
51
  context "using XavierMedia API for exchange" do
14
52
 
15
53
  let(:today) { Time.now }
@@ -74,10 +112,12 @@ describe "SimpleCurrency" do
74
112
  expect {
75
113
  begin
76
114
  1.usd.at(the_past).to_eur
115
+ rescue NotFoundError=>e
116
+ raise e
77
117
  rescue Timeout::Error
78
118
  retry
79
119
  end
80
- }.to raise_error("404 Not Found")
120
+ }.to raise_error(NotFoundError)
81
121
 
82
122
  end
83
123
 
@@ -139,4 +179,5 @@ describe "SimpleCurrency" do
139
179
 
140
180
  end
141
181
 
182
+
142
183
  end
metadata CHANGED
@@ -5,9 +5,9 @@ version: !ruby/object:Gem::Version
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 2
9
- - 2
10
- version: 1.2.2
8
+ - 3
9
+ - 0
10
+ version: 1.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Oriol Gual
@@ -17,28 +17,13 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2010-10-14 00:00:00 +02:00
20
+ date: 2010-10-24 00:00:00 +02:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
24
- type: :runtime
25
- prerelease: false
26
24
  name: crack
27
- version_requirements: &id001 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- hash: 3
33
- segments:
34
- - 0
35
- version: "0"
36
- requirement: *id001
37
- - !ruby/object:Gem::Dependency
38
- type: :runtime
39
25
  prerelease: false
40
- name: crack
41
- version_requirements: &id002 !ruby/object:Gem::Requirement
26
+ requirement: &id001 !ruby/object:Gem::Requirement
42
27
  none: false
43
28
  requirements:
44
29
  - - ">="
@@ -49,12 +34,12 @@ dependencies:
49
34
  - 1
50
35
  - 8
51
36
  version: 0.1.8
52
- requirement: *id002
37
+ type: :runtime
38
+ version_requirements: *id001
53
39
  - !ruby/object:Gem::Dependency
54
- type: :development
55
- prerelease: false
56
40
  name: jeweler
57
- version_requirements: &id003 !ruby/object:Gem::Requirement
41
+ prerelease: false
42
+ requirement: &id002 !ruby/object:Gem::Requirement
58
43
  none: false
59
44
  requirements:
60
45
  - - ">="
@@ -65,17 +50,17 @@ dependencies:
65
50
  - 4
66
51
  - 0
67
52
  version: 1.4.0
68
- requirement: *id003
69
- - !ruby/object:Gem::Dependency
70
53
  type: :development
71
- prerelease: false
54
+ version_requirements: *id002
55
+ - !ruby/object:Gem::Dependency
72
56
  name: rspec
73
- version_requirements: &id004 !ruby/object:Gem::Requirement
57
+ prerelease: false
58
+ requirement: &id003 !ruby/object:Gem::Requirement
74
59
  none: false
75
60
  requirements:
76
61
  - - ">="
77
62
  - !ruby/object:Gem::Version
78
- hash: 62196431
63
+ hash: 3192703827556712861
79
64
  segments:
80
65
  - 2
81
66
  - 0
@@ -83,12 +68,12 @@ dependencies:
83
68
  - beta
84
69
  - 22
85
70
  version: 2.0.0.beta.22
86
- requirement: *id004
87
- - !ruby/object:Gem::Dependency
88
71
  type: :development
89
- prerelease: false
72
+ version_requirements: *id003
73
+ - !ruby/object:Gem::Dependency
90
74
  name: fakeweb
91
- version_requirements: &id005 !ruby/object:Gem::Requirement
75
+ prerelease: false
76
+ requirement: &id004 !ruby/object:Gem::Requirement
92
77
  none: false
93
78
  requirements:
94
79
  - - ">="
@@ -99,12 +84,12 @@ dependencies:
99
84
  - 3
100
85
  - 0
101
86
  version: 1.3.0
102
- requirement: *id005
103
- - !ruby/object:Gem::Dependency
104
87
  type: :development
105
- prerelease: false
88
+ version_requirements: *id004
89
+ - !ruby/object:Gem::Dependency
106
90
  name: rails
107
- version_requirements: &id006 !ruby/object:Gem::Requirement
91
+ prerelease: false
92
+ requirement: &id005 !ruby/object:Gem::Requirement
108
93
  none: false
109
94
  requirements:
110
95
  - - ">="
@@ -115,12 +100,12 @@ dependencies:
115
100
  - 0
116
101
  - 0
117
102
  version: 3.0.0
118
- requirement: *id006
119
- - !ruby/object:Gem::Dependency
120
103
  type: :development
121
- prerelease: false
104
+ version_requirements: *id005
105
+ - !ruby/object:Gem::Dependency
122
106
  name: bundler
123
- version_requirements: &id007 !ruby/object:Gem::Requirement
107
+ prerelease: false
108
+ requirement: &id006 !ruby/object:Gem::Requirement
124
109
  none: false
125
110
  requirements:
126
111
  - - ">="
@@ -131,7 +116,8 @@ dependencies:
131
116
  - 0
132
117
  - 0
133
118
  version: 1.0.0
134
- requirement: *id007
119
+ type: :development
120
+ version_requirements: *id006
135
121
  description: A really simple currency converter using XavierMedia API. It's Ruby 1.8, 1.9 and JRuby compatible, and it also takes advantage of Rails cache when available.
136
122
  email: info@codegram.com
137
123
  executables: []
@@ -154,7 +140,8 @@ files:
154
140
  - Rakefile
155
141
  - VERSION
156
142
  - autotest/discover.rb
157
- - lib/core_ext/numeric.rb
143
+ - lib/core_ext/fixnum.rb
144
+ - lib/core_ext/float.rb
158
145
  - lib/simple_currency.rb
159
146
  - lib/simple_currency/currency_convertible.rb
160
147
  - simple_currency.gemspec
@@ -166,8 +153,8 @@ homepage: http://github.com/codegram/simple_currency
166
153
  licenses: []
167
154
 
168
155
  post_install_message:
169
- rdoc_options: []
170
-
156
+ rdoc_options:
157
+ - --charset=UTF-8
171
158
  require_paths:
172
159
  - lib
173
160
  required_ruby_version: !ruby/object:Gem::Requirement