1

I am trying to implement a currency based application using Ruby, I find that:

  require 'dollar'
  require 'franc'

 puts 'Why does this not work?'

 class Money

   attr_reader :amount

   def self.dollar(number)
     Dollar.new number
   end

   def ==(other)
     self.amount == other.amount
   end 

   def self.franc(number)
     Franc.new(number)
   end

end

I have the Franc class that looks like this:

require 'money'

class Franc < Money

  attr_reader :amount

  def initialize(amount)
    @amount = number
  end

  def times(mul)
    amount = @amount * mul
    Franc.new(amount)
  end

  def ==(other)
    return false unless other.is_a? self.class
    super
  end

end

This is a direct translation of some of the code from the Kent Beck book to Ruby. When I run bin/rspec I see:

/home/vamsi/Do/wycash/lib/franc.rb:3:in `<top (required)>': uninitialized constant Money (NameError)
    from /home/vamsi/Do/wycash/lib/money.rb:2:in `require'
    from /home/vamsi/Do/wycash/lib/money.rb:2:in `<top (required)>'
    from /home/vamsi/Do/wycash/lib/dollar.rb:1:in `require'
    from /home/vamsi/Do/wycash/lib/dollar.rb:1:in `<top (required)>'
    from /home/vamsi/Do/wycash/spec/dollar_spec.rb:1:in `require'
    from /home/vamsi/Do/wycash/spec/dollar_spec.rb:1:in `<top (required)>'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `load'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1435:in `block in load_spec_files'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `each'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/configuration.rb:1433:in `load_spec_files'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:100:in `setup'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:86:in `run'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:71:in `run'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/lib/rspec/core/runner.rb:45:in `invoke'
    from /home/vamsi/Do/wycash/.bundle/ruby/2.3.0/gems/rspec-core-3.5.4/exe/rspec:4:in `<top (required)>'
    from bin/rspec:17:in `load'
    from bin/rspec:17:in `<main>'
vamsiampolu
  • 6,328
  • 19
  • 82
  • 183

2 Answers2

2

I was going to add this as a comment when I realised it would start getting too lengthy.

When you say you have translated directly from Kent Beck's book I assume you are referring to his TDD by Example book (it's the only book I can find of his that references the currency example). I can't actually find an example in that book however that refers to cyclic dependancy, however from previous experience with Java and C++ you normally try to break up a cyclic dependancy by implementing an interface - for Ruby, you might want to refer to the following SO question which has good answers to this problem:

Circular Dependancies in Ruby

Having said that I think your design is broken. Your Money class should be defining the generic behaviour and attributes for all Money types. It should know nothing about Franc's or Dollars, etc... Specific behaviour for Franc should be encapsulated entirely within the Franc class. Either use a single class to handle all the currencies or use inheritance - it doesn't make much sense to do both.

Community
  • 1
  • 1
David
  • 3,510
  • 3
  • 21
  • 39
1

You should put require 'franc' after you defined Money in your first script.

The class Money will be then defined when Ruby executes your second_script.

EDIT: Circular require isn't an issue :

# a.rb
puts "A"
require_relative 'b'

# b.rb
puts "B"
require_relative 'a'

# ruby a.rb
A
B
A

Replacing require_relative with load './b.rb' would result in an infinite loop and "stack level too deep" Error.

Still, you should probably define Money in money.rb, Franc in franc.rb, ... and not put anything about Franc in Money.

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124