Reputation: 407
Just a quick question. I have a test specification that looks like so...
require 'user'
describe "Integration" do
let(:user) { User.new(voucher) }
context 'no voucher' do
let(:voucher) { nil }
context 'no voucher' do
let(:voucher) { nil }
it 'should bill default price all the time' do
user.bill
expect(user.orders[0].billed_for).to eql 6.95
... ...
end
end
context 'vouchers' do
describe 'default vouchers' do
let(:voucher) { Voucher.create(:default, credit: 15) }
it 'should not bill user if has a remaining credit' do
user.bill
expect(user.orders[0].billed_for).to eql 0.0
... ...
end
end
I have a very simple voucher class atm
class Voucher
attr_accessor :credit, :type
def self.create(type, *attrs)
@type = type
end
def billed_for
Random.new.rand(0..4)
end
end
Now what's weird is If I try to access the variables of the voucher class through user where it's stored it throws up and undefined variable. The object definitely isn't nil, when I check the type it tells me it's an array! Printing this produces stuff like... [{:credit=>15}][{:discount=>50, :number=>3}] Why is this happening? Have I set something wrong in my user class? My user class is below
require 'order'
require 'voucher'
class User
attr_accessor :voucher, :orders
def initialize(voucher = nil, orders =[])
@orders = orders
@voucher = voucher.nil? ? nil : voucher
end
Upvotes: 2
Views: 102
Reputation: 4982
Your voucher.create method isn't actually creating a voucher. If you check the class of the return result of Voucher.create
, you should see Symbol
:
Voucher.create(:default).class #=> Symbol
This is because you're returning the result of setting @type = type
, where type is :default
, which has a class of Symbol
. I'm not sure where the array is coming from, but in order to create a proper Voucher, you should override the initialize method, much like you're doing in the User
class:
class Voucher
def initialize(type, *attrs)
@type = type
# you should probably do something with attrs as well.
end
end
Now you can call Voucher.new
to build an initialized Voucher object:
Voucher.new(:default).class #=> Voucher
If you want to keep the same api, you can then define self.create as follows:
class Voucher
def self.create(type, *attrs)
new(type, *attrs)
end
end
Voucher.create(:default, :some, :more, :attrs).class #=> Voucher
As an aside, you don't really need that nil check in the initialize method of your User
class, the following is equivalent:
class User
def initialize(voucher = nil, orders =[])
@orders = orders
@voucher = voucher
end
end
Looking at your tests, Voucher's create method takes two parameters, a voucher type and an attributes hash.
Voucher.create(:default, credit: 15, discount: 50)
is equivalent to
Voucher.create(:default, { credit: 15, discount: 50 })
so you probably want to define Voucher's initialize method as follows:
class Voucher
def initialize(type, attrs = {})
@type = type
# iterate through the attributes hash and assign attributes appropriately
end
end
Assuming you have defined setters for each of the attribute keys you are assigning, you could do the following:
def initialize(type, attrs = {})
@type = type
attrs.each do |key, value|
send("#{key}=", value)
end
end
See documentation of send here.
Given the following line of code:
Voucher.new(:default, credit: 15, discount: 50)
The following logic would happen inside the voucher's initialize function:
@type = :default
# iterate through the hash { credit: 15, discount: 50 }
self.credit=(15) # equivalent to self.credit = 15
self.discount=(50) # equivalent to self.discount = 50
and you wind up with a properly initialized voucher object.
It is straightforward to modify your create method appropriately.
Upvotes: 1