RubyConf 2018 Day Two

Ruby Conf 2018 Day Two


Key note - how to build a magical living room by Saron Yitbarek

Building Communities

Opinions matter

Let’s subclass Hash - what’s the worst that could happen? by Michael Herold

To err is human

Indifferent access

If you’re a rails developer, you know wabout access in ActiveSupport

class MyHash < Hash
  include Hashie::Extensions...
  include IndifferentAccess..
end

hash = MyHash.new(
  cat: 'meow',
  dog: { name: 'Rover', sound: 'woof'}
)

hash[:cat] => 'meow'
hash['cat'] == hash['meow'] # indifferent access

new_dog = hash[:dog].merge(breed: 'Blue healer')
#=> unable to access the convert! method in Hashie::Extensions::IndifferentAccess

module Hashie::Extensions::IndifferentAccess
  def merge(*)
    super.convert! # just the normal ruby <- no indifferent access
  end
end

singleton_class.ancestors
#=> [.., Hashie::Extensions::IndifferentAccess, ...]

result.singleton_class.ancestors
#=> No indifferent access

# Fix
module Hashie::Extensions::IndifferentAccess
  def merge(*)
    super.convert! # just the normal ruby <- no indifferent access
    result = super
    result.extend(... IndifferentAccess)
  end
end

Why was this a problem?

Mash key

mash = Hashie::Mash.new
mash.key = 'foo'

def method_missing(method_name, *args, &blk)
  return self.[](method_name, &blk) if key?(method_name)

  name, suffix = method_name_and_suffix(method_name)

  case suffix
  when '='.freeze then
  when ...
  else self[method_name]
  end
end

mash.zip = 'bar' # bug - it actually invokes enumerable's zip method

Fix - dhh sharp knife

Destructuring a dash

class Foo < Hashie::Dash
  property :bar
end

foo = Foo.new(bar: 'baz')      #=> {:bar=>"baz"}
qux = { **foo, quux: 'corge' } #=> {:bar=> "baz", :quux=>"corge"}
qux.is_a?(Foo)                 #=> true
qux[:quux]
#=> raise NoMethodError, "The property 'quux' is not defined for Foo."
qux.key?(:quux) #=> true

The Developer’s Toolkit: Everything We Use But Ruby by Noel Rappin

Slides

Memory and focus

Choose your tools

Pointers for Eliminating Heaps of Memory - Aaron Patterson

The compilation process of Ruby programs

Direct ISeq Marking

Taking a look at Ruby VM

Processing phases

Source code (text)
 |
AST (Abstract Syntax Tree)
 |
Linked List
 |
Byte Code)

Instruction optimizations

Internal data structures used for running Ruby code.

Use this information to maintain liveness of Ruby objects in the code.

we’ll take all the information we covered so far to develop a technique for reducing dead space in the heap.

Video (from past conference)

https://www.youtube.com/watch?v=YJPE5yTfvyQ

Reducing Enumerable - An Illustrated Adventure by Brandon Weaver

irb(main):001:0> [1,2,3].reduce(0) { |a, v| a + v}
=> 6
irb(main):002:0> [1,2,3].reduce(1) { |a, v| a + v}
=> 7

Accumulator

a   v    new
0 + 1 => 1
1 + 2 => 3
3 + 3 => 6

Sum (Ruby 2.4+)

[1,2,3].sum => 6

Ruby Map

irb(main):005:0> [1,2,3].map{ |a| a * 2 }
=> [2, 4, 6]

Implement Map with Reduce

def map(list, &function)
  list.reduce([]) do |a, v|
    a.push function.call(v)
    a
  end
end

map([1,2,3]) {|v| v * 2}
=> [2,4,6]

Ruby Select

[1,2,3].select{|v| v.even?}
=> 2

Implement select with reduce

def select(list, &function)
  list.reduce([]) { |a, v|
    a.push(v) if function.call(v) # create a list if the return value is trueth
    a
  }
end

select([1,2,3]) { |v| v.even? }
=> [2]

Ruby find

[1,2,3,4].find{ |v| v.even? }
=> 2

Implement find with reduce

def find(list, &function)
  list.reduce(nil) { |_, v|
    break v if function.call(v)
  }
end

find([1,2,3,4]) { |v| v.even? }
=> 2

Implemnt tally by

def tally_by(list, &function)
  init = Hash.new(0)
  list.reduce(init) { |a, v|
    key = function.call(v)
    a[key] += 1
    a
  }
end

tally_by([1,2,3]) { |v| v.even? }
=> { true => 1, false => 2}

Cache is King: Get the Most Bang for Your Buck From Ruby

CPU in Database

Now we have a Redis cache problem

Sharding configuration growth

20 bytes -> 1KB -> 13 KB

Making database requests where you don’t expect

return unless user.ids.any? # optimized to bypass opening that database connection (probably pooled) and querying the database
User.where(users_ids: user.ids).each do
  # process users
end

.none scope

User.where(id: []).tall.etc # bad
User.none.tall.etc # better

Increasing resque workers

Cache invalidation

Logs and monitoring

Make Ruby Write Your Code for You

Motivations

Magic modules

Like mad lips

a = ____.new(0)
    food
a.add(______)
      topping
a.order()

What auto-generation isn’t (for us)

Abstractions

def order_pizza
  a = Pizza.new()
  a.add('Pepperoni')
  a.add('Oregano')
  a.order
end
def order_salad
  a = Salad.new()
  a.add('Caeser Dressing')
  a.order
end

When do you auto-generate?

Why is Ruby great for auto-generating code?

def order_<%= food %>()
  a = <%= food.capitalize %>.new()
  <% for topping in toppings %>
  a.add(topping)
  a.order
end

How does this differ from Rails web development?

Word Bank

Slides