Iggy Iggy - 2 months ago 8
Ruby Question

How to update two arguments at the same time using Ruby Observer method?

I am studying Ruby's Design Pattern and came across Observer method. I tried to customize my own observer method to help me understand it but it returns an error. Here is what I came up with:

class YummyTastyDonut
def update(changed_order)
puts "Kitchen: Yo, change the order to #{changed_order.order}!"
puts "Front: Order for #{changed_order.name}!"
puts "Front: The price will now be #{changed_order.order_price} "
end
end


class Customer
attr_reader :name, :order
attr_accessor :order_price

def initialize(name, order, order_price)
@name = name
@order = order
@order_price = order_price
@observers = []
end

def order=(new_order, new_price)
@order = new_order
@order_price = new_price
notify_observers
end

def notify_observers
@observers.each do |observer|
observer.update(self)
end
end

def add_observer(observer)
@observers << observer
end

def delete_observer(observer)
@observers.delete(observer)
end
end


If you read the book, I changed the class names, but the essence is the same. One thing I changed is
order=
method; it now accepts two arguments instead of one.

The goal is, after creating new
Customer
, I want the new customer to be able to change my order and notify
YummyTastyDonut
. However, I want to be able to update two things: the
order
and
order_price
(obviously, if I change my order, the price will also change). I want
YummyTastyDonut
to respond to my change.

igg = Customer.new("Iggy", "Bacon Donut", 10)
=> #<Customer:0x0056212e48a940 @name="Iggy", @order="Bacon Donut", @order_price=10, @observers=[]>

donut = YummyTastyDonut.new
=> #<YummyTastyDonut:0x0056212e4894c8>

## Updating my order and order_price ##

igg.order = ("yummy butter donut", 15)
#(repl):1: syntax error, unexpected ',', expecting ')'
#igg.order = ("yummy butter donut", 15)


If I use this
order=
method that accepts only one argument, it works just as expected.

def order=(new_order)
@order = new_order
notify_observers
end

igg.order = "Triple bacon donut explosion"
Kitchen: Yo, change the order to Triple bacon donut explosion!
Front: Order for Iggy!
Front: The price will now be 10


What do I have to change so I can update both
order
and
order_price
simultaneously?

Answer

Assignment = is meant to accept a single argument to be assigned.(or the same number of elements as variables in the event of parallel assignment)

You could do this as:

def order=(args)
  @order = args[0]
  @order_price = args[1]
  notify_observers
end

Then call like

igg.order=["yummy butter donut", 15]

or user an options Hash as @BartekGladys suggested but both solutions seem awful and fallible to say the least and would require knowledge of the internals of the method in order to utilize it correctly.

Also currently even if this method worked it would be extremely confusing to future you or anyone looking at your code base because you have an instance variable @order so the assumption would be order=(val) would assign that variable.

You would be better off with a method called change_order that accepts order and order_price and this would eliminate all confusion as to whats happening.

def change_order(new_order, new_price)
    @order = new_order
    @order_price = new_price
    notify_observers
end

and then reading it makes more sense too.

igg = Customer.new("Iggy", "Bacon Donut", 10)
#Currently seems like it is assigning order to an Array maybe? 
igg.order=("yummy butter donut", 15)
#New much clearer as to the implementation and purpose of changing an order
igg.change_order("yummy butter donut", 15)  
Comments