###### tags: `Ruby & Rails`
# 20221110 Rails 筆記
[TOC]
---
<政庭>
## Hash(雜湊)
* `old` h = {:name => "ccc", :age => 18} ---Ruby1.8
* `now` h = { name: "ccc", age: 18} ---Ruby1.9
```ruby
p h["name"] / p h[:name]
```
* 字串(String)跟符號(Symbol)的差別
* 符號就是"有名字的物件" 或是 符號就是一個"value"
`has_many(:books)`
`has_many(123)`
* 數字型態的 object_id 的規律 2N + 1
* 其他的型態 object_id 的規律 2N
```ruby
name = "cccc"
name[0] = "x"
p name
```
```ruby
name = :cccc
name[0] = "x"
p name
得到 undefined method '[]='。所以我們會說symbol不能更改它
```
## rails => ruby 加強版 好像魯夫 彈性很大
```ruby
params[:id]
params["id"] => nil
```
```rails
params[:id]
params["id"]
兩種寫法都ok
```
## 課堂練習題
陣列 a = [1, 2, 3, 1, 2, 1, 3, 1, 2, 3, 4, 5, 6],請計算在陣列 a 中,每個數字出現的次數。
```ruby
a = [1, 2, 3, 1, 2, 1, 3, 1, 2, 3, 4, 5, 6]
def count(arr)
arr.map{ |a| arr.count(a) }
end
p count(a) // [4, 3, 3, 4, 3, 4, 3, 4, 3, 3, 1, 1, 1]
```
## 定義方法
method 跟物件有綁定
function 跟物件沒有綁定,它自己就是一個物件
在javascript可以說,呼叫某個物件的"方法"(function)。
## 參數 VS 引數
如果不知道兩者差別,那就通通記成"參數"
```ruby
def sayhello_to(someone)
p "hello"
end
def hi
p a = 1
end
sayhello_to
hi(someone)
```
## 〈冷知識〉
```ruby
age = 18
def age
return 20
end
p age // 會得到什麼?
```
答案:
在Ruby的世界,會先找是否有定義 區域變數 或 方法
所以這題 p 印出來的結果會是 18
## 判斷方法的引數有幾個
link_to
## 在Ruby裡面所有方法都有回傳值
```ruby
def hi
1
2
3
end
這個方法回傳最後一個3並結束方法
```
## 問號 與 驚嘆號
```ruby
def is_adult?(age)
end
```
這裡暗示是要回傳 true / false
```ruby
def is_adult!(age)
end
```
這裡是要提醒這個方法有需要注意的地方
要去看手冊才知道驚嘆號是做了什麼
## 回傳 = 「交回控制權」
## 程式碼整理(模組化)
* 一個蘿蔔一個坑
* 盡量 一個方法寫在一個檔案裡面
## require VS load
`require "./檔案.rb"` //有需要方法什麼就直接引入並執行檔案內容,但只會執行1次。
`load "./檔案.rb"` //跟require的差別load幾次檔案,它就會執行幾次。
## 標準函式庫(Standard Libary)
Ruby 預設不會一起載入標準函式庫
## Rake
* Rake 是怎麼來的
* desc / namespace task 都有固定寫法
* 在linex Unix 系統 安裝套件時會輸入`make install`
* 所以在Ruby的世界,Rake = Ruby 的make ===> Rake

