Ruby10 Class Module Gem 深入

Agenda

  1. class_eval & instance_eval
  2. method_missing
  3. Module as a namespace
  4. Gems
  5. require vs load
  6. $LOAD_PATH

class_eval

  1. 首先class_eval是只有类才能调用的,Class#class_eval
  2. class_eval会重新打开当前类的作用域
# class_eval
class User
end
User.class_eval do
  attr_accessor :name
  def hello
    "hello"
  end
end
user = User.new
user.name = "world"
puts user.name
puts user.hello


# module"s self
module Management
  def self.track
    "track"
  end
end
class User
  include Management
end
# User.track # => error
Management.track


# class_eval in project
# requirement: we need to execute a class method when module included
module Manegement
  def self.included base #Manafement模块被其他类Included的时候会执行
    base.extend ClassMethods #User类注入ClassMethod
    base.class_eval do #打开User类
      setup_attribute
    end
  end
 # Manegement 内部模块 当引入Management的时候 会被引用为其他类的类方法
  module ClassMethods
    def setup_attribute
      puts "setup_attribute"
    end
  end
end
class User
  include Manegement #目的是在include Management 的时候执行一些方法或者设置
end

instance_eval

  1. instance_eval 是所有类实例的方法
  2. 打开的是当前实例作用域
# instance_eval, instance methods and class methods
# 1. as a question
class User
end
User.class_eval do
  def hello
    "hello"
  end
end
User.instance_eval do
  def hi
    "hi"
  end
end
puts User.hi
user = User.new
puts user.hello
# puts user.hi #报错

# instance_eval, singleton_method
a = "xxx"
a.instance_eval do
  def to_hello
    self.replace("hello")
  end
end
puts a.to_hello
# b = "world"
# b.to_hello # => error

# class_eval as instance_eval
class User
end
User.class_eval do
  def hello
    "hello"
  end
  # works same as instance_eval
  def self.hi
    "hi"
  end
end
puts User.new.hello
puts User.hi

method_missing

  1. 当当前作用域上下文没有找到方法时就会调用
  2. method_missing方法
# metho missing
#
# 1. how it works
# 2. ancestors
# 3. rails"s AR
class User
  def hello
    "hello from User"
  end
  def method_missing(name, *args)
    "method name is #{name} ,parameters :#{args}"
  end
end
user = User.new
puts user.hello
puts "-" * 30
puts user.hi("hello",19)

Namespace

  1. Module
  2. Class
  3. Constants

使用::来访问

……

Ruby9 Class & Modules 进阶

Ruby的内部类结构

Array.class # => Class
Class.class # => Class

superclass 查看父类

Array.superclass # =>Object
Object.superclass # =>BasicObject
BasicObject.superclass # => nil

ancestors 查看当前类的继承链

Array.ancestors # => [Array, Enumerable, Object, Kernel, BasicObject]

Method Finding 方法查找

# class structure, method finding
class User
  def panels
    @panels ||= ["Profile", "Products"]
  end
end
class Admin < User
end
puts Admin.ancestors
admin = Admin.new
p admin.panels
# 从下往上查找 在admin中查找 找不到往上找User 然后Object 然后Kernel 然后 BasicObject

Method Overwrite 方法覆盖

  1. class和module可以重新打开
  2. 方法可以重定义
# 重新打开class
class User
  def panels
    @panels ||= ["Profile", "Products"]
  end
end
class User
  def panels
    "overwrite"
  end
end
puts User.ancestors
admin = User.new
p admin.panels
# 从下往上查找 在admin中查找 找不到往上找User 然后Object 然后Kernel 然后 BasicObject

# overwrite and re-open
class Array
  def to_hello_word
    "hello #{self.join(", ")}"
  end
end
a = %w[cat horse dog]
puts a.to_hello_word

# overwrite and re-open
a = %w[cat horse dog]
puts a.join(",")
class Array
  def join
    "hello"
  end
end
puts "-" * 30
puts a.join

Module

Array.ancestors # => [Array, Enumerable, Object, Kernel, BasicObject]
Enumerable.class # => Module
Module.class # => Class

# module acts linke a class
module Management
  def company_notifies
    "company_notifies from management"
  end
end
class User
  include Management
  def company_notifies
    puts super
    "company_notifies from user"
  end
