---
# System prepended metadata

title: 20221111 Rails 筆記
tags: [Ruby & Rails]

---

###### tags: `Ruby & Rails`
# 20221111 Rails 筆記
[TOC]

---

<政庭>
## 物件導向程式設計- Object-Oriented Programming
* OOP -> 金字塔結構
* 目的是做分類，以人類理解的方式呈現
* Ruby世界裡面，block、method(function)不是物件
* Ruby效能不是唯一考量。好寫、貼近人類語言趨向

## Functional Programming 功能型程式設計 
* FP ->(扁平的結構)

## 類別與實體 
* 實體方法作用在實體身上
* 類別方法作用在類別身上
* 類別命名必須是"常數"
```ruby
class Cat  // 實體方法
    def hi
        p "hi"
    end
end

kitty = Cat.new // 類別方法
kitty.hi
```

## 繼承
```ruby
class Animal
    def eat
    end
end

class Cat < Animal
end

class Dog < Animal
end

kitty = Cat.new
kitty.eat

lucky = Dog.new
lucky.eat
```
## initialize(初始化)
* initialize 是new完成後馬上接著做的第一件事
* 目的是要讓 new 出來的物件可以馬上有裝備起來
```ruby
class Cat
    # 建構、建構子 Constructor
    def initialize(nn)
        puts nn
        puts "hi"
    end
    
    def hey
    end
end

kitty = Cat.new("nancy")
// 執行結果 nancy
//          hi
```
```ruby
# class Object
#     def initialize
#     end
# end
// 假設最上層有一個叫做Object的系統預設類別
class Animal
end

class Cat < Animal
end

kitty = Cat.new("nancy")
// 結果顯示 錯誤訊息 引數個數錯誤
// 因為new完後馬上執行initialize
// 往上找class Cat 沒有 initialize
// 再往上找 class Animal 沒有initialize
// 再往上找 看不到的 Object 裡面有一個沒有接收(參數)的initialize
// 所以會看到 error訊息顯示 引數個數錯誤
```

## 實體變數 ＠變數名稱
* 實體變數只會作用實體上
* 每個實體變數，會有個別的實體變數，不會跟其他實體共享

## Ruby 沒有屬性
```ruby
class Animal
    attr_accessor :age  // 可讀可寫
    attr_reader :age    // 做 getter的事情     
    attr_writter :age   // 做 setter的事情
    
    def initialize(age)
        @age = age
    end
    # getter
    def age
        @age
    end
    # setter
    def age=(n)
        @age=n
    end
end
```
## 實體方法 VS 類別方法
* 實體方法作用在實體上
* 類別方法作用在類別上

## 單體方法 Singleton method
```ruby
class Cat
    def kitty.hi      // 只幫kitty定義hi的方法
    puts "hihihi"
    end
end

kitty = Cat.new
nn = Cat.new


kitty.hi    // 執行結果 會印出 hihihi
nn.hi       // 執行結果 會出錯
```

## 類別方法
```ruby
class Cat
    # 類別方法
    def self.fly    // self = Cat 誰呼叫誰就是self    
    puts "fly!!!"
    end
    
    def hi
        p "hi"
    end
end

Cat.fly

kkk = Cat.new("cc")
kkk.hi   
```
### Open Class
```ruby
class Cat
    def hi
    end
end
class Cat
    def hwy
    end
end
// 這兩個方法都會存在，Ruby會把它"融合"起來
// Cat.instance_methods(false)
```
## 類別變數
```ruby
class Cat
    @@count = 0
    def initialize
        @@count += 1
    end
    def self.total
        @@count
    end
end
```
## 手刻 attr_reader / attr_writter / attr_acessor
```ruby
class Animal
    def self.my_attr_reder(attr)
        // define_method可以做到的事情，動態定義"方法"的 method。
        define_mtthod(attr) do  
            instance_variable_get(:"@#{attr}")
        end
    end
    def self.my_attr_writter(attr)
        define_mtthod("#{attr=}) do |val|
            instance_variable_set(:"@#{attr}",val)
        end
    end
    
    def my_attr_acessor(attr)
        self.my_attr_reder(attr)
        self.my_attr_writter(attr)
    end
        
end
    
class Cat < Amimal
    my_attr_reader :age
end
    
kitty = Cat.new
p kitty.age
```

## 封裝的三種方法 private  VS public VS protected
* 取用private方法時，請透過public方法來取用。
* 呼叫private方法時，不可以有.出現 // 早期版本Ruby不允許
* 呼叫private方法，前面不可有 self. 
* private方法，就算是在上層類別裡面被定義，下層類別都可以使用它

// private封裝
```ruby 
class Cat
    def hi
        hey      // 只要沒有"."就可以呼叫private方法
    end
    
  private
    def hey
        p "private"
    end
end

kitty = Cat.new
kitty.hey           // 這行會出錯
kitty.send(:hey)    // 這行會正確執行，表示它繞過了private的規則我沒有出現"."，所以我可以呼叫pirvate方法。
```
// protected封裝
```ruby
class Cat
    def hi
        self.hey     // 可以有"."也可以不要有"."
    end
    
  protected
    def hey
        p "private"
    end
end

kitty = Cat.new
kitty.hey          // 可以正確執行
kitty.send(:hey)
```