### 命名空間(namespace)
* 可以把task做區分
### rake -T / rake --tasks
* 查詢在rakefile 裡面有哪些任務可以使用
## rails 5 之後將指令 rake db:migrate 改成 rails db:migrate
## Block(區塊)
block = 一段不會被“主動執行”的程式碼區塊
在Ruby世界裡面,block 不能單獨存在
block可以依附在其他方法後面,但是不會執行。這樣的目的是要做非同步執行。
除非前面的方法加上 `yield` 將控制權 "讓出" 交給後面的block。
```ruby
def hey
yield 1,2
end
hey do |n,m|
puts n
puts m
end
```
這裡表示前面的 hey 方法會讓出 1,2 給後面的 do..end block,這個 block 會接那兩個引數並執行 block 裡面的程式碼,印出n ,印出m。
## block也會有回傳值
```ruby
def hi
result = yield 2
p result
end
hi do |x|
x * 2
end
```
這樣的寫法,就是讓 hi 方法 可以接 block 回傳的值再印出來
等於是讓 block 來決定 hi 方法要印出的結果。
* 所以等於 block 讓 method 變得更有彈性。
## Open Class(開放類別)
我們可以擴充原本的 Class
會用到 self
## 在 block 裡面不要加上return
* 等於在block裡面就直接結束了,不回會傳值給方法的yield。
```ruby
p list.filter(|x| x % 2 == 0)
p list.filter(|x| x.even?)
p list.filter(&:even?)
```
## bolck不是參數,要如何知道block有沒有存在?
```ruby
def hi
if block_given?
yield
end
```
加上一行 `if block_given?` 有 block 依附在後面,才會 `yield`。
## do-end 跟 { } 的差別
```ruby
p [1,2,3,4,5].map do |x|
x * 2
end
p [1,2,3,4,5].map { |x|
x * 2
}
```
do-end的結合率比較弱,所以第一個 p 要看成
```ruby
p([1,2,3,4,5].map) do |x| x * 2 end
```
第二個 p 看成
```ruby
p [1,2,3,4,5].map { |x| x * 2 }
```
這邊有發現到什麼嗎?
* 第一個 `p` 其實 `map` 被它包起來,所以後面的do-end等於完全沒有用了。
## Proc (讓block可以被物件化,這樣block就能單獨存活,這樣就能被當作參數被丟來丟去)
```ruby
add_two = Proc.new { |x| x + 2 }
p add_two.call(3) // 只要記得這個用法就好,必較常用。
#===================#
p add_two[3]
p add_two.(3)
p add_two.=== 3
p add_two === 3
p add_two.yield(3)
```
## Lambda (讓block可以被物件化,這樣block就能單獨存活,這樣就能被當作參數被丟來丟去)
```ruby
add_two = Lambda { |x| x + 2 }
add_two = -> (n) { |x| x + 2 }
```
# 在 Ruby 裡面 function 不是"物件"!!!
---
<侑庭>
## hash
```ruby
舊是 h = {:name => 'aaa' , :age => 18}
糖衣 h = {name: 'aaa' , age: 18}
p h["name"] #nil
p h[:name] # aaa
```
## symbol vs string
1. symbol 也是一種值,也是一個物件 :ccc <-冒號ccc
2. `"string" .object_id *4 //40 60 80 100 `
3. `:symbol .object_id *4 // 188 188 188 188`
4. 所有.object_id 的基數都會留給數字用,因為數字的id都會帶有2N+1的公式,所以都用單數
5. string 可視為字元陣列 可以直接用 name[0]去做取用但是這[]=也是一個方法
## 什麼時候要傳符號或是字串
看手冊,就會看到需要什麼,然後有些會有防呆機制但是就還是需要看手冊
## params 抓取物件
在controller 中很常看到
* `params[:id]`
* `params["id"]`
## method vs function
最簡單認定方式, method要有主詞 cat.eat()這樣
## 以下這段age會抓到什麼?
```ruby
age = 18
def age
retuen 20
end
puts age #印出18
#如果沒有給他method or local virble他就什麼都不是
#相對他會先找區域變數, 如果在age 後面加()就變成呼叫方法
```
## 回傳值
* 所有方法都有回傳值, 如果沒有給東西,回傳值就是nil
```ruby
def cat
end
p cat #nil
```
* puts 寫在method 裡面是沒有回傳值的會直接印出來計算結果
* method 內都會自動回傳自己最後一行的結果
## 問號與驚嘆號
* 問號 跟 驚嘆號 都只能放在方法最後面
* 問號通則只會回傳true or false , 問號只是暗示不是rails 內建間的元素
* 驚嘆號就代表這方法可能會有不同的結果, 要看一下手冊
* 通常正常的跟不同的版本的都會綁在一起
## 模組化
* 用 require './路徑位置' 就可以先把方法定義在不同檔案裡,在引入重複執行很多次的時候,require只會載入一次
* 用 load './路徑位置' 大致上相同跟require 只差在load 可以重複載入
## 標準函式庫 StdLib
* 不常用的方法所以不會在預設就載入而是會到,需要的時候再會去require近來
## 隨堂測驗
---
<侑庭> 下午場
## Rake
1. take 後面接hash的話會先做 value在做key ,task 設計問題
```ruby=
desc "text"
task :hi do
puts 'hello word'
end
#db = datebass的意思 , namespace = 命名空間,類似分類的感覺
namespace :db do
task :migrate do
puts "ok"
end
end
desc "hey!!"
task :hey => :hi do
puts "hey"
end
task :default => :hi
# task (:default => :hi) #把小括號加回去
# task ({:default => :hi})#把大括號加回去
```
2. rake 目的是為了讓製作的時候可以新增新的方法
## Block
1. 他沒有辦法單獨存在,需要依附在一個物件後面才可以,but JS可以
2. 有兩種寫法 do..end 或 {}
3. 要能讀取到block 必須在方法裡面有要yield 在能暫時把控制權轉讓出去
4. 我們常在用loop method 例如 `list.map{|n| n2}` 這樣就是map裡面有yield 在map裡面
5. 在block裡面最後一行也會自動回傳
6. block的好處? 我要任何結果都可以經過block去改變得到的答案,把彈性留在block裡面
7. block裡不能能有return 當有return過後就不會再繼續傳遞,在新版不會壞掉也不會錯,但在早期的版本就會跳出`LocaljumpError`
8. block 是一段不會被主動執行的程式碼
9. yield block_given? 可以用這方法去判斷有沒有使用block
## Block 物件化 ?
1. block 用 Proc.new
2. block 用 lamnda -> {需要晚點執行的方法}
3. 呼叫Pron.new 要用add.call() , add[], add.() , add === 3
,add.yield(3) 這些方法呼叫
4. 因為在JS裡面的function可以隨時取用,但ruby內的block 不是物件所以無法提取, 所以才要用上面兩種方式去讓他物件化,這樣比較好去提取東西
## 隨堂解析
```ruby=
class Integer #更改
alias :old_plus :+
def +(n)
self.old_plus(n)
end
end
```
---
<于婷>
## 符號v.s.字串
符號:有名字的物件,是一種值
### 1. symbol的內容不能改變
字串想要改其中一個字是可以的,但符號不行。
```
"abcd"[0] = "z" #字串變成"zbcd"
:abcd[0] = "z" #錯誤訊息
```
### 2. symbol指到同一個記憶體位置(編號)
字串每次的記憶體位置是不固定的,而符號是同一個記憶體位置。
```
5.times do
puts "abcd".object_id
end
```
結果分別是:720 740 760 780 800
```
5.times do
puts :abcd.object_id
end
```
結果都是:1920348
*每個人電腦的記憶體位置顯示都會有差異喔!
我的例子是用Replit執行的結果。
*補充:所有數字的位置都是奇數,其他放偶數。
### 3. symbol的效能比較好
程式在比較符號是否相同時,
是直接比對這物件的object_id是否相同,
但在比較字串時,
它是一個一個字母比對,
因此比較的時間會隨著字母的數量而增加。
*補充:但因為字串位置是暫存,長期來說,不會佔記憶體位置。
## hash v.s 陣列
1. 結構不同
2. 存取方式不同
3. 沒有順序
## 題目
```
a = [1, 2, 3, 1, 2, 1, 3, 1, 2, 3, 4, 5, 6]
#請計算在陣列 a 中,每個數字出現的次數。
p a.tally
```
## ruby的省略
為了更像人在講話
可「**適時**」省略(){} return(最後一行的執行結果)
### 回傳值意義?
為了交回控制權
### ?與!
1. 放在物件後面
2. ?不成文默契:回傳true 或 false
3. !不成文默契:有你意想不到的結果,要注意
## Rakefile
```
desc "這是測試" #描述
task :hi do
puts "hello world"
end
desc "hey123"
task :hey => :hi do #相依性
puts "hey!!"
end
#初始
task :default => :hi
```
## Block
不會主動執行的程式區塊,無法單獨存活。
## 題目
```
def my_map(arr)
result = []
arr.each { |x| result <<( yield x) }
result
end
list = [1, 2, 3, 4, 5]
result = my_map(list) { |x| x * 2 }
p result # [2, 4, 6, 8, 10]
```
```
def my_filter(arr)
result = []
arr.each { |x| result << x if yield x }
result
end
list = [1, 2, 3, 4, 5]
result = my_filter(list) { |x| x > 2 }
p result # [3, 4, 5]
```
## 有一些方法可以使block物件化:
### Proc
使用 Proc 類別把 Block 物件化
```
say_hello_to = Proc.new { |name| puts "hi #{name}"}
```
呼叫Proc 方式
```
say_hello_to.call("小花") # .call
say_hello_to.("小花") # .小括號
say_hello_to["小花"] # 中括號
say_hello_to === "小花" # 三個等號
say_hello_to.yield "小花" # .yield
```
### lambda
Proc 類別下,還有一個lambda可以把 Block 物件化,
但又有些許不同。
```
say_hello_to = lambda { |name| puts "hi #{name}"}
```
或是
```
say_hello_to = ->(name) { puts "hi #{name}"}
```
這兩種方式都可以把 Block 物件化,
呼叫方式和Proc 相同,
不過執行上會有些許不同
---