end
p User.ancestors
puts "-" * 30
user = User.new
puts user.company_notifies

# module included sequence
module Management
  def company_notifies
    "company_notifies from management"
  end
end
module Track
  def company_notifies
    "company_notifies from track"
  end
end
class User
  include Management
  include Track
  def company_notifies
    puts super
    "company_notifies from user"
  end
end
p User.ancestors
puts "-" * 30
user = User.new
puts user.company_notifies

# 1 module included in module
# 2 module acts as class
module Management
  def company_notifies
    "company_notifies from management"
  end
end
module Track
  include Management
  def company_notifies
    puts super
    "company_notifies from track"
  end
end
p Track.ancestors
puts "-" * 30
include Track
puts company_notifies

# module"s class method
module Management
  def self.progress
    "progress"
  end
  # you need to include/extend/prepend to use this metod
  def company_notifies
    "company_notifies from management"
  end
end
puts Management.progress

include vs prepend

  1. include 把模块注入当前类的继承链(祖先链) 后面
  2. prepend 把模块注入当前累的继承链(祖先链) 前面
# module include
# include
module Management
  def company_notifies
    "company_notifies from management"
  end
end
class User
  prepend Management
  # include Management
  def company_notifies
    "company_notifies from user"
  end
end
p User.ancestors
puts "-" * 30
user = User.new
puts user.company_notifies

include和exten方法

当模块被include时会被执行,同事会传递当前作用于的self对象

……

Ruby8 继承(Inheritance)和模块(Modules)

继承示例

# inheritance
class User
  attr_accessor :name, :age
  def initialize name, age
    @name, @age = name, age
  end
  def panels
    # ||= 操作符, 如果变量不存在 那么就赋值
    @panels ||= ["Profile", "Products"]
  end
end
class Admin < User
  def panels
    @panels ||= ["Profile", "Products", "Manage Users", "System Setup"]
  end
end
user = User.new("user_1", 18)
p user.panels
puts "-" * 30
admin = Admin.new("admin_1", 28)
puts admin.name
p admin.panels
# 查看这个类的父类
p Admin.superclass

常用關鍵字

super关键字
调用父类的同名方法
self關鍵字
指向當前作用域實例

……

Ruby7 OOP

Everything is Object 一切皆对象

1
2
3
4
5
6
7

    a = "hello"
    a.class # => String
    b = 3.14
    b.class # => Float
    c = %w[pear cat horse]
    c.class # => Array

Instance Method & Instance Attribute 实例方法,实例属性

  1. Instance Method: 实例方法,成员方法,成员函数
  2. Instance Attribute: 实例变量,成员属性,属性(property),使用@定义
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
    # class
    class User
      def initialize name, age
        @name = name
        @age = age
      end
      # getter
      def name
        @name
      end
      def age
        @age
      end
    end
    user = User.new("Hello", 18)
    puts user.name
    puts user.age

    # class
    class User
      def initialize name, age
        @name = name
        @age = age
      end
      # getter
      def name
        @name
      end
      def age
        @age
      end
      # setter
      def name= name
        @name = name
      end
      def age= age
        @age = age
      end
    end
    user = User.new("Hello", 18)
    puts user.name
    puts user.age
    puts "-" * 30
    user.name = "hello 2"
    user.age = 20
    puts user.name
    puts user.age
    

    # class, attr_reader, attr_writer
    class User
      attr_reader :name
      attr_reader :age
      attr_writer :name
      attr_writer :age
      def initialize name, age
        @name = name
        @age = age
      end
    end
    user = User.new("Hello", 18)
    puts user.name
    puts user.age
    puts "-" * 30
    user.name = "hello 2"
    user.age = 20
    puts user.name
    puts user.age
    

    # class, method
    class User
      attr_accessor :name
      attr_accessor :age
      def initialize name, age
        @name = name
        @age = age
      end
      def say_hi
        puts "hello  #{@name}, i am #{@age}"
      end
    end
    user = User.new("Hello", 18)
    user.say_hi