## 訊息與接收者
```ruby
kitty.say_hello()
```
#### 在Ruby世界繼承是單向的，且一次只能繼承一個單位
#### 此時有誕生了module這個好用的方法
* module就像海賊王的惡魔果實，吃下(include)果實可以獲得惡魔之力
* module 就是外掛在搜尋方法的路徑上。
* module 不能new / 不能繼承與被繼承
```ruby
module Flyable
    def fly
        puts "i can fly!!!"
    end
end

class Cat
    include Flyable   // 把這個fly模組加裝到Cat類別
end

kitty = Cat.new
kitty.fly            // 恭喜以後的貓都會飛了～～
```

```ruby
class Animal
end

class Cat < Animal
end

p Cat.superclass  // 印出 Animal
kitty = Cat.new
p kitty.class    // 印出 Cat
p Cat.superclass.superclass  // 印出 Object
p Cat.superclass.superclass.superclass  // 印出 BasicObject
p Cat.superclass.superclass.superclass.superclass // 印出 nil

* 故可得知最最上層就是BasicObject *
```
### Ruby 找方法源頭的流程很像 javascrpit 的__proto__的關係鏈
```ruby
p Cat.ancestors    // 找出Cat繼承哪些祖先們
                   // [Animal Oject Kernel BasicObject] 
```

### 繼承體系 與 Kernel模組
```ruby
module Flyable
    def fly
        puts "i can fly!!!"
    end
end

class Cat < Animal
    include Flyable   // 把這個fly模組加裝到Cat類別
end

p kitty.ancestors  // 會發現多出fly模組[Cat Flyable Animal Oject Kernel BasicObject]
```
## Module 是類別，所以它可以 new 物件出來
* Module = Class.new  // Ｍodule 是 Class 這個類別分類出來的"類別"

* ff = Module.new  // ff 是Module new 出來的 "模組"

## class VS superclass
* 了解類別之間的繼承關係

## extend(擴充)
* 跟includes 的差別就是 extend 是幫類別擴充module
```ruby
module Flyable
    def fly
        puts "i can fly!!!"
    end
end

class Cat
    extend Flyable   // 這邊 Cat 這個類別就擴充飛行模組
end

Cat.fly
```

## namespace(命名空間)


## 為什麼view可以使用Controller裡面的實體變數？
* 因為在Controller 裡面 正在執行的程式碼還沒做完
* erb檔案 ---> 會去找正在執行中的程式碼有宣告一個實體變數，erb有需要使用它，就會把它拿去用。














---
<侑庭>
## 物件導向程式設計Object-Oriented Programming

1. 物件導向最終目的就是在做分類,並用人類理解的方式呈現
2. Ruby 唯二不是物件就是 method , block
3. 如何去分辨不是物件 就是辦法在物件之後加東西 a 是物件在
```ruby=
 def tim
 end

 a.tim     
```

4. 類別一定是要常數,常數一定是大寫開頭


## 繼承
1. 像金字塔把方法存在金字塔頂端, 之後在這金字塔底下的都會共同有這功能

```ruby=
class Animal  #頂端
    def eat
    end
end

class Cat < Animal #向下延伸都會繼承方法
end

class Dog < Animal
end

kitty = Cat.new
kitty.eat

lucky = Dog.new
lucky.eat
```
## 初始化 initialize
1. 在new一個新的實體的時候就會直接呼叫initializa的方法,在看不見得地方一定會有一個init 
2. 在JS中是使用constructor
3. 
```ruby=

class Word     #只是沒標示,出來其實word後面還是會有接ruby內建的class 讓 word 繼承只是不會特別寫出來 ,所以只要new就一定會呼叫initialize 就算原本word裡面得class沒寫他也會一直網上找到有initialize設定的
  def initializa #在呼叫new 之後就 會直接呼叫initializa
    puts "hi"
  end 
end

tim = Word.new

```
## 類別方法

```ruby=

#單體方法 Singletion method
#只有幫kitty加 method
def kitty.hi
    puts "hihihi"
end

kitty.hi #印出hihihi
nn.hi    #印出 defind method 'hi'
```

## 類別加self
* 重點誰呼叫誰就是self ,要依照成級來看self是實體還是類別,就像JS的this 


## 當遇到兩個名字一樣的class

* 兩個會融合再一起不會壞掉,而是兩個都可以取用不會重複

<下午>

## 存取控制
* 要存取private 必須要按按鈕, 就需要把他寫在public裡面就可以拿到了

* private 就很像專利,有寫東西不想讓別人知道就會把他寫在private下面

* 取用private 前面不能有小數點,不管在public方法內 或是在外面

* 但在版本3.1.2 中 在public放法中`self.private` 是可以取得 private 的,但是當他不存在

* 如果要在外部取用 `kitty.send(:private)` 可以取的private裡面的東西

