quattro_4 scribble

scribble 落書き (調べた事をただ落書きする)

RubyTapas #194 - #198

194 String Format

"Average high: %.1f, low: %.1f" % [avg_hi, avg_lo]
# => "Average high: 49.7, low: 25.4"

format, sprintfも同じ

  • %.1f
  • %d
  • %x -> 17 (hex)
    • %#x -> 0x17
    • %#X -> 0X17
  • %o
    • %#o -> 027
  • %b
    • %#b -> 0B10111
  • %e -> 2.400000e+19
  • %g
    • 123.0 -> 123
    • 2.4e19 -> 2.4e+19
  • %p (inspect)
  • %% -> escape '%'

195 Advanced String Formats

★★

temperature listing code

padding with space

puts "x: % d, y: % d\nx: % d, y: % d" % [123, 456, -789, 321]

長さを渡す、hashを渡す

width = -4
DAYS.each_with_index do |day, i|
  puts "%-9s High: %#{width}d Low: %#{width}d" % [day, C(HIGHS[i]), C(LOWS[i])]

DAYS.each_with_index do |day, i|
  values = {day: day, high: C(HIGHS[i]), low: C(LOWS[i])}
  puts "%<day>-9s High: %<high>3d Low: %<low>3d" % values

printf "my %{vehicle} is full of %{animal}", vehicle: "hovercraft", animal: "eels"

使い方と存在を最近は忘れてた

196 String Templates

★★

evalするから良くない

class MyLogger
  attr_accessor :formatter

  def initialize
    @formatter = ->(data) {
      "#{data[:severity]} #{data[:time]} #{data[:host]} #{data[:pid]} #{data[:message]}"
    }
  end

  def error(message)
    data = {
      message:  message,
      time:     Time.now,
      host:     Socket.gethostname,
      pid:      $$,
      severity: "ERROR"
    }
    puts formatter.call(data)
  end
end

logger = MyLogger.new
logger.formatter = ->(data) { "#{data[:severity][0]} #{data[:message]}" }
logger.error "Bogosity increasing!"
# >> E Bogosity increasing!

format使う セキュリティなといろいろな面で良い、分かり易い

class MyLogger
  attr_accessor :format

  def initialize
    @format = '%<severity>s %<time>s %<host>s %<pid>s %<message>s'
  end

  def error(message)
    data = {
      message:  message,
      time:     Time.now,
      host:     Socket.gethostname,
      pid:      $$,
      severity: "ERROR"
    }
    printf format, data
  end
end

logger = MyLogger.new
logger.format = "%<severity>.1s %{message}"
logger.error "Klingons off the starboard bow!"
# >> E Klingons off the starboard bow!

197 Decorator

object should have a single responsibility

require 'delegate'
class AuditedAccount < SimpleDelegator
  def initialize(account, audit_log)
    super(account)
    @audit_log = audit_log
  end

  def deposit(amount)
    super
    @audit_log.record(number, :deposit, amount, balance_cents)
  end

  def withdraw(amount)
    super
    @audit_log.record(number, :withdraw, amount, balance_cents)
  end
end
require "./bank_account"
require "./audited_account"
require "./audit_log"

account = BankAccount.new(123)
account = AuditedAccount.new(account, AuditLog.new)
account.deposit(1000)
account.withdraw(500)
# >> 2014-03-25 19:55:48 -0400 #123 deposit 10.00 (10.00)
# >> 2014-03-25 19:55:48 -0400 #123 withdraw 5.00 (5.00)

Audited bank accounts are responsible for adding audit logging to a bank account, without worrying about how to maintain a balance.

それぞれのクラスがsingle responsibilityを守る

198 Decorator Transparency

★★

returnがnil

def withdraw(amount)
  super
  @audit_log.record(number, :withdraw, amount, balance_cents)
end

値を返す

def withdraw(amount)
  new_balance = super
  @audit_log.record(number, :withdraw, amount, balance_cents)
  new_balance
end

def withdraw(amount)
  super.tap do
    @audit_log.record(number, :withdraw, amount, balance_cents)
    new_balance
  end
end

これでも最後に一行追加するだけで壊れてしまう

最初のnotifyの部分を修正した方が良い

def withdraw_and_notify(customer, account, amount)
  new_balance = account.withdraw(amount)
  customer.notify("Your new balance is %.2f" [new_balance / 100.0])
enddef withdraw_and_notify(customer, account, amount)
  account.withdraw(amount)
  new_balance = account.balance_cents
  customer.notify("Your new balance is %.2f" [new_balance / 100.0])
end

そもそもwithdrawの返り値を使わないようにする withdrawして、その後金額を読み込む