robturtle
10/29/2017 - 4:30 PM

learnruby.md

Everything is an object

(-1).abs # numbers are objects
'hello'.class # strings are objects
String.to_s # a class is an object
Human.new.__binding__ # Even a scope is an object!

Object Oriented in a nutshell

An object is a kind of entity who can receive messages from other objects and respond back to them.

x.foo # You may say it as "invoking foo method from x object"
x.send('foo') # But it's actually "sending a message called 'foo' to x object"

# You may say it as "defining a bar function within x"
def x.bar
  'bar'
end
# But it's actually "create a new message slot for x object so it can respond to message 'bar'"

# Since it's just a message, it can also be any random string
define_method('la la') { 'ha ha' } # however you can't define it with `def` keyword if it's not a legal variable name
send('la la') # And you can only use `send` method

# syntactic sugar of arithmetic expressions
1 + 1
1.+(1)
1.send('+', 1)

Dynamic Language & Meta-programming

[1,2,3].each do |n|
  define_method("func_#{n}") { n }
end

func_1
func_2

Modularization

Class

class Human
  def initialize(name, sex, age)
    @name = name
    @sex = sex
    @age = age
  end
end

laowang = Human.new('LaoWang', 'male', 35)

# `class` keyword didn't necessarily create a class object, it may refer to the previous one
# We call this "reopening a class"
class Human
  def pee
    if @sex == 'male'
      "#{@name} needs to pee"
    else
      "#{@name} needs to go the the bathroom"
    end
  end
end
laowang.pee

# This is also called "Monkey Patching"
# You can even monkey-patch the builtin classes
class Integer
  def plusplus
    self + 1
  end
end

3.plusplus

# In Rails, they monkey-patched Integer so we can write code like this:
1.day.ago
Date.today + 3.days

Inheritance

class Programmer < Human
  def initialize(*args)
    super(*args)
    @power = 'change the world'
  end
  
  def pee
    "POST pee\r\nHost-name: #{@name}\r\n"
  end
end

john = Programmer.new('John', 'male', 37)

Singleton Class

C = 'C'
class << C
  def plusplus
    "#{self}++"
  end
end

C.plusplus
B = 'B'
B.plusplus # no such method

Module & Mixin

module Runable
  def run
    "Run #{name}, run!"
  end
end

class Human
  puts 'Yoo' # you can run any code inside `class`
  
  include Runable # this is also just a normal function call
  
  def name
    @name
  end
  
  def name=(new_name)
    @name = new_name
  end
end

yang = Programmer.new('Yang Liu', 'male', 26)
yang.name = 'Yang' # calling `Human.name=('Yang')`
yang.run

Rubyists say: DRY!

A: defining getter/setter is tedious
Some ignorant Java developer: Don't you have auto completion in your IDE?

class Class
  def attr_accessor(var)
    self.class_eval("def #{var}; @#{var}; end")
    self.class_eval("def #{var}=(val); @#{var} = val; end")
  end
end

class Human
  attr_accessor :name
end

Mixins are good for its Orthogonality

Considering we want implement a Human class which is both runable and talkable, how do we do with a programming language without mixins?

interface Runable { void run(); }
interface Talkable { void talk(); }

class DefaultRunable implements Runable {
  @Override public void run() {
    printf("Run!"); // how can we access field from Human?
  }
}

class DefaultTalkable implements Talkable {
  ...
}

public class Human implements Runable, Talkable {
  private final Runable runableDelegate;
  private final Talkable talkableDelegate;
  
  public Human(Runable r, Talkable t) {
    runableDelegate = r;
    talkableDelegate = t;
  }
  
  public Human() {
    this(new DefaultRunable(), new DefaultTalkable);
  }
  
  @Override public void run() {
    runableDelegate.run();
  }
  
  @Override public void talk() {
    talkableDelegate.talk();
  }
}

Meanwhile in Ruby:

module Runable
  def run
    "Run! #{name} run!"
  end
end

module Talkable
  def talk
    "What does #{name} say?"
  end
end

class Human
  attr_accessor :name, :sex, :age
  
  include Runable
  include Talkable
  
  def initialize(name, sex, age)
    @name = name
    @sex = sex
    @age = age
  end
end

Rails specific

Convention over Configuration

  • Dependency Auto-loading (Hot Module Replacement)

ActiveSupport::Concern

module MyModule
  module ClassMethods
    def foo
      'foo'
    end
  end

  module InstanceMethods
    def foo
      'instance foo'
    end
  end
end

class X
  include MyModule::InstanceMethods
  extend MyModule::ClassMethods
end

X.foo # => 'foo'
X.new.foo # => 'instance foo'

# KISS with ActiveSupport::Concern
module MyModule
  include ActiveSupport::Concern
end

class Y
  include MyModule
end

Y.foo
Y.new.foo