Slide 1

Slide 1 text

palkan_tula palkan RubyConf 2019 Ruby Next Vladimir Dementyev Evil Martians

Slide 2

Slide 2 text

142 palkan_tula palkan RubyConf 2019 2 Ruby

Slide 3

Slide 3 text

142 palkan_tula palkan RubyConf 2019 Features & challenges 3

Slide 4

Slide 4 text

142 palkan_tula palkan RubyConf 2019 Challenges for Ruby developers 4

Slide 5

Slide 5 text

142 palkan_tula palkan RubyConf 2019 5 Ruby Next

Slide 6

Slide 6 text

142 palkan_tula palkan RubyConf 2019 Make old Rubies quack like a new one 6

Slide 7

Slide 7 text

142 palkan_tula palkan RubyConf 2019 $ ruby -v
 2.5.3p105
 $ ruby -e "
 case {hello: 'RubyConf'}
 in hello: hello if hello =~ /Rails/
 puts 'My name is David'
 in hello: 'RubyConf'
 puts 'My name is Vova'
 end"
 -e:3: syntax error, unexpected in, expecting when in hello: if hello =~ /Rails/ 7 tl;dr

Slide 8

Slide 8 text

142 palkan_tula palkan RubyConf 2019 $ ruby -v
 2.5.3p105
 $ ruby -ruby-next -e "
 case {hello: 'RubyConf'}
 in hello: hello if hello =~ /Rails/
 puts 'My name is David'
 in hello: 'RubyConf'
 puts 'My name is Vova'
 end"
 My name is Vova 8 tl;dr

Slide 9

Slide 9 text

142 palkan_tula palkan RubyConf 2019 9 About me

Slide 10

Slide 10 text

142 palkan_tula palkan RubyConf 2019 Evil Martian 10

Slide 11

Slide 11 text

142 palkan_tula palkan RubyConf 2019 evilmartians.com 11

Slide 12

Slide 12 text

142 palkan_tula palkan RubyConf 2019 12 evilmartians.com What's next ?

Slide 13

Slide 13 text

142 palkan_tula palkan RubyConf 2019 Ruby and !Ruby developer 13

Slide 14

Slide 14 text

142 palkan_tula palkan RubyConf 2019 github.com/palkan 14

Slide 15

Slide 15 text

142 palkan_tula palkan RubyConf 2019 Gems author 15

Slide 16

Slide 16 text

142 palkan_tula palkan RubyConf 2019 rubygems.org/profiles/palkan 16

Slide 17

Slide 17 text

142 palkan_tula palkan RubyConf 2019 I have to write Ruby code compatible with older versions 17

Slide 18

Slide 18 text

142 palkan_tula palkan RubyConf 2019 18 2.3 — 2019-03-31 2.4 — 2020-03-31 ⏳ Ruby EOL

Slide 19

Slide 19 text

142 palkan_tula palkan RubyConf 2019 spec.require_ruby_version = " >= 2.5.0" 19

Slide 20

Slide 20 text

142 palkan_tula palkan RubyConf 2019 I want to use 2.7 features 20

Slide 21

Slide 21 text

142 palkan_tula palkan RubyConf 2019 Not everyone is a gem maintainer 21

Slide 22

Slide 22 text

142 palkan_tula palkan RubyConf 2019 jetbrains.com/lp/devecosystem-2019/ruby/ 22

Slide 23

Slide 23 text

142 palkan_tula palkan RubyConf 2019 “30% reported that they're not about to switch” 23

Slide 24

Slide 24 text

142 palkan_tula palkan RubyConf 2019 Performance Why upgrade? 24

Slide 25

Slide 25 text

142 palkan_tula palkan RubyConf 2019 Performance New features Why upgrade? 25

Slide 26

Slide 26 text

142 palkan_tula palkan RubyConf 2019 26 Story time

Slide 27

Slide 27 text

142 palkan_tula palkan RubyConf 2019 Jan 2018, Ruby 2.5 27

Slide 28

Slide 28 text

142 palkan_tula palkan RubyConf 2019 28 #yield_self

Slide 29

Slide 29 text

142 palkan_tula palkan RubyConf 2019 module Kernel def yield_self yield self end end 29 Metaprogramming for good

Slide 30

Slide 30 text

142 palkan_tula palkan RubyConf 2019 30 #then

Slide 31

Slide 31 text

