(-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!
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)
[1,2,3].each do |n|
define_method("func_#{n}") { n }
end
func_1
func_2
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
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)
C = 'C'
class << C
def plusplus
"#{self}++"
end
end
C.plusplus
B = 'B'
B.plusplus # no such method
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
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
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
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