Slide 1

Slide 1 text

@kkuchta This is ruby var first = 3; var second = 4; var sum = function(a, b) { a + b; } console.log("Sum = ", sum(first, second));

Slide 2

Slide 2 text

@kkuchta Ruby is the best javascript Kevin Kuchta | he/him | Joyable Inc

Slide 3

Slide 3 text

@kkuchta Good ideas • Opal: ruby -> js • Therubyracer: js in ruby • Our thing: ruby == js will not feature heavily in this talk

Slide 4

Slide 4 text

@kkuchta Inspiration

Slide 5

Slide 5 text

@kkuchta Taking it further var first = 3; var second = 4; var sum = function(a, b) { a + b; } console.log("Sum = ", sum(first, second));

Slide 6

Slide 6 text

@kkuchta def var(_);end var(first = 3) var second = 4;

Slide 7

Slide 7 text

@kkuchta console.log("something") Console.foo

Slide 8

Slide 8 text

@kkuchta class Console def log(input) puts input end end console = Console.new() console.log("something") Console.foo

Slide 9

Slide 9 text

@kkuchta Splat: * def some_func(*my_args) puts my_args[0] puts my_args[1] puts my_args[2] end some_func('first arg', 'second arg', 'third arg')

Slide 10

Slide 10 text

@kkuchta Splat: * def log(*args) puts args.join(',') end log('Hey', 'world')

Slide 11

Slide 11 text

@kkuchta Classes class Foo ... end bar = Foo.new

Slide 12

Slide 12 text

@kkuchta Classes bar.class # Foo Foo.class # Class Class.class # Class

Slide 13

Slide 13 text

@kkuchta Anonymous Classes Foo = Class.new do ... end bar = Foo.new

Slide 14

Slide 14 text

@kkuchta Anonymous Classes bar = (Class.new { ... }).new

Slide 15

Slide 15 text

@kkuchta Anonymous Object bar = Object.new def bar.whatever # ...do stuff... end

Slide 16

Slide 16 text

@kkuchta Console.foo console = Object.new def console.log(*x) puts x.join(',') end console.log('hello', 'world') # prints "hello,world"

Slide 17

Slide 17 text

@kkuchta Anonymous Functions // Javascript var sum = function(a,b){ return a + b } sum(3, 4) # ruby sum = Proc.new { |a, b| a + b } sum.call(3, 4) sum[3, 4]

Slide 18

Slide 18 text

@kkuchta Functions var sum = function(a, b) { return a + b; } def function(*args, &block) return Proc.new { ... } end

Slide 19

Slide 19 text

@kkuchta Main Object $ irb > self => main > self.class => Object

Slide 20

Slide 20 text

@kkuchta Main Object class Object def foo 'bar' end end 3.foo # 'bar' foo # 'bar'

Slide 21

Slide 21 text

@kkuchta MethodMissing class Foo def method_missing(*args) args[0] end end bar = Foo.new bar.nonexistent # returns :nonexistent

Slide 22

Slide 22 text

@kkuchta GlobalMissing class Object def method_missing(*args) return args[0] end end 3.whatever # :whatever function(a, b) { return a + b } a # :a b # :b

Slide 23

Slide 23 text

@kkuchta Caveat def method_missing(*args) skip_methods = %i( to_a to_hash to_io to_str to_ary to_int ) return nil if skip_methods.include?(args[0]) return args[0] end

Slide 24

Slide 24 text

@kkuchta Functions var sum = function(a, b) { |a, b| return a + b; }

Slide 25

Slide 25 text

@kkuchta Send some_obj = [ 'a', 'b', 'c'] some_obj.first # 'a' some_obj.send('first') # 'a'

Slide 26

Slide 26 text

@kkuchta Assignment obj = Class.new { attr_accessor(:foo) }.new obj.foo = 3 obj.foo=(3) obj.send('foo=', 3)

Slide 27

Slide 27 text

@kkuchta Instance Eval obj = Class .new { attr_accessor :a, :b } .new obj.a = 3 obj.b = 4 obj.instance_eval { a + b }

Slide 28

Slide 28 text

@kkuchta Function Function def function(*args, &block) end [:a, :b] { a + b }

Slide 29

Slide 29 text

@kkuchta Function Function def function(*args, &block) function_block = Proc.new { |*arg_values| } return function_block end [3, 4] [:a, :b] { a + b }

Slide 30

Slide 30 text

@kkuchta Function Function def function(*args, &block) klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| } return function_block end [:a, :b] { a + b } [3, 4]

Slide 31

Slide 31 text

@kkuchta Function Function def function(*args, &block) klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new } return function_block end [3, 4] [:a, :b] { a + b }

Slide 32

Slide 32 text

@kkuchta Function Function def function(*args, &block) klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new obj.a = 3 obj.b = 4 } return function_block end [3, 4] [:a, :b] { a + b }

Slide 33

Slide 33 text

@kkuchta Function Function def function(*args, &block) klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new args.zip(arg_values).each { |arg, arg_value| obj.send(:"#{arg}=", arg_value) } } return function_block end [3, 4] [:a, :b] { a + b }

Slide 34

Slide 34 text