142 palkan_tula palkan RubyConf 2019 module YieldSelfThen refine Kernel do unless nil.respond_to?(:yield_self) def yield_self yield self end end alias then yield_self end end 31 Metaprogramming for good

Slide 32

Slide 32 text

142 palkan_tula palkan RubyConf 2019 Refinements 32

Slide 33

Slide 33 text

142 palkan_tula palkan RubyConf 2019 Introduced in 2.0 Has been experimental until 2.1 Getting better with every release Won't go away in 3.0 History 33

Slide 34

Slide 34 text

142 palkan_tula palkan RubyConf 2019 Lexically scoped monkey-patch 34

Slide 35

Slide 35 text

142 palkan_tula palkan RubyConf 2019 Refinements changed the way method lookup work 35

Slide 36

Slide 36 text

142 palkan_tula palkan RubyConf 2019 def send(mid, *args) # iterate through the ancestors chain # (included/prepended modules and classes) k = self.class iter = self.class.ancestors.each loop do if k.instance_methods(false).include?(mid) return k.instance_method(mid) .bind_call(self, *args) end k = iter.next end end 36 Regular lookup

Slide 37

Slide 37 text

142 palkan_tula palkan RubyConf 2019 def send(mid, *args) k = self.class iter = self.class.ancestors.each loop do meth = + # lookup refinements in the execution context + if context.refinements_for(k)&.include?(mid) + context.refinements_for(k)[mid] - if k.instance_methods(false).include?(mid) + elsif k.instance_methods(false).include?(mid) k.instance_method(mid) end return meth.bind_call(self, *args) if meth k = iter.next end end 37 Refined lookup

Slide 38

Slide 38 text

142 palkan_tula palkan RubyConf 2019 “Refinements... Not fun.” — Ruby Implementation developer 38

Slide 39

Slide 39 text

142 palkan_tula palkan RubyConf 2019 JRuby vs refinements 39 15 issues were closed in 2019

Slide 40

Slide 40 text

142 palkan_tula palkan RubyConf 2019 Define refinement via refine Activate refinement via using 40

Slide 41

Slide 41 text

142 palkan_tula palkan RubyConf 2019 module YieldSelfThen refine Kernel do unless nil.respond_to?(:yield_self) def yield_self yield self end end alias then yield_self end end 41 Define

Slide 42

Slide 42 text

142 palkan_tula palkan RubyConf 2019 using YieldSelfThen p Gem ::Version.new(RUBY_VERSION).then { |v| v.segments[0 ..1].join(".") } # => "2.5" 42 Activate

Slide 43

Slide 43 text

