Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What a hard work to make the recipe sharing service available on Ruby 1.9.3!

What a hard work to make the recipe sharing service available on Ruby 1.9.3!

This is presented in oedorubykaigi03

Kenta Murata

March 16, 2013
Tweet

More Decks by Kenta Murata

Other Decks in Technology

Transcript

  1. ๭ϨγϐαΠτͷ Ruby 1.9.3 ରԠͰۤ࿑͠·ͨ͠
    mrkn
    Kenta Murata
    What a hard work to make the recipe
    sharing service available on Ruby 1.9.3!
    2013.03.16 #odrk03
    1

    View Slide

  2. Kenta Murata (mrkn)
    DevInfra Engineer
    at COOKPAD Inc.
    Ruby committer
    http://www.flickr.com/photos/kakutani/8526732201/
    2

    View Slide

  3. 3

    View Slide

  4. Kenta Murata (mrkn)
    DevInfra Engineer
    at COOKPAD Inc.
    Ruby committer
    http://www.flickr.com/photos/kakutani/8526732201/
    4

    View Slide

  5. Kenta Murata (mrkn)
    DevInfra Engineer
    at COOKPAD Inc.
    Ruby committer
    http://www.flickr.com/photos/kakutani/8526732201/
    BATMAN
    4

    View Slide

  6. 5

    View Slide

  7. 5

    View Slide

  8. Run on 1.9.3-p392 since 26 Feb 2013
    5

    View Slide

  9. Run on 1.9.3-p392 since 26 Feb 2013
    We’ll move to 2.0.0-p0 next month !!!
    5

    View Slide

  10. REE 1.9.3 2.0.0
    809
    576
    497
    REE 1.9.3 2.0.0
    1773
    1305
    1166
    ฏۉϨεϙϯελΠϜ [ms] 90% Line [ms]
    6

    View Slide

  11. http://info.cookpad.com/
    7

    View Slide

  12. 8

    View Slide

  13. http://bit.ly/cookpad_jobs
    8

    View Slide

  14. 1.9.3 ҠߦͰ
    ۤ࿑ͨ͠ࣄ
    9

    View Slide

  15. Ϛδίϝ
    then/do ͷ୅ΘΓͷ :
    Πϯελϯεม਺ΛϒϩοΫύϥϝʔλʹ࢖͏ͳ
    Temp le ͷΦʔϓϯϞʔυ͸ ‘w+’
    Hash ͷॱং໰୊
    Array#to_s ͱ Hash#to_s
    (time .. time).include? time
    Date#step with ActiveSupport::Duration
    enum_with_index
    lambda ͷҾ਺Ϛονϯά
    next Ͱ͸ϝιου͔Βൈ͚ΒΕͳ͍Α
    ଟॏ୅ೖ
    Symbol#to_int ͷͪΐͬͱྑ͍࿩
    ਖ਼نදݱͷඇޓ׵ੑ
    nkf, kconv, jcode, iconv
    ΦϦδφϧ String#blank?
    ActiveRecord ͱ nil.id
    ϝʔϧ
    10

    View Slide

  16. # coding: utf-8
    11

    View Slide

  17. ౰࣌͸ΈΜͳॻ͍ͯ͘Εͳ͔ͬͨ
    12

    View Slide

  18. script/insert_magic_comment
    13

    View Slide

  19. #! /usr/bin/env ruby
    # coding: utf-8
    def has_magic_comment?(content)
    if content.lines.first =~ /\A#!/
    first_line = content.lines.take(2).last
    else
    first_line = content.lines.first
    end
    comment = first_line.sub(
    /(?:\"(?:[^"]|\\\")*\"|\'(?:[^']|\\\')*\'|[^#]*)#/, '').strip
    comment =~ /\b(?:en)?coding\s*:\s*(?:utf|UTF)-?8\b/
    end
    def insert_magic_comment(path)
    content = open(path, 'rb') {|io| io.read } rescue $!
    return if Exception === content || content.empty?
    content.force_encoding('BINARY') if content.respond_to?(:force_encoding)
    unless has_magic_comment?(content)
    if content =~ /[^\x00-\x7E]/m
    $stderr.puts "inserting magic comment to #{path}"
    open(path, 'wb') do |io|
    io.puts "# coding: utf-8"
    io.write content
    end
    end
    end
    end
    if ARGV[0] == '--pre-commit'
    open("|git diff --cached --name-only HEAD") do |io|
    while path = io.gets
    path.strip!
    next unless path =~ /\.rb$/
    insert_magic_comment(path)
    end
    end
    else
    require 'find'
    Find.find(Dir.pwd) do |path|
    next unless path =~ /\.rb$/
    insert_magic_comment(path)
    end
    end
    14

    View Slide

  20. https://gist.github.com/
    mrkn/5173137
    http://bit.ly/cookpad_jobs
    15

    View Slide

  21. if condition:
    when condition:
    while condition:
    16

    View Slide

  22. items.each do |@item|
    17

    View Slide

  23. view template ಺Ͱࢄݟ
    18

    View Slide

  24. <%# index.html.erb %>

    <% @items.each_with_index do |@item, idx| -%>

    <%= render ‘item’ %>

    <% end -%>

    <%# _item.html.erb %>

    name<%= @item.title %>
    description<%= @item.description %>
    ...

    19

    View Slide

  25. ϨϯμϦϯά͞ΕΔ·Ͱ
    syntax error ͕஗Ԇ͢Δ
    20

    View Slide

  26. Ͳͷ partial view ͔
    Βࢀর͞ΕͯΔ͔ௐ΂
    Δͷ͕େมͳͷͰ୯७
    ʹ @ ΛऔΔͱࢮ͵
    21

    View Slide

  27. શͯෛ࠴Ͱஔ͖׵͑ͨ
    22

    View Slide

  28. <%# index.html.erb %>

    <% @items.each_with_index do |item, idx| @item = item -%>

    <%= render ‘item’ %>

    <% end -%>

    <%# _item.html.erb %>

    name<%= @item.title %>
    description<%= @item.description %>
    ...

    23

    View Slide

  29. Tempfile ͷΦʔϓϯ
    Ϟʔυ͕ ‘w+’ ݻఆ໰୊
    24

    View Slide

  30. default_external ͸ UTF-8
    25

    View Slide

  31. ςϯϙϥϦϑΝΠϧʹ
    JPEG Λॻ͖ࠐΊͳ͍
    26

    View Slide

  32. ͓ྉཧͷࣸਅΛอଘ
    Ͱ͖ͳ͍ʼʻ
    27

    View Slide

  33. tf.set_encoding(‘BINARY’) \
    ! if tf.respond_to? :set_encoding
    28

    View Slide

  34. Hash ͷॱং໰୊
    29

    View Slide

  35. # ruby-1.8.7 ͷ৔߹
    {:a => 1, :b => 2}.to_a
    #=> [[:b, 2], [:a, 1]]
    # ruby-1.9.3 ͷ৔߹
    {:a => 1, :b => 2}.to_a
    #=> [[:a, 1], [:b, 2]]
    30

    View Slide

  36. Hash ͷ map ʹΑΔ݁
    ՌΛݕূ͢Δςετͱ͔
    31

    View Slide

  37. to_s ͷڍಈͷมԽ
    32

    View Slide

  38. Array#to_s ͕
    join(“”) ૬౰ͩͬͨ
    33

    View Slide

  39. # ruby-1.8.7 ͷ৔߹
    %w(a b c).to_s #=> “abc”
    # ruby-1.9.3 ͷ৔߹
    p %w(a b c).to_s #=> "[\"a\", \"b\", \"c\"]"
    34

    View Slide

  40. # ruby-1.8.7 ͷ৔߹
    [:foo].to_s #=> “foo”
    [:foo].first.to_s #=> “foo”
    # ruby-1.9.3 ͷ৔߹
    [:foo].to_s #=> “[:foo]”
    [:foo].first.to_s #=> “foo”
    35

    View Slide

  41. ໌࣏తʹ to_s ΛݺΜ
    ͰΔίʔυͳΜͯͦ
    Μͳʹͳ͍
    36

    View Slide

  42. # ruby-1.8.7 ͷ৔߹
    “#{[:foo]}” #=> “foo”
    “#{[:foo].first}” #=> “foo”
    # ruby-1.9.3 ͷ৔߹
    “#{[:foo]}” #=> “[:foo]”
    “#{[:foo].first}” #=> “foo”
    37

    View Slide

  43. Hash#to_s ͕
    to_a.join(“”) ૬౰ͩͬͨ
    38

    View Slide

  44. # ruby-1.8.7 ͷ৔߹
    p({:a => 1, :b => 2}.to_s)
    #=> “b2a1”
    # ruby-1.9.3 ͷ৔߹
    p({:a => 1, :b => 2}.to_s)
    #=> "{:a=>1, :b=>2}"
    39

    View Slide

  45. (time .. time).include? time
    40

    View Slide

  46. class Range
    def include_with_warn?(obj)
    if Time === self.begin
    caller.tap do |callstack|
    repository_root = File.expand_path(
    '../../../../../../../', __FILE__) + '/'
    offending_line = callstack.find {|line|
    File.expand_path(line.split(':').first).
    start_with?(repository_root)
    } || callstack.first
    $stderr.puts "[WARN] can't iterate from Time
    since 1.9 at #{offending_line}"
    end
    end
    include_without_warn?(obj)
    end
    alias include_without_warn? include?
    alias include? include_with_warn?
    end
    41

    View Slide

  47. Date#step ͕
    ActiveSupport::Duration Λ
    ڋઈͩ͢͠
    42

    View Slide

  48. # Ruby 1.9.3 Ͱಈ͔ͳ͘ͳͬͨίʔυ
    begin_date = Date.parse(“2013/03/01”)
    end_date = Date.parse(“2013/06/30”)
    begin_date.step(end_date, 1.week) do |date|
    ...
    end
    43

    View Slide

  49. require 'date'
    class Date
    def step_with_warn(*args, &block)
    unless Numeric === args[1] || args[1].nil?
    $stderr.puts "\n[WARN] non-Numeric object is
    given for the 2nd argument of step at #{caller[0]}"
    $stderr.flush
    end
    step_without_warn(*args, &block)
    end
    alias step_without_warn step
    alias step step_with_warn
    end
    44

    View Slide

  50. enum_with_index
    45

    View Slide

  51. each_with_index
    46

    View Slide

  52. lambda ͷҾ਺Ϛονϯά
    47

    View Slide

  53. # ApplicationHelper ͷத
    def init_handler(foo)
    Hash.new {|h, k|
    h[k] = lambda {}
    }.merge(foo)
    end
    # ΞϓϦέʔγϣϯίʔυ಺
    init_handler(@handlers)
    ...
    @handlers[:foo].call(model)
    48

    View Slide

  54. wrong number of
    arguments (1 for 0)
    49

    View Slide

  55. # ApplicationHelper ͷத
    def init_handler(foo)
    Hash.new {|h, k|
    h[k] = lambda {|*args| }
    }.merge(foo)
    end
    # ΞϓϦέʔγϣϯίʔυ಺
    init_handler(@handlers)
    ...
    @handlers[:foo].call(model)
    50

    View Slide

  56. # ApplicationHelper ͷத
    def init_handler(foo)
    Hash.new {|h, k|
    h[k] = proc {}
    }.merge(foo)
    end
    # ΞϓϦέʔγϣϯίʔυ಺
    init_handler(@handlers)
    ...
    @handlers[:foo].call(model)
    51

    View Slide

  57. ࢿྉ࡞ͬͯͯࢥͬͨ
    52

    View Slide

  58. # null_proc.rb
    module NullProc
    class << self
    def call(*args); end
    alias [] call
    end
    end
    # ApplicationHelper ͷத
    require ‘null_proc’
    def init_handler(foo)
    Hash.new {|h, k|
    h[k] = NullProc
    }.merge(foo)
    end
    53

    View Slide

  59. next Ͱϝιου͔Β
    ൈ͚Εͳ͘ͳͬͨ
    54

    View Slide

  60. class FooController
    def show
    ...
    next if params[:bar].blank?
    ...
    end
    end
    55

    View Slide

  61. ࣮૷Λ Chanko ͔Β
    ίϯτϩʔϥຊମ΁Ҡ
    ಈ͢Δͱ͖ʹى͖Δ
    56

    View Slide

  62. Chanko ͸ Rails Ξ
    ϓϦέʔγϣϯΛ؆
    ୯ʹ֦ு͢Δ࢓૊Έ
    57

    View Slide

  63. ֦ுػೳ͸ϒϩοΫ
    Ͱ࣮૷͢Δ
    58

    View Slide

  64. Chanko ͷதͰ࣮૷͞
    ΕͯΔؒ͸ɺ֦ுػೳ
    ͷத͔Β୤ग़͢Δͱ͖
    ͸ next Λ࢖͏
    59

    View Slide

  65. ࣮૷Λ Chanko ͔Β
    ίϯτϩʔϥຊମ΁Ҡ
    ಈ͢Δͱ͖ʹ return
    ʹஔ׵͠๨ΕΔ
    60

    View Slide

  66. ଟॏ୅ೖ
    61

    View Slide

  67. # Ruby 1.8.7
    lambda {|*a| b = *a }.call(1)
    #=> 1
    # Ruby 1.9.3
    lambda {|*a| b = *a }.call(1)
    #=> [1]
    62

    View Slide

  68. ΧϯϚΛ෇͚Ε͹ྑ͍
    63

    View Slide

  69. # Ruby 1.8.7
    lambda {|*a| b, = *a; b }.call(1)
    #=> 1 ↑
    # Ruby 1.9.3
    lambda {|*a| b, = *a; b }.call(1)
    #=> 1 ↑
    64

    View Slide

  70. Symbol#to_int
    65

    View Slide

  71. # Ruby 1.8.7
    {:foo => “hello world”}[:foo][:bar]
    #=> nil
    # Ruby 1.9.3
    {:foo => “hello world”}[:foo][:bar]
    #=> can't convert Symbol into Integer (TypeError)
    66

    View Slide

  72. Ruby 1.9 Ͱ
    Symbol#to_int ͕ফ
    ͓͔͑ͨ͛Ͱόά͕
    ໌Β͔ʹʂʂ̍
    67

    View Slide

  73. ྑ͍࿩
    68

    View Slide

  74. ਖ਼نදݱͷඇޓ׵
    69

    View Slide

  75. \s ͷҙຯ (ޙड़)
    70

    View Slide

  76. \w ͱ \W ͱ
    \p{Word}
    71

    View Slide

  77. \w ͕ US-ASCII ͷൣ
    ғʹมΘͬͨ
    72

    View Slide

  78. \p{Word} ͸ 1.8 Ͱਖ਼
    نදݱίϯύΠϧΤ
    ϥʔʹͳΔ
    73

    View Slide

  79. \w → \p{Word}
    \W →ɹ ???? ɹ
    74

    View Slide

  80. if RUBY_VERSION >= ‘1.9’
    word = ‘\p{Word}’
    re = /(?!#{word})./
    else
    re = /\W/
    end
    75

    View Slide

  81. ಠࣗͷ
    String#blank?
    76

    View Slide

  82. class String
    def blank?
    self =~ /\A[\sɹ]*\z/m
    end
    end
    ↑ IDEOGRAPHIC SPACE
    77

    View Slide

  83. ໰୊͸ IDEOGRAPHIC SPACE
    ͷํ͡Όͳͯ͘ \s ʹ͋Δ
    78

    View Slide

  84. # for 1.8
    class String
    UTF8_WHITESPACE_CHAR_PATTERN =
    /(?:\s|\xE3\x80\x80)/.freeze
    UTF8_BLANK_PATTERN =
    /\A(?:#{UTF8_WHITESPACE_CHAR_PATTERN})*\z/.freeze
    end
    # for 1.9.3
    class String
    NON_BLANK_SPACE_CHARACTERS =
    "\u00A0\u1680\u180E\u2000-\u200B\u202F\u205F".freeze
    private_constant :NON_BLANK_SPACE_CHARACTERS
    UTF8_WHITESPACE_CHAR_PATTERN =
    /(?:(?![#{NON_BLANK_SPACE_CHARACTERS}])\p{Space})/.freeze
    UTF8_BLANK_PATTERN =
    /\A(?:#{UTF8_WHITESPACE_CHAR_PATTERN})*\z/.freeze
    end
    79

    View Slide

  85. จࣈΤϯίʔσΟϯάม׵ܥ
    80

    View Slide

  86. NKF ޓ׵ϝιου
    81

    View Slide

  87. # Ruby 1.8.7
    class String
    def to_win31j_from_utf8
    NKF.nkf(‘-W -s -m0 -x’, self)
    end
    end
    # Ruby 1.9.3
    class String
    def to_win31j_from_utf8
    encode('Windows-31J', 'UTF-8',
    invalid: :replace,
    undef: :replace,
    replace: '')
    end
    end
    82

    View Slide

  88. kconv ͷϝιουͰ
    ܯࠂΛग़͢
    83

    View Slide

  89. # Ruby 1.8.7
    class String
    def tosjis_with_warn
    $stderr.puts “[WARN] ... #{caller[0]}”
    tosjis_without_warn
    end
    alias tosjis_without_warn tosjis
    alias tosjis tosjis_with_warn
    end
    # Ruby 1.9.3
    class String
    def tosjis
    $stderr.puts “[WARN] ... #{caller[0]}”
    encode(‘Windows-31J’,
    invalid: :replace,
    undef: :replace,
    replace: '')
    end
    end
    84

    View Slide

  90. ܯࠂΛग़͢Α͏ʹͯ͠
    ͓͘ࣄͰɺαʔϏε
    ։ൃΤϯδχΞ͕ؾ
    ෇͍ͯमਖ਼ͯ͘͠ΕΔ
    85

    View Slide

  91. ActiveRecord ͱ
    nil.id
    86

    View Slide

  92. 1.8 Ͱ͸ nil.id ͕ 4 Λ
    ฦ͢ͷͰྫ֎͕ग़ͳ͍
    87

    View Slide

  93. if Rails.application.config.whiny_nils
    require 'active_support/whiny_nil'
    end
    if RUBY_VERSION < '1.9'
    class NilClass
    def id_with_warn(*args)
    return 4 unless File.expand_path(caller[0]).
    starts_with?(Rails.root)
    message = "nil.id was called at #{caller[0]}"
    if defined? Logger
    Logger.error.post('nil.id', message)
    else
    $stderr.puts message
    end
    4
    end
    alias id_without_warn id
    alias id id_with_warn
    end
    end
    88

    View Slide

  94. ϝʔϧؔ܎
    89

    View Slide

  95. ଧ౗ tmail
    90

    View Slide

  96. tmail → mail.gem
    91

    View Slide

  97. ۤߦ
    92

    View Slide

  98. tmail ͷόάʹґଘ
    ͨ͠ίʔυ͕͋ͬͨ
    93

    View Slide

  99. ·ͩઓ͍͸ऴΘͬͯ
    ͳ͍ɾɾɾ
    94

    View Slide

  100. ײ૝
    95

    View Slide

  101. ྑ͘ͳ͍ίʔυΛېࢭ
    ͢Δඇޓ׵ੑ͸େ׻ܴ
    96

    View Slide

  102. items.each do |@item|
    97

    View Slide

  103. ศརͩͬͨࣄ͕Ͱ͖
    ͳ͘ͳΔͱ൵͍͠
    98

    View Slide

  104. # Ruby 1.9.3 Ͱಈ͔ͳ͘ͳͬͨίʔυ
    begin_date = Date.parse(“2013/03/01”)
    end_date = Date.parse(“2013/06/30”)
    begin_date.step(end_date, 1.week) do |date|
    ...
    end
    99

    View Slide

  105. Ҡߦ TIPS
    100

    View Slide

  106. Ruby ͷόʔδϣϯผ
    ʹϞϯΩʔύονΛ
    ੔ཧ
    101

    View Slide

  107. lib/
    monkey_patches/
    ruby/
    1.8/
    array/
    object/
    string/
    blank.rb
    conversion.rb
    encoding.rb
    multibyte.rb
    1.9/
    array/
    object/
    string/
    blank.rb
    conversion.rb
    encoding.rb
    multibyte.rb
    2.0/
    common/
    102

    View Slide

  108. # config/initializers/000_load_libs.rb
    FileUtils.chdir(‘../../../lib’, __FILE__) do
    # load monkey patches for ruby first
    Dir[‘monkey_patches/ruby/**/*.rb’].sort.each do |fn|
    version = fn.split(‘/’)[2]
    case
    when version == ‘common’
    # do nothing
    when RUBY_VERSION < ‘1.9’
    next unless version == ‘1.8’
    when RUBY_VERSION < ‘2.0’
    next unless version == ‘1.9’
    when RUBY_VERSION < ‘2.1’
    next unless (‘1.9’...‘2.1’).cover? version
    else
    next unless version >= ‘2.1’
    end
    require fn
    end
    end
    103

    View Slide

  109. ৽͍͠όʔδϣϯͰ
    ૿͑Δϝιου͸
    ύονͰಋೖ͢Δ
    104

    View Slide

  110. Object#singleton_c
    lass
    105

    View Slide

  111. # for 1.8
    class Object
    def singleton_class
    class << self
    self
    end
    end
    end
    106

    View Slide

  112. String#bytesize
    107

    View Slide

  113. # for 1.8
    class String
    alias bytesize size
    end
    108

    View Slide

  114. String#b
    109

    View Slide

  115. # for 1.8
    class String
    def b
    self
    end
    end
    # for 1.9
    class String
    def b
    dup.force_encoding(‘ASCII-8BIT’)
    end
    end
    110

    View Slide

  116. String#force_enco
    ding
    111

    View Slide

  117. # for 1.8
    class String
    def force_encoding(enc)
    self
    end
    end
    112

    View Slide

  118. ࣮͸͜ΕͰ͸ବ໨
    113

    View Slide

  119. respond_to?
    (:force_encoding) Ͱ 1.9
    ͔Ͳ͏͔൑அͯ͠ΔϥΠϒ
    ϥϦ͕͍͔ͭ͘ଘࡏ͢Δ
    114

    View Slide

  120. # for 1.8
    class String
    def method_missing_with_force_encoding(name, *args, &block)
    if name == :force_encoding
    self
    else
    method_missing_without_force_encoding(
    name, *args, &block)
    end
    end
    alias method_missing_without_force_encoding method_missing
    alias method_missing method_missing_with_force_encoding
    end
    115

    View Slide

  121. ΤϯίʔσΟϯάม
    ׵
    116

    View Slide

  122. from ͱ to ͷΤϯ
    ίʔσΟϯάΛݶఆ
    ͢Δͱಋೖ͠΍͍͢
    117

    View Slide

  123. ͨͬͨ͜Ε͚ͩͰΞϓ
    Ϧέʔγϣϯίʔυ͔
    Β RUBY_VERSION Λ
    ݮΒͤΔ
    118

    View Slide

  124. ͓·͚
    119

    View Slide

  125. irb(main):005:0> keys = (:a .. :d).to_a
    => [:a, :b, :c, :d]
    irb(main):006:0> values = (1 .. 4).to_a
    => [1, 2, 3, 4]
    irb(main):007:0> Hash[keys.zip(values)]
    => {:a=>1, :b=>2, :c=>3, :d=>4}
    120

    View Slide

  126. Time.now Λ্ॻ͖͢Δ
    ͷ͸ࢭΊΑ͏ ʼʻ
    121

    View Slide

  127. ·ͱΊ
    122

    View Slide

  128. REE 1.9.3 2.0.0
    809
    576
    497
    REE 1.9.3 2.0.0
    1773
    1305
    1166
    ฏۉϨεϙϯελΠϜ [ms] 90% Line [ms]
    Use Ruby 2.0.0 !!!
    123

    View Slide

  129. 124

    View Slide

  130. 125

    View Slide

  131. http://bit.ly/cookpad_jobs
    125

    View Slide