Class Method & Class Variable 类方法,类变量

  1. Class Method:类方法,静态方法
  2. Class Variable:类变量, 使用@@定义
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    # class, method
    class User
      attr_accessor :name
      attr_accessor :age
      @@counter = 0
      def initialize name, age
        @name = name
        @age = age
        @@counter += 1
      end
      def say_hi
        puts "hello  #{@name}, i am #{@age}"
      end
      def self.get_counter
        @@counter
      end
    end
    puts User.get_counter
    user = User.new("Hello", 18)
    user.say_hi
    puts User.get_counter

## Access Control 访问控制

  1. public
  2. protected
  3. private

    # class, method
    class User
      attr_accessor :name
      attr_accessor :age
      def initialize name, age
        @name = name
        @age = age
      end
      def say_hello
        puts "hello..."
        say_hi
        say_hey
      end
      def say_hi
        puts "hello  #{@name}, i am #{@age}"
      end
      def say_hey
        puts "hey, i am #{@name}"
      end
      protected :say_hi, :say_hey
    end
    user = User.new("Hello", 18)
    user.say_hello

Dynamic Ruby 动态特性

User内幕

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
    # attr_accessor 方法从哪里来的?
    class User
      # ...
    end
    User.class # => class
    # Class.attr_accessor 背后的秘密
    # define_method
    # attr_accessor, define_method
    define_method :hello do
      puts "hello world"
    end
    hello

自己实现 attr_accessor

……

Ruby6 细节补充

代码规范

  1. 使用UTF-8编码
  2. 使用空格缩进,不使用tab, 1 tab = 2 spaces
  3. 不需要使用分号(;)和反斜杠()连接代码

Demo

……

Ruby5 其他的奇技淫巧

变量赋值

# 变量交换
a = 1
b = 2
b,a = a,b
puts a
puts b
puts "-" * 30
x = [1, 2, 3]
a, b = x #默认会把数组中的值依次赋值给 a ,b
puts a
puts b
puts "-" * 30
x = [1, 2, 3]
a, *b = x #这里a会接受第一个元素 b用了*号 表示接受剩下所有的元素
puts a
p b
#output
2
1
------------------------------
1
2
------------------------------
1
[2, 3]

Fixnum & Float

# number
puts 1 / 10
puts 1 / 10.0
puts "-" * 30
#output
0.1
------------------------------

String

# string
a = "world"
b = %Q{
    hello
    #{a}
}
# 这里不但可以用 {} 也可以用 ()
# 但是这里的Q必须是大Q 如果是小q的话 就相当于单引号的效果
puts b
puts "-" * 30

Ruby4 Blocks and Exceptions

Block 代码块

  1. Block是一个参数
  2. 匿名参数
  3. Callback
  4. 使用do/en或者{}来定义
{puts "hello"}

Demo:

# block usage
def hello
    puts "hello method start"
    yield
    yield
    puts "hello method end"
end
hello {puts "i am in block"}
#output
hello method start
i am in block
i am in block
hello method end


# yield with parameter
def hello
    puts "hello method start"
    yield("hello","world")
    puts "hello method end"
end
hello {|x,y| puts "i am in block,#{x} #{y}"}
#output
hello method start
i am in block,hello world
hello method end

# yield with paramter
def hello name
    puts "hello method start"
    result = "hello " + name
    yield(result)
    puts "hello method end"
end
hello("world"){|x| puts "i am in block,i got #{x}"}
#output
hello method start
i am in block,i got hello world
hello method end

# build in methods
["cat", "dog","frog"].each do |animal|
    puts animal
end
puts "-" * 30
["cat","dog","frog"].each{|animal| puts animal}
#output
cat
dog
frog
------------------------------
cat
dog
frog


# build in methods
10.times do |t|
    puts t
end
puts "-" * 30
("a".."d").each { |char| puts char}
#output
1
2
3
4
5
6
7
8
9
------------------------------
a
b
c
d

# varibale scope
# before ruby2.0
x = 1
[1, 2, 3].each { |x| puts x}
puts x # => x will be 3,which is incorrect
#output
1
2
3
1
如果是在ruby2之前的版本 那么外部的变量x会被改变

# varibale scope
# 如果是2.0版本之后 puts x会报错
sum = 0
[1, 2, 3].each { |x| sum += x}
puts sum
# puts x
#output
6

# block return value
class Array
    def find
        each do |value|
            return value if yield (value)
        end
        nil
    end
end
puts [1, 2, 3].find { |x| x == 2 }
#output
2

