07. Трето домашно и case

07. Трето домашно и case

07. Трето домашно и case

7 ноември 2011

Днес

Утре

Регулярни изрази

Преди това

Вместо четвърта задача

watchr

$ gem install watchr

watch('solution.rb') do
  system 'rspec --require ./solution sample_spec.rb'
end

Как решаваме втора задача

с watchr

until spec_passing?
  write_code
  save
  look_at :watchr_output
end

Решение на трета задача

Преди това:

Какви проблеми срещнахте?

Какво очаквахме от вас

Моето решение

Следва моето решение, на части.

Inventory#initialize

class Inventory
  def initialize
    @products = []
    @coupons  = []
  end

  # ...
end

Inventory#register

def register(name, price, options = {})
  price     = price.to_d
  promotion = Promotions.for options

  @products << Product.new(name, price, promotion)
end

Inventory#new_cart

class Inventory
  def new_cart
    ShoppingCart.new self
  end
end

class Product

class Product
  def initialize(name, price, promotion)
    raise "Name should be at most 40 characters" unless name.length <= 40
    raise "Only prices between 0.01 and 999.99 allowed" unless 0 < price and price < 1000

    @name      = name
    @price     = price
    @promotion = promotion
  end

  # ...
end

class Product

мотивация

ShoppingCart#initialize

class ShoppingCart
  attr_reader :items, :coupon

  def initialize(inventory)
    @inventory  = inventory
    @items      = []   # инстанции на LineItem
    @coupon     =      # засега няма купон
  end

ShoppingCart#add

def add(product_name, count = 1)
  product = @inventory[product_name]
  item    = @items.detect { |item| item.product == product }

  if item
    item.count += count
  else
    @items << LineItem.new(product, count)
  end
end

Inventory#[]

def [](name)
  @products.detect { |product| product.name == name } or raise 'Unexisting product'
end

Нищо сложно.

LineItem#initialize

class LineItem
  attr_reader :product
  attr_accessor :count

  def initialize(product, count)
    @product = product
    @count   = 0

    increase count
  end
end

LineItem#increment

class LineItem
  def increase(count)
    raise 'You have to add at least one item' if count <= 0
    raise 'Maximum 99 items of each product can be bought' if count + @count > 99
    @count += count
  end
end

Не е най-интересния метод.

ShippingCart#total_price

class ShoppingCart
  def total
    items_price - coupon_discount
  end

  def items_price
    @items.map(&:price).inject(&:+)
  end

  # ...
end

LineItem#price

class LineItem
  def price()
    price_without_discount - discount
  end

  def price_without_discount
    product.price * count
  end

  def discount
    product.discount_for(count)
  end
end

LineItem#price (2)

class LineItem
  def price()       price_without_discount - discount  end
  def price_without_discount()  product.price * count  end
  def discount()          product.discount_for(count)  end
end # Пример за лошо подравняване

Product#discount_for

def discount_for(count)
  if @promotion
    @promotion.discount(count, @price)
  else
    0
  end
end

Промоции

class GetOneFree

class GetOneFree
  def initialize(nth_item_free)
    @nth_item_free = nth_item_free
  end

  def discount(count, price)
    (count / @nth_item_free) * price
  end

  def name
    "buy #{@nth_item_free - 1}, get 1 free"
  end
end

Null Object pattern

Да се върнем на if-a:

def discount_for(count)
  if @promotion
    @promotion.discount(count, @price)
  else
    0
  end
end

Null Object pattern (2)

Просто създайте клас NoPromotion.

class NoPromotion
  def discount(count, price)
    0
  end

  def name
    ''
  end
end

Останалите неща

case

В Ruby има "switch". Казва се case.

def quote(name)
  case name
    when 'Yoda'
      puts 'Do or do not. There is no try.'
    when 'Darth Vader'
      puts 'The Force is strong with this one.'
    when 'R2-D2'
      puts 'Beep. Beep. Beep.'
    else
      puts 'Dunno what to say'
  end
end

case

особености

case

стойности

case не сравнява с ==. Може да напишете следното.

def qualify(age)
  case age
    when 0..12
      'still very young'
    when 13..19
      'a teenager! oh no!'
    when 33
      'the age of jesus'
    when 90..200
      'wow. that is old!'
    else
      'not very interesting'
  end

case

Object#===

case сравнява с ===. Няколко класа го имплементират.

def qualify(thing)
  case thing
    when Integer then 'this is a number'
    when String then 'it is a string'
    when Array then thing.map { |item| qualify item }
    else 'huh?'
  end
end

Въпроси