142 palkan_tula palkan RubyConf 2019 # succ.rb
 using YieldSelfThen
 
 p ARGV[0].then { |i| i.to_i + 1 } 
 # prec.rb
 p ARGV[0].then { |i| i.to_i - 1 } 
 $ ruby succ.rb 2
 3 
 $ ruby prec.rb 3
 undefined method `then' for "3":String 43 Lexical

Slide 44

Slide 44 text

142 palkan_tula palkan RubyConf 2019 Safe monkey-patch 44

Slide 45

Slide 45 text

142 palkan_tula palkan RubyConf 2019 We can use refinements to safely “backport” new APIs 45

Slide 46

Slide 46 text

142 palkan_tula palkan RubyConf 2019 Ruby gets new APIs with every minor release 46

Slide 47

Slide 47 text

142 palkan_tula palkan RubyConf 2019 47 Kernel#yield_self Thread#fetch Hash#transform_keys Hash#slice 2.5

Slide 48

Slide 48 text

142 palkan_tula palkan RubyConf 2019 48 Kernel#then Proc# >>/# << Enumerable#filter Array#union/#difference 2.6

Slide 49

Slide 49 text

142 palkan_tula palkan RubyConf 2019 49 2.7 Enumerable#tally Enumerable#filter_map Time#ceil/#floor UnboundMethod#bind_call

Slide 50

Slide 50 text

142 palkan_tula palkan RubyConf 2019 rubyreferences.github.io/rubychanges/ 50

Slide 51

Slide 51 text

142 palkan_tula palkan RubyConf 2019 One refinement to rule them all 51 Ruby Next 2018

Slide 52

Slide 52 text

142 palkan_tula palkan RubyConf 2019 # stats.rb using RubyNext Dir["**/*"].filter_map do |path| next if path =~ %r{[$/]m?spec} File.extname(path) end.tally.sort_by(&:last).reverse_each do |(ext, count)| puts " #{count}\t #{ext}" end 52

Slide 53

Slide 53 text

142 palkan_tula palkan RubyConf 2019 # stats.rb using RubyNext Dir["**/*"].filter_map do |path| next if path =~ %r{[$/]m?spec} File.extname(path) end.tally.sort_by(&:last).reverse_each do |(ext, count)| puts " #{count}\t #{ext}" end 53

Slide 54

Slide 54 text

142 palkan_tula palkan RubyConf 2019 $ ruby -ruby-next stats.rb 67 .rb 50 5 .md 2 .lock 1 .txt 1 .mspec 1 .gemspec 1 .gemfile 1 .bat 1 .dict 54

Slide 55

Slide 55 text

142 palkan_tula palkan RubyConf 2019 55 Only contains undefined or modified methods => no-op in Ruby 2.7 using RubyNext

Slide 56

Slide 56 text

142 palkan_tula palkan RubyConf 2019 That's what Ruby Next could be but... 56

Slide 57

Slide 57 text

142 palkan_tula palkan RubyConf 2019 Ruby 2.7 adds a lot of new syntax features 57

Slide 58

Slide 58 text

142 palkan_tula palkan RubyConf 2019 Pattern matching: case i; in ... end Numbered params: -> { _1 + _2 } Ruby 2.7 58

Slide 59

Slide 59 text

142 palkan_tula palkan RubyConf 2019 Method reference: d.then(JSON.:parse) Args forwarding: def foo( ...) bar( ...) Ruby 2.7 59

Slide 60

Slide 60 text

142 palkan_tula palkan RubyConf 2019 We cannot refine syntax 60

Slide 61

Slide 61 text

142 palkan_tula palkan RubyConf 2019 Let's transpile it! 61

Slide 62

Slide 62 text

142 palkan_tula palkan RubyConf 2019 Polyfills (universal refinement) Transpiler RubyNext 2019 62

Slide 63

Slide 63 text

142 palkan_tula palkan RubyConf 2019 Transpiling 63

Slide 64

Slide 64 text

142 palkan_tula palkan RubyConf 2019 Transpiling 64 transpiler Ruby 2.7 source code Ruby 2.5 source code

Slide 65

Slide 65 text

142 palkan_tula palkan RubyConf 2019 Ruby Next is Babel for Ruby? 65

Slide 66

Slide 66 text

142 palkan_tula palkan RubyConf 2019 RubyNext is PostCSS for Ruby? 66

Slide 67

Slide 67 text

142 palkan_tula palkan RubyConf 2019 Transpiling 67 source code new old source code AST new old AST

Slide 68

Slide 68 text

142 palkan_tula palkan RubyConf 2019 def beach(*temperature) case temperature in :celcius | :c, (20 ..45) :favorable in :kelvin | :k, (293 ..318) :scientifically_favorable in :fahrenheit | :f, (68 ..113) :favorable_in_us else :avoid_beach end end 68 Example

Slide 69

Slide 69 text

142 palkan_tula palkan RubyConf 2019 69 Ripper Source to AST

Slide 70

Slide 70 text

142 palkan_tula palkan RubyConf 2019 $ ruby -r ripper -e "pp Ripper.sexp(File.read('beach.rb'))" [:program, [[:def, [:@ident, "beach", [1, 4]], [:paren, [:params, [:rest_param, [:@ident, "temperature", [1, 11]]], ], [:bodystmt, [[:case, [:var_ref, [:@ident, "temperature", [2, 7]]], [:in, [:aryptn, nil, [[:binary, [:symbol_literal, [:symbol, [:@ident, "celcius", [3, 6]]]], 70

Slide 71

Slide 71 text

142 palkan_tula palkan RubyConf 2019 Since 1.9 Version-specific Ripper 71

Slide 72

Slide 72 text

142 palkan_tula palkan RubyConf 2019 $ ruby -v
 2.5.3p105 $ ruby -r ripper -e "pp Ripper.sexp(File.read('beach.rb'))" nil 72

Slide 73

Slide 73 text

142 palkan_tula palkan RubyConf 2019 preval 73 github.com/kddeisz/preval

Slide 74

Slide 74 text

142 palkan_tula palkan RubyConf 2019 74 Ripper RubyVM::AbstractSyntaxTree Source to AST

Slide 75

Slide 75 text

142 palkan_tula palkan RubyConf 2019 $ ruby -e "pp RubyVM ::AbstractSyntaxTree.parse_file('beach.rb')" (SCOPE@1:0-14:3 body: (DEFN@1:0-14:3 mid: :beach body: (SCOPE@1:0-14:3 tbl: [:temperature] args: ... body: (CASE3@2:2-13:5 (LVAR@2:7-2:18 :temperature) (IN@3:2-12:16 (ARYPTN@3:5-3:28 const: nil pre: (LIST@3:5-3:28 75

Slide 76

Slide 76 text

142 palkan_tula palkan RubyConf 2019 New in 2.6 MRI-specific Version-specific RubyVM ::AbstractSyntaxTree 76

Slide 77

Slide 77 text

142 palkan_tula palkan RubyConf 2019 77 Ripper RubyVM::AbstractSyntaxTree Parser Source to AST

Slide 78

Slide 78 text

142 palkan_tula palkan RubyConf 2019 $ gem install parser $ ruby-parse ./beach.rb (def :beach (args (restarg :temperature)) (case-match (lvar :temperature) (in-pattern (array-pattern (match-alt (sym :celcius) (sym :c)) (begin (irange (int 20) (int 45)))) nil 78

Slide 79

Slide 79 text

142 palkan_tula palkan RubyConf 2019 Written in pure Ruby Version-independent Bullet-proofed (e.g., by RuboCop) Parser 79

Slide 80

Slide 80 text

142 palkan_tula palkan RubyConf 2019 80 Parser Born at Evil Martians

Slide 81

Slide 81 text

142 palkan_tula palkan RubyConf 2019 81 Unparser $ gem install unparser 
 $ ruby -r parser/current -r unparser -e "
 p Unparser.unparse(
 Parser ::CurrentRuby.parse('%i[a a b c].tally'))
 " "[:a, :a, :b, :c].tally"

Slide 82

Slide 82 text

142 palkan_tula palkan RubyConf 2019 82 Unparser $ gem install unparser 
 $ ruby -r parser/current -r unparser -e "
 p Unparser.unparse(
 Parser ::CurrentRuby.parse('%i[a a b c].tally'))
 " "[:a, :a, :b, :c].tally"

Slide 83

Slide 83 text

142 palkan_tula palkan RubyConf 2019 83 <<"A #{b}C" #{ <<"A #{b}C" A #{b}C } str A #{b}C 
 # => "\nstr\n" Parser is not ideal (dstr (begin (dstr (str "A") (begin (send nil :b)))) (str "\n") (str "str\n") (str "A") (begin (send nil :b)))) " #{"A #{b}"}\nstr\nA #{b}" # => undefined local variable or method `b'

