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
  2. Kenta Murata (mrkn) DevInfra Engineer at COOKPAD Inc. Ruby committer

    http://www.flickr.com/photos/kakutani/8526732201/ 2
  3. 3

  4. Kenta Murata (mrkn) DevInfra Engineer at COOKPAD Inc. Ruby committer

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

    http://www.flickr.com/photos/kakutani/8526732201/ BATMAN 4
  6. 5

  7. 5

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

  9. Run on 1.9.3-p392 since 26 Feb 2013 We’ll move to

    2.0.0-p0 next month !!! 5
  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
  11. http://info.cookpad.com/ 7

  12. 8

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

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

  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
  16. # coding: utf-8 11

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

  18. script/insert_magic_comment 13

  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
  20. https://gist.github.com/ mrkn/5173137 http://bit.ly/cookpad_jobs 15

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

  22. items.each do |@item| 17

  23. view template ಺Ͱࢄݟ 18

  24. <%# index.html.erb %> <div class=”item-list”> <% @items.each_with_index do |@item, idx|

    -%> <div class=”item-box”> <%= render ‘item’ %> </div> <% end -%> </div> <%# _item.html.erb %> <table> <tr><td>name</td><td><%= @item.title %></td></tr> <tr><td>description</td><td><%= @item.description %></td></tr> ... </table> 19
  25. ϨϯμϦϯά͞ΕΔ·Ͱ syntax error ͕஗Ԇ͢Δ 20

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

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

  28. <%# index.html.erb %> <div class=”item-list”> <% @items.each_with_index do |item, idx|

    @item = item -%> <div class=”item-box”> <%= render ‘item’ %> </div> <% end -%> </div> <%# _item.html.erb %> <table> <tr><td>name</td><td><%= @item.title %></td></tr> <tr><td>description</td><td><%= @item.description %></td></tr> ... </table> 23
  29. Tempfile ͷΦʔϓϯ Ϟʔυ͕ ‘w+’ ݻఆ໰୊ 24

  30. default_external ͸ UTF-8 25

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

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

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

  34. Hash ͷॱং໰୊ 29

  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
  36. Hash ͷ map ʹΑΔ݁ ՌΛݕূ͢Δςετͱ͔ 31

  37. to_s ͷڍಈͷมԽ 32

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

  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
  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
  41. ໌࣏తʹ to_s ΛݺΜ ͰΔίʔυͳΜͯͦ Μͳʹͳ͍ 36

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

    ruby-1.9.3 ͷ৔߹ “#{[:foo]}” #=> “[:foo]” “#{[:foo].first}” #=> “foo” 37
  43. Hash#to_s ͕ to_a.join(“”) ૬౰ͩͬͨ 38

  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
  45. (time .. time).include? time 40

  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
  47. Date#step ͕ ActiveSupport::Duration Λ ڋઈͩ͢͠ 42

  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
  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
  50. enum_with_index 45

  51. each_with_index 46

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

  53. # ApplicationHelper ͷத def init_handler(foo) Hash.new {|h, k| h[k] =

    lambda {} }.merge(foo) end # ΞϓϦέʔγϣϯίʔυ಺ init_handler(@handlers) ... @handlers[:foo].call(model) 48
  54. wrong number of arguments (1 for 0) 49

  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
  56. # ApplicationHelper ͷத def init_handler(foo) Hash.new {|h, k| h[k] =

    proc {} }.merge(foo) end # ΞϓϦέʔγϣϯίʔυ಺ init_handler(@handlers) ... @handlers[:foo].call(model) 51
  57. ࢿྉ࡞ͬͯͯࢥͬͨ 52

  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
  59. next Ͱϝιου͔Β ൈ͚Εͳ͘ͳͬͨ 54

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

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

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

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

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

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

  66. ଟॏ୅ೖ 61

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

    1 # Ruby 1.9.3 lambda {|*a| b = *a }.call(1) #=> [1] 62
  68. ΧϯϚΛ෇͚Ε͹ྑ͍ 63

  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
  70. Symbol#to_int 65

  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
  72. Ruby 1.9 Ͱ Symbol#to_int ͕ফ ͓͔͑ͨ͛Ͱόά͕ ໌Β͔ʹʂʂ̍ 67

  73. ྑ͍࿩ 68

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

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

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

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

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

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

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

    else re = /\W/ end 75
  81. ಠࣗͷ String#blank? 76

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

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

  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
  85. จࣈΤϯίʔσΟϯάม׵ܥ 80

  86. NKF ޓ׵ϝιου 81

  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
  88. kconv ͷϝιουͰ ܯࠂΛग़͢ 83

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

  91. ActiveRecord ͱ nil.id 86

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

  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
  94. ϝʔϧؔ܎ 89

  95. ଧ౗ tmail 90

  96. tmail → mail.gem 91

  97. ۤߦ 92

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

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

  100. ײ૝ 95

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

  102. items.each do |@item| 97

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

  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
  105. Ҡߦ TIPS 100

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

  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
  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
  109. ৽͍͠όʔδϣϯͰ ૿͑Δϝιου͸ ύονͰಋೖ͢Δ 104

  110. Object#singleton_c lass 105

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

    self end end end 106
  112. String#bytesize 107

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

  114. String#b 109

  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
  116. String#force_enco ding 111

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

    112
  118. ࣮͸͜ΕͰ͸ବ໨ 113

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

  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
  121. ΤϯίʔσΟϯάม ׵ 116

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

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

  124. ͓·͚ 119

  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
  126. Time.now Λ্ॻ͖͢Δ ͷ͸ࢭΊΑ͏ ʼʻ 121

  127. ·ͱΊ 122

  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
  129. 124

  130. 125

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