# block as named parameter
def hello name, &block
    puts "hello #{name}, from method"
    block.call(name)
end
hello("world") {|x| puts "hello #{x} form block"}
#output
hello world, from method
hello world form block

# yield with parameter
def hello
    puts "hello method start"
    yield("hello","world")
    puts "hello method end"
end
hello {|x,y| puts "i am block ,#{x},#{y}"}
#output
hello method start
i am block ,hello,world
hello method end

# block_given?
def hello
    if block_given?
        yield
    else
        puts "hello from method"
    end
end
hello
puts "-" * 30
hello {puts "hello from block"}
#output
hello from method
------------------------------
hello from block

# block can be closure
def counter
    sum = 0
    # 代码库接收了一个参数x 如果x没有定义那么x为1 然后 sum +=x
    proc {|x| x = 1 unless x; sum +=x }
end
c2 = counter
puts c2.call(1) #1
puts c2.call(2)
puts c2.call(3)
# 这里 closure 为闭包
#
#output
1
3
6

# new method to create block
# name is required
hello = -> (name){"hello #{name}"}
puts hello.call("world")
puts "-" * 30
# name is required
hello3 = lambda {|name| "hello #{name}"}
puts hello3.call("world")
puts "-" * 30
hello2 = proc {|name| "hello #{name}"}
puts hello2.call
puts hello2.call("world")
# lambda和proc区别 proc可以不传参数 lambda 更像是一个方法,必须传递参数
#output
hello world
------------------------------
hello world
------------------------------
hello
hello world

Exceptions 异常

All Exception inherited from Exception Class

……

Ruby3 流程控制

if/else/elsif

1
2
3
4
5
6
7
8
9
a = "hello"
b = false
if a
    p a
elsif b
    p b
else
    p "ok"
end

unless

unless相当于if的反向断言

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    unless false
        "ok"
    end
    # => "ok"

## if/unless

    a = 1 if a != 1 #如果a不是1 则a复制为1
    b = 2 unless defined?(b) #如果b未定义 那么就定义b赋值为2

## case

    case a
    when 1
        1
    when /hello/
        "hello world"
    when Array
        []
    else
        "ok"
    end

## while

    a = 0
    while a < 100 do
        p a
        a += 1
    end

ruby 没有++和–操作符

……

Ruby1 数据类型,变量

  1. 整数类型: 3,222
  2. 小数: 3.14
  3. 字符串: hello,world
  4. 布尔类型: true(TrueClass),false(FalseClass)
  5. 数组: [1,2],["hello","hello world"]
  6. Hash(字典): {"name"=>"luo","age"=>24},{:name=>"daoyi",:age=>24}
  7. Symbol(符号): :a,:hello,:"hello world"
  8. Range: 1..10,1...10(三个点不包括10本身)
  9. 正则: /hello/i

String 字符串

1
2
3
4
5
a = "hello" #=> hello
b = "world" #=> world
a.class #=> String
a + " " + b #=> hello world
"#{a} #{b}" #=> hello world

string method

1
2
3
"hello world".length #=>11
"hello world".capitalize #=> Hello world
"hello world".gsub("world","gril").upcase #=> HELLO GIRL

变量赋值

1
2
3
4
5
6
a = "hello" # => hello
a.object_id # => 70353313681980
a.replace("hello2") # => "hello2"
a.object_id # => 70353313681980
a = "hello"
a.object_id # => 70353313549380

当a 使用replace时候仍然是原本的引用地址,所以Object_id不变
但是当a重新赋值为hello 的时候,a的引用地址发生了变化 object_id就改变了

……

Ruby2 Method and Block

Method(Function)

1
2
3
4
5
6
7
8
9
def hi(name)
    p "hi " + name
end
hi("666") # => "hi 666"
hi "code" #括号省略  => "hi code"
def hello name
    p "hello #{name}"
end
hello "world" # => "hello world"

Method 参数

1
2
3
4
5
6
7
8
9
def hi name="code"
    p "hi " +name
end
hi # => "hi code"
def hi name,age=32
    p "#{name} is #{age}"
end
hi "code" # => "code is 32"
hi "code", 54 # => "code is 54"

Method 返回值

凡有方法都有返回值,方法体最后一行代码的返回值默认会做为方法的返回值,也可以显式的使用return关键字

……