Slide 84

Slide 84 text

142 palkan_tula palkan RubyConf 2019 Parser is good enough for now 84

Slide 85

Slide 85 text

142 palkan_tula palkan RubyConf 2019 Parse Ruby code using edge parser Replace unsupported AST nodes with equivalents for the target version Generate new source code with unparser Ruby Next 85

Slide 86

Slide 86 text

142 palkan_tula palkan RubyConf 2019 def transform(source) Parser.parse(source).then do |ast| rewriters.inject(ast) do |tree, rewriter| rewriter.new.process(tree) end.then do |new_ast| Unparser.unparse(new_ast) end end end end 86

Slide 87

Slide 87 text

142 palkan_tula palkan RubyConf 2019 module Rewriters class MethodReference < Base def on_meth_ref(node) receiver, mid = *node.children node.updated( # (meth-ref :send, # (const nil :C) :m) [ # receiver, # -> :method, # s(:sym, mid) # (send ] # (const nil :C) :method ) # (sym :m) end end end 87

Slide 88

Slide 88 text

142 palkan_tula palkan RubyConf 2019 module Rewriters class PatternMatching < Base def on_case_match(node) # ~500 LOC end end end 88

Slide 89

Slide 89 text

142 palkan_tula palkan RubyConf 2019 if ... else if ... else ... end #deconstruct & #deconstruct_keys Type and structure checks “Pattern matching” in <2.7 89

Slide 90

Slide 90 text

142 palkan_tula palkan RubyConf 2019 90 def fizzbuzz(num) rems = [num % 3, num % 5] if rems == [0, 0]
 'FizzBuzz' elsif rems[0] == 0
 'Fizz'
 elsif rems[1] == 1
 'Buzz' else
 raise "NoMatchingPattern"
 end
 end def fizzbuzz(num) case [num % 3, num % 5] in [0, 0] then 'FizzBuzz' in [0, _] then 'Fizz' in [_, 0] then 'Buzz' end end 2.7 2.5 ad-hoc rewrite

Slide 91

Slide 91 text

142 palkan_tula palkan RubyConf 2019 91 def fizzbuzz(num) vals = [num % 3, num % 5]
 .deconstruct raise TypeError unless Array === vals if (0 === vals[0]) &&
 (0 === vals[1]) 
 'FizzBuzz' elsif (0 === vals[0])
 && (_ == vals[1] || true)
 'Fizz'
 elsif (_ == vals[0] || true)
 && (1 === vals[1])
 'Buzz' else
 raise "NoMatchingPattern"
 end
 end def fizzbuzz(num) case [num % 3, num % 5] in [0, 0] then 'FizzBuzz' in [0, _] then 'Fizz' in [_, 0] then 'Buzz' end end 2.7 2.5 transpile

Slide 92

Slide 92 text

142 palkan_tula palkan RubyConf 2019 $ gem install ruby-next $ ruby-next nextify ./beach.rb -o stdout def beach(*temperature)
 __matchee __ = temperature
 if (((( __matchee_arr __ = __matchee __.deconstruct) || true) && ((Array === __matchee_arr __) || Kernel.raise(TypeError, __matchee __.inspect))) && ((2 == __matchee_arr __.size) && (((:celcius === __matchee_arr __[0]) || (:c === __matchee_arr __[0])) && ((20 ..45) === __matchee_arr __[1]))))
 :favorable
 else
 if (((:kelvin === __matchee_arr __[0]) || (:k === __matchee_arr __[0])) && ((293 ..318) === __matchee_arr __[1]))
 :scientifically_favorable
 else
 if (((:fahrenheit === __matchee_arr __[0]) || (:f === __matchee_arr __[0])) && ((68 ..113) === __matchee_arr __[1]))
 :favorable_in_us
 else
 :avoid_beach
 end
 end
 end
 end 92

Slide 93

Slide 93 text

142 palkan_tula palkan RubyConf 2019 93 def beach(*temperature)
 __matchee __ = temperature
 if (((( __matchee_arr __ = __matchee __.deconstruct) || true) && ((Array === __matchee_arr __) || Kernel.raise(TypeError, __matchee __.inspect))) && ((2 == __matchee_arr __.size) && (((:celcius === __matchee_arr __[0]) || (:c === __matchee_arr __[0])) && ((20 ..45) === __matchee_arr __[1]))))
 :favorable
 else
 if (((:kelvin === __matchee_arr __[0]) || (:k === __matchee_arr __[0])) && ((293 ..318) === __matchee_arr __[1]))
 :scientifically_favorable
 else
 if (((:fahrenheit === __matchee_arr __[0]) || (:f === __matchee_arr __[0])) && ((68 ..113) === __matchee_arr __[1]))
 :favorable_in_us
 else
 :avoid_beach
 end
 end
 end
 end def beach(*temperature) case temperature in :celcius | :c, (20 ..45) :favorable in :kelvin | :k, (293 ..318) :scientifically_favorable in :fahrenheit | :f, (68 ..113) :favorable_in_us else :avoid_beach end end For humans For machines

Slide 94

Slide 94 text

142 palkan_tula palkan RubyConf 2019 Transpiling vs. performance 94

Slide 95

Slide 95 text

142 palkan_tula palkan RubyConf 2019 def beach(*temperature) case temperature in :celcius | :c, (20 ..45) :favorable in :kelvin | :k, (293 ..318) :scientifically_favorable in :fahrenheit | :f, (68 ..113) :favorable_in_us else :avoid_beach end end $ ruby benchmark/pattern_matching_array.rb Comparison:
 transpiled: 2165271.2 i/s 
 baseline: 1004383.8 i/s - 2.16x slower 95

Slide 96

Slide 96 text

142 palkan_tula palkan RubyConf 2019 case JSON.parse(ARGV[0], symbolize_names: true) in {name: "Alice", children: [{name: "Bob", age: age}]} p "Bob age is #{age}" in _ "No Alice" end $ ruby benchmark/pattern_matching_mixed.rb Comparison:
 transpiled: 314105.5 i/s
 baseline: 273998.7 i/s - 1.15x slower 96

Slide 97

Slide 97 text

142 palkan_tula palkan RubyConf 2019 How to integrate a transpiler into an interpreted language? 97

Slide 98

Slide 98 text

142 palkan_tula palkan RubyConf 2019 Gems: transpile at “build”/ release time Apps/Scripts: transpile at runtime 98

Slide 99

Slide 99 text

142 palkan_tula palkan RubyConf 2019 Hijack Kernel#require & co Reimplement require mechanism Runtime 99

Slide 100

Slide 100 text

142 palkan_tula palkan RubyConf 2019 module Kernel module_function alias_method :require_without_ruby_next, :require def require(path) RubyNext.require(path) rescue => e warn "Failed to require ' #{path}': #{e.message}" require_without_ruby_next(path) end end 100 Kernel#require *Don't try this at home

Slide 101

Slide 101 text

142 palkan_tula palkan RubyConf 2019 $LOAD_PATH: where to search for “features” $LOADED_FEATURES: required files Two pillars of require 101

Slide 102

Slide 102 text

142 palkan_tula palkan RubyConf 2019 module RubyNext module_function def require(path) realpath = resolve_feature_path(path) return false if $LOADED_FEATURES.include?(realpath) File.read(realpath) .then(&Language.:transform).then do |source| TOPLEVEL_BINDING.eval source, path $LOADED_FEATURES << path true end end end 102 RubyNext.require

Slide 103

Slide 103 text

142 palkan_tula palkan RubyConf 2019 module RubyNext module_function def require(path) realpath = resolve_feature_path(path) return false if $LOADED_FEATURES.include?(realpath) File.read(realpath) .then(&Language.:transform).then do |source| TOPLEVEL_BINDING.eval source, path $LOADED_FEATURES << path true end end end 103 RubyNext.require

Slide 104

Slide 104 text

142 palkan_tula palkan RubyConf 2019 module RubyNext module_function def resolve_feature_path(path) path = " #{path}.rb" if File.extname(path).empty? return path if Pathname.new(path).absolute? $LOAD_PATH.find do |lp| lpath = File.join(lp, path) return lpath if File.file?(lpath) end end end 104

Slide 105

Slide 105 text

142 palkan_tula palkan RubyConf 2019 module RubyNext module_function def resolve_feature_path(path) path = " #{path}.rb" if File.extname(path).empty? return path if Pathname.new(path).absolute? $LOAD_PATH.find do |lp| lpath = File.join(lp, path) return lpath if File.file?(lpath) end end end 105

Slide 106

Slide 106 text

142 palkan_tula palkan RubyConf 2019 module RubyNext module_function def require(path) realpath = resolve_feature_path(path) return false if $LOADED_FEATURES.include?(realpath) File.read(realpath) .then(&Language.:transform).then do |source| TOPLEVEL_BINDING.eval source, path $LOADED_FEATURES << path true end end end 106 RubyNext.require

Slide 107

Slide 107 text

142 palkan_tula palkan RubyConf 2019 module RubyNext module_function def require(path) realpath = resolve_feature_path(path) return false if $LOADED_FEATURES.include?(realpath) File.read(realpath) .then(&Language.:transform).then do |source| TOPLEVEL_BINDING.eval source, path $LOADED_FEATURES << path true end end end 107 RubyNext.require

Slide 108

Slide 108 text

142 palkan_tula palkan RubyConf 2019 $ ruby -ruby-next -e "
 case {eta: ARGV[0].to_i}
 in eta: (0 ...10)
 puts 'The end is near'
 in eta: x if x > 10
 puts 'Boooring'
 end" 8
 The end is near 108 -ruby-next

Slide 109

Slide 109 text

142 palkan_tula palkan RubyConf 2019 ruby -ruby-next == ruby -r uby-next 109

Slide 110

Slide 110 text

142 palkan_tula palkan RubyConf 2019 110

Slide 111

Slide 111 text

142 palkan_tula palkan RubyConf 2019 Runtime mode is meant for tests and playgrounds 111

Slide 112

Slide 112 text

142 palkan_tula palkan RubyConf 2019 112 # test_helper.rb require 'ruby-next/runtime' Runtime in tests

Slide 113

Slide 113 text

142 palkan_tula palkan RubyConf 2019 113 # boot.rb require 'bootsnap/setup' require 'ruby-next/bootsnap' Runtime future

Slide 114

Slide 114 text

142 palkan_tula palkan RubyConf 2019 Add transpiled files to releases No additional runtime deps* Nextify Gems 114 * polyfills might still be required

Slide 115

Slide 115 text

142 palkan_tula palkan RubyConf 2019 $ ruby-next nextify ./lib -V
 Generated: ./lib/.rbnext/rubanok/rule.rb
 Generated: ./lib/.rbnext/rubanok/dsl/matching.rb 115

Slide 116

Slide 116 text

142 palkan_tula palkan RubyConf 2019 116 # lib/my_gem.rb require "ruby-next/language/setup" RubyNext ::Language.setup_gem_load_path

Slide 117

Slide 117 text

142 palkan_tula palkan RubyConf 2019 117 def setup_gem_load_path lib_dir = File.dirname(caller_locations(1, 1).first.path) current_index = $LOAD_PATH.index(lib_dir) next_dir = File.join(lib_dir, ".rbnext") if File.exist?(next_dir) $LOAD_PATH.insert current_index, next_dir current_index += 1 end end setup_gem_load_path

Slide 118

Slide 118 text

142 palkan_tula palkan RubyConf 2019 github.com/ruby-next/ruby-next 118

Slide 119

Slide 119 text

142 palkan_tula palkan RubyConf 2019 Supported features 119 github.com/ruby-next/ruby-next/blob/master/SUPPORTED_FEATURES.md

Slide 120

Slide 120 text

142 palkan_tula palkan RubyConf 2019 Compatibility matters 120

Slide 121

Slide 121 text

142 palkan_tula palkan RubyConf 2019 ruby/spec ruby/ruby/test/* Ruby Next tests 121

Slide 122

Slide 122 text

142 palkan_tula palkan RubyConf 2019 What's next? 122

Slide 123

Slide 123 text

142 palkan_tula palkan RubyConf 2019 Make old all Rubies quack like a new one 123

Slide 124

Slide 124 text

142 palkan_tula palkan RubyConf 2019 JRuby TruffleRuby mruby Opal Artichoke Prism Other Rubies 124

Slide 125

Slide 125 text

142 palkan_tula palkan RubyConf 2019 The role of Ruby Next? 125

Slide 126

Slide 126 text

142 palkan_tula palkan RubyConf 2019 Performance New features Why upgrade? 126

Slide 127

Slide 127 text

142 palkan_tula palkan RubyConf 2019 Performance Experimental features Why upgrade? 127

Slide 128

Slide 128 text

142 palkan_tula palkan RubyConf 2019 128 Experimental

Slide 129

Slide 129 text

142 palkan_tula palkan RubyConf 2019 Experimental 129

Slide 130

Slide 130 text

142 palkan_tula palkan RubyConf 2019 Experimental 130

Slide 131

Slide 131 text

142 palkan_tula palkan RubyConf 2019 Expelliarmus 131

Slide 132

Slide 132 text

142 palkan_tula palkan RubyConf 2019 We need an experiment 132

Slide 133

Slide 133 text

142 palkan_tula palkan RubyConf 2019 133 |> 2019-06-13 — 2019-08-29

Slide 134

Slide 134 text

142 palkan_tula palkan RubyConf 2019 134 .: 2018-12-31 — 2019-11-12

Slide 135

Slide 135 text

142 palkan_tula palkan RubyConf 2019 Do we have a process? 135

Slide 136

Slide 136 text

142 palkan_tula palkan RubyConf 2019 Example: TC39 136

Slide 137

Slide 137 text

142 palkan_tula palkan RubyConf 2019 “will require feedback from ... users” ☝ 137

Slide 138

Slide 138 text

142 palkan_tula palkan RubyConf 2019 Babel 138 https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them

Slide 139

Slide 139 text

142 palkan_tula palkan RubyConf 2019 The best way to taste new features is to start using them every day 139

Slide 140

Slide 140 text

142 palkan_tula palkan RubyConf 2019 Don't wait for Christmas to give new Ruby a try 140

Slide 141

Slide 141 text

142 palkan_tula palkan RubyConf 2019 Start using RubyNext 141

Slide 142

Slide 142 text

palkan_tula palkan RubyConf 2019 Thank you! Vladimir Dementyev @ruby-next Evil Martians @palkan @palkan_tula evilmartians.com @evilmartians