Slide 1

Slide 1 text

๭ϨγϐαΠτͷ 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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

3

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

5

Slide 7

Slide 7 text

5

Slide 8

Slide 8 text

Run on 1.9.3-p392 since 26 Feb 2013 5

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

http://info.cookpad.com/ 7

Slide 12

Slide 12 text

8

Slide 13

Slide 13 text

http://bit.ly/cookpad_jobs 8

Slide 14

Slide 14 text

1.9.3 ҠߦͰ ۤ࿑ͨ͠ࣄ 9

Slide 15

Slide 15 text

Ϛδίϝ 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

Slide 16

Slide 16 text

# coding: utf-8 11

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

script/insert_magic_comment 13

Slide 19

Slide 19 text

#! /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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

if condition: when condition: while condition: 16

Slide 22

Slide 22 text

items.each do |@item| 17

Slide 23

Slide 23 text

view template ಺Ͱࢄݟ 18

Slide 24

Slide 24 text

<%# index.html.erb %>
<% @items.each_with_index do |@item, idx| -%>
<%= render ‘item’ %>
<% end -%>
<%# _item.html.erb %> name<%= @item.title %> description<%= @item.description %> ... 19

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

શͯෛ࠴Ͱஔ͖׵͑ͨ 22

Slide 28

Slide 28 text

<%# 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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

default_external ͸ UTF-8 25

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Hash ͷॱং໰୊ 29

Slide 35

Slide 35 text

# 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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

to_s ͷڍಈͷมԽ 32

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

# 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

Slide 40

Slide 40 text

# 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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

# 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

Slide 45

Slide 45 text

(time .. time).include? time 40

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

# 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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

enum_with_index 45

Slide 51

Slide 51 text

each_with_index 46

Slide 52

Slide 52 text

lambda ͷҾ਺Ϛονϯά 47

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

wrong number of arguments (1 for 0) 49

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

ࢿྉ࡞ͬͯͯࢥͬͨ 52

Slide 58

Slide 58 text

# 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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

ଟॏ୅ೖ 61

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

ΧϯϚΛ෇͚Ε͹ྑ͍ 63

Slide 69

Slide 69 text

# 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

Slide 70

Slide 70 text

Symbol#to_int 65

Slide 71

Slide 71 text

# 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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

ྑ͍࿩ 68

Slide 74

Slide 74 text

ਖ਼نදݱͷඇޓ׵ 69

Slide 75

Slide 75 text

\s ͷҙຯ (ޙड़) 70

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

ಠࣗͷ String#blank? 76

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

# 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

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

NKF ޓ׵ϝιου 81

Slide 87

Slide 87 text

# 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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

# 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

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

ActiveRecord ͱ nil.id 86

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

ϝʔϧؔ܎ 89

Slide 95

Slide 95 text

ଧ౗ tmail 90

Slide 96

Slide 96 text

tmail → mail.gem 91

Slide 97

Slide 97 text

ۤߦ 92

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

ײ૝ 95

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

items.each do |@item| 97

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

# 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

Slide 105

Slide 105 text

Ҡߦ TIPS 100

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

# 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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

Object#singleton_c lass 105

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

String#bytesize 107

Slide 113

Slide 113 text

# for 1.8 class String alias bytesize size end 108

Slide 114

Slide 114 text

String#b 109

Slide 115

Slide 115 text

# 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

Slide 116

Slide 116 text

String#force_enco ding 111

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

࣮͸͜ΕͰ͸ବ໨ 113

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

# 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

Slide 121

Slide 121 text

ΤϯίʔσΟϯάม ׵ 116

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

͓·͚ 119

Slide 125

Slide 125 text

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

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

·ͱΊ 122

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

124

Slide 130

Slide 130 text

125

Slide 131

Slide 131 text

http://bit.ly/cookpad_jobs 125