###### 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 // Module 是 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,打錯就沒有了
* 初始化行為又稱建構或建構子
* JS也有類似行為: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
```
---