u16suzuの blog

u16suzu のブログです。

Rubyでデザインパターン ~Stateパターン~

RubyでStateパターンを実装してみるテスト。

Stateのコンクリートクラスにメソッドの実装を生やすことで、Stateを新しく追加するときに追加しやすくしている。

クラスマクロもちょろっと使ってみたり。

でもクラスマクロはコードが短くなるのはいいけど、初見の人には読みづらいと思うんだよなぁ。

class Class
  def subclasses
    subclasses = []
    ObjectSpace.each_object(singleton_class) do |k|
      subclasses << k if k.superclass == self
    end
    subclasses
  end
end
class State
  @_time
  def self.time(time=nil) # 引数がnilの時はgetter, nilでない時はsetterになる
    @_time ||= time
  end
  def time # インスタンスメソッドも一応定義しておく
    self.class.instance_variable_get("@_time")
  end
  def hello
    raise
  end
end
class MorningState < State
  time 0..9
  
  def hello
    puts "Good morning!"
  end
end
class DayState < State
  time 10..15
  
  def hello
    puts "Hello!"
  end
end
class EveningState < State
  time 16..18

  def hello
    puts "Good evening."
  end
end
class NightState < State
  time 19..24
  
  def hello
    puts "Good night."
  end
end
class StateManager
  attr_accessor :state, :time
  def initialize(state=MorningState.new)
    @state = state
  end
  def say_hello
    @state.hello
  end
  def time=(time)
    @time = time
    State::subclasses.each do |k|
      if k.time.include?(@time)
        @state = k.new
        break
      end
    end
  end
end


mgr = StateManager.new
mgr.say_hello
(0..24).to_a.each do |i|
  mgr.time = i
  mgr.say_hello
end

ORDER BY 狙いのキーについて動作確認メモ

こちらのスライドで説明されていることの理解を深めるために動作確認してみました。

www.slideshare.net

# 実験用に bulk insert でデータを入れる

values_1 = (1..9990).to_a.map do |_|
  "(#{rand(80)}, 1)"
end.join(",  ")


values_2 = (1..10).to_a.map do |_|
  "(#{rand(80)}, 0)"
end.join(",  ")

[values_1, values_2].each do |v|
  system "mysql -u root spike -e 'INSERT INTO `users` ( `age`, `gender`) VALUES #{ v };'"
end

puts "success!"
EXPLAIN SELECT *
FROM users
WHERE gender = 1 ORDER BY age LIMIT 5 # 9990件
# WHERE gender = 0 ORDER BY age LIMIT 5 # 10件

# ORDER BY 狙いのインデックス idx_users__age が使われている
# id    select_type table   type    possible_keys   key key_len ref rows    Extra
# 1    SIMPLE  users   INDEX   idx_users__gender   idx_users__age  4  NULL    5  USING WHERE

# WHERE 狙いのインデックス idx_users__gender が使われている
# * id  select_type TABLE   TYPE    possible_keys   KEY key_len ref ROWS    Extra
# 1    SIMPLE  users   ref idx_users__gender   idx_users__gender   1  const   10 USING WHERE; USING filesort
;

Bundlerのエラー設計のメモ

  • Bundlerのエラー設計メモ
module Bundler
  class BundlerError < StandardError
    def self.status_code(code)
      define_method(:status_code) { code }

      if match = BundlerError.all_errors.find {|_k, v| v == code }
        error, _ = match
        raise ArgumentError,
          "Trying to register #{self} for status code #{code} but #{error} is already registered"
      end

      # 全てのエラーcodeリストを BundlerError に保持している. 上の部分で、同じ error code を登録しないためにそうしている。
      BundlerError.all_errors[self] = code
    end

    def self.all_errors
      @all_errors ||= {}
    end
  end

  class GemfileError < BundlerError; status_code(4); end
  class InstallError < BundlerError; status_code(5); end
  # message定義するケース
  class NoSpaceOnDeviceError < PermissionError
    def message
      "There was an error while trying to #{action} `#{@path}`. " \
      "There was insufficient space remaining on the device."
    end

    status_code(31)
  end
end
  • 例外をraiseするところ
raise BundlerError, "Unknown user path requested: #{dir}"
  • 例外ハンドリング周り
rescue GemfileNotFound
  bundle_dir = default_bundle_dir
  raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir
  Pathname.new(File.expand_path("..", bundle_dir))
end

2018/1/8(mon) ホットサンドメーカーの使い方が上達した