* 繼承的部分沒有所為被 private 綁在上層的問題,只要是繼承就是所有方法都可以用,但就是還是要符合private 前面不能有小數點的設定

## 模組

* 模組不像繼承,模組是需要什麼就去引入什麼,而繼承是上面有什麼我就全部都拿到不管好壞,這就是兩個的差異性
```ruby=
module Flyable
  def fly
    puts "I can fly"
  end
end

class Cat
    include Flyable #用include 引入模組
end

kitty = Cat.new
kitty.fly

```

* 用Cat.superclass 可以找到class上層
* 用Cat.ancestors 可以找到所有祖先
* 模組會在class跟class中間原本沒東西,但在某個class 可能有被 include()模組進來了 , 就會在被夾在中間
* 模組依照後進來先出
* class 比 module 多了四個方法, 多了繼承 ,跟new的功能
* superclass是分類他們都是class 去做出來 只是用模組類別new出來的模組不能繼承及new
* extand 可以擴充作用範圍把原本只做用到實體上,擴充到類別也可以用

## namespace

* R::Cat < namespace , name: < key , :name < symble  

```ruby=
module a
  module b
    class Cat
    end 
  end
end


kitty = a::b::Cat.new  
#a 類別/模組裡面有, b 類別/模組裡面有, C 類別

```


















---
<于婷>


## Ruby 唯二不是物件?
method , block

## 類別繼承使用時機

因為類別多了繼承的能力，
如果彼此間是有關連的，
甚至是要有另一類別內的所有能力，
可以考慮使用類別繼承。

例如我們產生一個動物類別會動、能吃東西，
然後我們要產生一樣會動、能吃東西的魚類別、鳥類別、狗類別，
我們就可以讓這三個類別都繼承動物類別，
這樣這三個類別所有實體都會動、能吃東西。

## initialize(初始化)

* 只要一出生就會執行（new實體時一並執行）
* initialize 多練習打～有四個i，打錯就沒有了
* 初始化行為又稱建構或建構子
* ＪＳ也有類似行為：constructor

## superclass(超類別/父類別)

```ruby
clss Animal
end

p Animal.superclass  #Object

p Class.superclass   #Module
p Module.superclass  #Object
p Object.superclass  #BasicObject
p BasicObject.superclass  #nil
```

## 實體與實體變數

實體是一種實體(位置)、也是一種變數。
實體變數是一種實體變數、也是一種變數，只活在實體方法內。

## RB沒有屬性只有方法,可以使用以下方式

```ruby
attr_accessor :age #可讀可寫
attr_reader :age  #可讀
attr_writer :age  #可寫

```

## 單體（例）方法 => 類別方法 v.s 實體方法

```ruby
def kitty.hi #單體（例）方法
  p "hi"
end

class Cat
  def self.hi  #類別方法
    p "hi"
  end
  
  def haha     #實體方法
    p "haha"
  end
  
end

Cat.hi #類別方法

kitty = Cat.new  #類別方法
kitty.hi #單體（例）方法
kitty.haha  #實體方法

```
要使用實體方法時，new出實體再使用實體方法

## 類別變數使用方式

```ruby
class Cat
  @@count = 0
  
  def initialize
    @@count += 1
  end
  
  def self.total
    @@count
  end
end

```

## public(公用的)

系統預設定義是public,
是一種開放的、誰都可以存取的方式。
```ruby
class Cat
  def eat
    puts "eating..."
  end
end

kitty = Cat.new
kitty.eat  #eating...
```
像這樣，kitty可以直接提取eat這個方法。

## Protected(受保護的)

較public不自由，
不可以直接存取，
```ruby
class Cat
  protected
  def eat
    puts "eating..."
  end
end

kitty = Cat.new
kitty.eat  #錯誤訊息
```
但不限定有沒有明確的接收者，
所以用以下兩個方法都可以成功執行。
```ruby
class Cat
  def do_eat
    eat           #沒有接收者
    self.eat      #有接收者
  end
  
  protected
  def eat
    puts "eating..."
  end
end

kitty = Cat.new
kitty.do_eat 
```

##  Private(私人的)

相對於public，
不可以直接存取，
```ruby
class Cat
  private
  def eat
    puts "eating..."
  end
end

kitty = Cat.new
kitty.eat  #錯誤訊息
```
也不能有明確的接收者，
因此在呼叫方法時不能有.小數點
```ruby
class Cat
  def do_eat
    eat           #沒有接收者,但是在ruby3版本後可寫成self.eat
  end
  
  private
  def eat
    puts "eating..."
  end
end

kitty = Cat.new
kitty.do_eat 
```

不過在ruby的世界裡，
還可以用send方法來呼叫成功喔！
```ruby
class Cat
  private
  def eat
    puts "eating..."
  end
end

kitty = Cat.new
kitty.send(:eat) #動態執行
```

## 所有的.class都是Class


## namespace

```
kitty = A::B::Cat.new
```
代表
```
module(或class) A
  module(或class) B
    class Cat
    end
  end
end
```

---