Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby's Inner Beauty 2.0

Ruby's Inner Beauty 2.0

Presented at Codemash 2016.

Alex Burkhart

January 08, 2016
Tweet

More Decks by Alex Burkhart

Other Decks in Programming

Transcript

  1. PARSE A LINE 2 + 5 * 10 2 +

    (5 * 10) (2 + (5 * 10))
  2. WALK THE TREE 2 + 5 * 10 2 +

    (5 * 10) (2 + (5 * 10)) (2 + (50)) (2 + 50)
  3. EVALUATE THE TREE 2 + 5 * 10 2 +

    (5 * 10) (2 + (5 * 10)) (2 + (50)) (2 + 50) (52) 52
  4. HUMAN READABLE NUMBERS error_prone = 1000000000 super_easy = 1_000_000_000 if

    error_prone != super_easy raise ProgrammerError.new("Looks like you made a typo!") end
  5. STRING SYNTAX s1 = "syntactic sugar" s2 = 'syntactic sugar'

    s3 = String.new("syntactic sugar") [s1, s2, s3].uniq.one? # => true
  6. ARRAY OF WORDS SYNTAX words1 = %w| array of strings

    | words2 = ["array", "of", "strings"] sym1 = %i( array of symbols ) sym2 = [:array, :of, :symbols]
  7. ESCAPING STRING SYNTAX s1 = "\"Don't forget to escape your

    quotes\" -Alex" s2 = '"Don\'t forget to escape your quotes -Alex' s3 = %Q{"Don't forget to escape your quotes" -Alex} s4 = %q{"Don't forget to escape your quotes" -Alex} [s1, s2, s3, s4].uniq.one? # => true
  8. ESCAPING REGEXP r1 = /^(\/foo\/|\/bar\/)/ r2 = Regexp.new("^(/foo/|/bar/)") r3 =

    %r_^(/foo/|/bar/)_ [r1, r2, r3].uniq.one? # => true
  9. SHORTHAND SYNTAX square = Proc.new { |x| x * x

    } square.call(2) # => 4 cube = ->(y){ y * y * y } cube.call(2) # => 8 cube.class == Proc # => true
  10. HASH SYNTAX h1 = { a: 10 } h2 =

    { :a => 10 } h3 = Hash.new h3[:a] = 10 h1 == h2 == h3 # => true
  11. FAKE SYNTAX params = { category: 'ruby' } params[:category] #

    => 'ruby' class Hash def [](key) fetch(key, nil) end end
  12. INSTANCE VARIABLE ACCESS class Coffee def initialize(size, roast) @size =

    size @roast = roast end def to_s "#{@size} cup of #{@roast} roast" end end pry> Coffee.new('large', 'light').to_s => "large cup of light roast"
  13. INSTANCE VARIABLE ACCESS class Coffee def initialize(size, roast) instance_variable_set(:@size, size)

    instance_variable_set(:@roast, roast) end def to_s size = instance_variable_get(:@size) roast = instance_variable_get(:@roast) "#{size} cup of #{roast} roast" end end pry> Coffee.new('large', 'light').to_s => "large cup of light roast"
  14. EVERYTHING IS A MESSAGE 12 + 8 12.send(:+, 8) operators

    = [:+, :-, :/] operators.send(:<<, :*)
  15. ASSIGNMENT class Todo attr_accessor :state def initialize(title) @title = title

    @state = :pending end end ruby_talk = Todo.new("Present Ruby's Inner Beauty at Codemash") ruby_talk.state = :in_progress
  16. ASSIGNMENT METHODS class Todo attr_reader :state def initialize(title) @title =

    title @state = :pending end def state=(state) @state = state end end ruby_talk = Todo.new("Present Ruby's Inner Beauty at Codemash") ruby_talk.state = :in_progress
  17. ASSIGNMENT METHODS class Todo attr_reader :state def initialize(title) @title =

    title @state = :pending end def state=(state) @state = state end end ruby_talk = Todo.new("Present Ruby's Inner Beauty at Codemash") ruby_talk.state=(:in_progress)
  18. ASSIGNMENT VIA MESSAGES class Todo attr_reader :state def initialize(title) @title

    = title @state = :pending end def state=(state) @state = state end end ruby_talk = Todo.new("Present Ruby's Inner Beauty at Codemash") ruby_talk.send(:state=, :in_progress)
  19. ABSOLUTELY EVERYTHING IS A MESSAGE class Todo attr_reader :state def

    initialize(title) @title = title @state = :pending end def state=(state) @state = state end end ruby_talk = Todo.send(:new, "Present Ruby's Inner Beauty at Codemash") ruby_talk.send(:state=, :in_progress)
  20. Class#new class Class def new(*args) instance = allocate instance.initialize(*args) instance

    end def allocate Heap.malloc(self) # gross simplification end end Coffee.send(:new, 'medium', 'dark')
  21. include Celluloid # Class methods added to classes which include

    Celluloid module ClassMethods # Create a new actor def new(*args, &block) proxy = Cell.new(allocate, behavior_options, actor_options).proxy proxy._send_(:initialize, *args, &block) proxy end end
  22. ASYNC MESSAGE PASSING class AsyncSpammer include Celluloid def deliver_spam(address) sleep(1)

    puts "spamming #{address.inspect}" end end worker = AsyncSpammer.new # => #<Celluloid::CellProxy(AsyncSpammer:0x3fd351810484)> worker.async.deliver_spam('[email protected]') # => nil # => spamming "[email protected]"
  23. class KEYWORD class Todo def initialize(title) @title = title @state

    = :pending end def to_s "#{@state}: #{@title}" end end Todo.new('Introduce class Keyword')
  24. Class::new Todo = Class.new(Object) do def to_s "#{@state}: #{@title}" end

    end Todo.send(:new, "Reveal the `new` class method on Class")
  25. REOPENING CLASSES Todo ||= Class.new(Object) Todo.class_eval do def state=(state) instance_variable_set(@:state,

    state) end end t2 = Todo.send(:new, 'Reopen Todo Class') t2.send(:state=, :in_progress)
  26. def DEMYSTIFIED class Todo define_method(:state=) do |new_state| @state = new_state

    end end t2 = Todo.new('Breakdown def keyword') t2.state = :in_pieces
  27. def DEMYSTIFIED Todo ||= Class.new Todo.class_eval do define_method :state= do

    |new_state| instance_variable_set(:@state, new_state) end end t2 = Todo.send(:new, 'Breakdown def keyword') t2.send(:state=, :in_pieces)
  28. A BETTER METHOD MAKING MACHINE Class.class_eval do define_method(:better_reader) do |name|

    define_method(name) do instance_variable_get("@#{name}") end end end Todo.class_eval { better_reader :title } t = Todo.new("Reimplement attr_reader") t.title # => "Reimplement attr_reader"
  29. PRACTICALLY A MONEY MAKING MACHINE Class.class_eval do define_method(:better_writer) do |name|

    define_method("#{name}=") do |val| instance_variable_set("@#{name}", val) end end end Todo.class_eval { better_writer :title } t = Todo.new("Reimplement attr_writer") t.title = "A much better writer..." t.title # => "A much better writer..."
  30. LOCAL VARIABLES class Todo def state=(new_state) previous_state = @state puts

    "Transitioning from #{previous_state} to #{new_state}" @state = new_state end end
  31. Kernel#binding class Todo def state=(new_state) previous_state = @state binding.local_variables #

    => [:new_state, :previous_state] puts "Transitioning from #{previous_state} to #{new_state}" binding.eval('new_state') == new_state # => true @state = new_state end end
  32. SELF class Todo def get_binding binding end def get_self binding.eval('self')

    end end t = Todo.new t.get_binding # => #<Binding:0x007fa6a485df20> t.get_self # => #<Todo:0x007fa6a485df20>
  33. CHANGE SELF Todo.class_eval do # self == Todo class end

    t2 = Todo.new("Change execution contexts") t2.instance_eval do # self == instance of Todo end
  34. EVAL + BINDING => SUPERMETA class SuperMeta def initialize(data) @data

    = data end def instanze_eval(str) eval(str, self.send(:binding)) end end sm = SuperMeta.new(too_far: true) sm.instanze_eval('puts "Too far? #{self.instance_variable_get(:@data)}"') # => Too far? {:too_far=>true}