ホットサンドメーカーで毎朝トーストを焼いているが、今日はとても上手く焼くことができた。 あんこを挟んでバターを乗せて小倉ホットサンドを作って見た。なかなかの味だった。

f:id:u16s:20180108150815j:plain:w300 f:id:u16s:20180108184041j:plain:w300

夜に妹から電話があり、姪に送ったモンテッソーリのおもちゃであるところのシリンダーというやつが届いたという連絡があった。 なかなか巨大なやつで、ブナの木でできており品質は良さそうで、大人からしてもスポット入り込んで行く感覚がとても気持ち良いそう。俺もやってみたい。

f:id:u16s:20180109002333p:plain:w600

明日からは久しぶりの仕事。今回は12/28~1/8までの12連休だった。かなりリフレッシュできたので明日から仕事頑張って行くぞという気持ち。

2017/12/31-2018/1/1,2,3,4,5 久々に名古屋に帰っていた

年末風邪気味だったのが徐々に落ち着いてきた頃に、名古屋に帰った。 RubyKaigiが開催された大須がとても近かったので、観光と初詣がてら行ってみたら、予想をはるかに超えた人の波でかなり混雑していた。 イメージ的には仙台の商店街をイメージしていたがそれよりもはるかに大須は巨大だった。大通りが複数あって、さらに脇道にも小さな商店や屋台がポツポツと軒を連ねており、近年ではグルメブームもあって食べ歩きの町として売り出しているらしい。

f:id:u16s:20180103130826j:plain:w300

写真はパオパオ亭の焼き包子(パオズ) 確かマツコの知らない世界で放送されていて、是非一度食べてみたかったやつ。噂通りの肉汁が出てきてとても美味しかった。ここは毎年のリピート決定。お土産で買って帰っても、フライパンで温め直してアツアツで食べることができて、みんなに美味しいととても喜ばれた。 1個160円なのでもっと多めに買って行ってもよかったかもしれない。

f:id:u16s:20180103132504j:plain:w300

からあげ彩音で食べた唐揚げもボリューミーで味が濃くて美味しかった。これでたったの500円。これをおかずにご飯を食べたかった。 真っ赤な唐辛子がかかっているけどそんなには辛くない。

f:id:u16s:20180103141952j:plain:w300

他には喫茶店やドックカフェに行ったり、コンパルのエビカツサンドを食べた。エビカツサンドは手羽先などに隠れてあまり目立たない名古屋飯かもしれないが、名古屋駅のすぐ近くのメイチカで買えてかつ、しっかり美味しいのでかなりおすすめ。 [コンパル メイチカ店](https://tabelog.com/en/aichi/A2301/A230101/23006112/

あとは、中古の宝石を扱うコメ兵というローカルチェーン店に行ってみた。興味本位で見ていると、宝石でも結構安いのが多くて、妻に後でそのことを話したら安いならば買ってきてくれればよかったのにと言われて、「余計なことを言ってしまった」という感じだった。

f:id:u16s:20180102110841j:plain:w300

2018/1/7 オブスキュラに久しぶりに行った

近くにある馴染みの神社である池尻稲荷に初詣に行ってきて、おみくじを引いたら大吉だった。おそらく人生初の大吉ではないかと思う。出産が安産だったのが何より。相場に山気を出すなと書いてあって、これは今年は仮想通貨には手を出さずにおとなしく投信でも買っておきなさいという神の啓示なのかもしれない。

学問に関心手は安心して励めという内容で、昨年からコツコツとよく使うツール周りの内部動作がどうなっているか知りたくて、時間のあるときにコードを追っている。普段何気なく使っているツール類でも内部挙動がどうなっているかを知っておくと安心感がある。こういう部分が、何か問題があった時の対応力につながってくると思う。

f:id:u16s:20180108162443j:plain:w300

その後、去年のお札を奉納した後に、あのフレッシュでとろけるようなレアチーズケーキが久しぶりに食べたくなって、三茶のカフェのマメヒコに行ったらあいにくの臨時休業だったので、久しぶりにオブスキュラまで足を伸ばした。酸味のあるあっさりしたコーヒーと、濃厚なチーズケーキをいただいた。

あるオブジェクトに対して1回で複数のメソッドを呼ぶ

書いて見た。jbuilderっぽい見た目になった。

class Object
  def call(*methods)
    methods.map do |method|
      self.send(method)
    end
  end
end

p [1,2,3].(:first, :last, :class) # => [1, 3, Array]