@kkuchta Function Function def function(*args, &block) klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new args.zip(arg_values).each { |arg, arg_value| obj.send(:"#{arg}=", arg_value) } obj.instance_eval(&block) } return function_block end [:a, :b] { a + b } [3, 4]

Slide 35

Slide 35 text

@kkuchta So Close var sum = function(a, b) { a + b } sum.call(3, 4) # 7

Slide 36

Slide 36 text

@kkuchta Define_method class Foo [1,2,3,4].each do |i| define_method("get_#{i}") { i } end end foo = Foo.new foo.get_1 # 1 foo.get_2 # 2

Slide 37

Slide 37 text

@kkuchta So Close-er var func_1234 = function(a, b) { a + b } define_method(:sum, &func_1234) puts sum(3, 4) # 7

Slide 38

Slide 38 text

@kkuchta Local Variables a = 1 b = 2 local_variables # [:b, :a]

Slide 39

Slide 39 text

@kkuchta eval eval("3 + 4") # 7 x = 5 eval("x + 1") # 6 eval("puts 'Hello, #{input}!'") input = "Ted';User.delete_all;'" # Oh dear

Slide 40

Slide 40 text

@kkuchta Back to the Function def function(*args, &block) ... function_block = Proc.new {...} ... func_name = :"func_#{rand(1000000)}" define_method(func_name, &function_block) func_name # "func_1234" end

Slide 41

Slide 41 text

@kkuchta Var Redux def var(_);end var sum = function(...) { ... } var(sum = function(...) { ... }) var(sum = "func_1234")

Slide 42

Slide 42 text

@kkuchta Var Redux define_method(:var) { |random_function_name| } "func_1234"

Slide 43

Slide 43 text

@kkuchta Var Redux define_method(:var) { |random_function_name| var_name = local_variables.find do |local_var| end # var_name should be "sum" } "func_1234"

Slide 44

Slide 44 text

@kkuchta Var Redux define_method(:var) { |random_function_name| var_name = local_variables.find do |local_var| local_var != :random_function_name && eval(local_var.to_s) == random_function_name end # var_name should be "sum" } "func_1234"

Slide 45

Slide 45 text

@kkuchta Var Redux define_method(:var) { |random_function_name| var_name = local_variables.find do |local_var| local_var != :random_function_name && eval(local_var.to_s) == random_function_name end # var_name should be "sum" define_method(var_name) { |*args| send(random_function_name, *args) } # sum(...) -> func_1234(...) } "func_1234"

Slide 46

Slide 46 text

@kkuchta Success! var sum = function(a, b) { a + b } puts sum(3, 4) # 7

Slide 47

Slide 47 text

@kkuchta Tiny Text Success! def function(*args, &block) func_name = :"func_#{rand(1000000)}" klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new args.zip(arg_values).each {|arg, arg_value| obj.send(:"#{arg}=", arg_value) } obj.instance_eval(&block) } define_method(func_name, &function_block) func_name end define_method(:var) { |random_function_name| var_name = local_variables.find do |local_var| local_var != :random_function_name && eval(local_var.to_s) == random_function_name end define_method(var_name) { |*args| send(random_function_name, *args) } }

Slide 48

Slide 48 text

@kkuchta This is ruby var first = 3; var second = 4; var sum = function(a, b) { a + b; } console.log("Sum = ", sum(first, second)); I'm @kkuchta

Slide 49

Slide 49 text

@kkuchta If you're reading this after the talk, you might have noticed a bug: we're missing `return`. `function sum(a,b) { return a + b }` is the correct javascript, but it'll fail in our ruby code. This was a blatant oversight on my part in the original blog post I wrote around this idea. Credit for a simple fix goes to /u/TikiTDO on Reddit: we just need to ditch the cool instance_eval and instead actually define a _method_ on our `obj`: def function(*args, &block) func_name = :"func_#{rand(1000000)}" impl_name = :"func_#{rand(1000000)}" klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new klass.send(:define_method, impl_method_name, &block) args.zip(arg_values).each {|arg, arg_value| obj.send(:"#{arg} =", arg_value) } obj.send(impl_method_name) } define_method(func_name, &function_block) func_name end

Slide 50

Slide 50 text

@kkuchta Bonus: here's a complete version of the code that you can actually run: console = Object.new def console.log(*x) puts x.map(&:to_s).join(',') end class Object def method_missing(*args) return args[0] end end def function(*args, &block) func_name = :"func_#{rand(1000000)}" impl_name = :"func_#{rand(1000000)}" klass = Class.new { attr_accessor *args } function_block = Proc.new { |*arg_values| obj = klass.new klass.send(:define_method, impl_method_name, &block) args.zip(arg_values).each {|arg, arg_value| obj.send(:"#{arg}=", arg_value) } obj.send(impl_method_name) } define_method(func_name, &function_block) func_name end define_method(:var) { |random_function_name| var_name = local_variables.find do |local_var| local_var != :random_function_name && eval(local_var.to_s) == random_function_name end define_method(var_name) { |*args| send(random_function_name, *args) } } var foo = function(a, b) { return a + b } var first = 3; var second = 4; var sum = function(a, b) { a + b; } console.log("Sum = ", sum(first, second));