Kurt Hsu's blog

The Rails developer in taiwan.


  • 首頁

  • 標籤

  • 分類

  • 歸檔

[Ruby] 物件導向

發表於 2020-04-03 更新於 2020-04-04 分類於 Ruby

這幾天發現我對於原生的 Ruby 並不是這麼的熟悉, 物件導向的概念也不是到真正理解, 會用不代表真正理解原理, 看了泰安大大推薦的The Ruby Object Model by Dave Thomas才真正更進一步的理解 Ruby 物件導向的宇宙觀

什麼是物件導向

關鍵字是 Object-oriented programming(簡稱 OOP), 網路上有許多說法, 但不如先說為什麼 coding 需要物件導向呢?

先敘述在沒有物件導向時遇到的問題, 假設有一個庫存系統可以純粹的寫入和讀取資料, 我們必須對每一種物品都各自定義商業邏輯再寫入和讀取, 如果有100種物品再乘上10種商業邏輯我們可能就要細心的寫出1000種程式碼了, 所以有幾個問題:

  1. 程式碼重複性很高
  2. 程式碼將以倍數成長
  3. 程式碼的擴充十分沒有彈性
  4. 程式碼將難以維護

人是會出錯的, 如果我們能讓每一種物品更智能, 可以有方法可以計算自己呢? 所以有了物件導向的概念出現了!

這個概念就是每一個 object(物品) 都有自己的 state(狀態), 以及自己的 behaviours(行為)
在 Ruby 的世界就是每一個 object(物品) 都有自己的 instance variables(實例變數) 和 methods(方法)

可以看作是每一筆資料(object)都是完全獨立的, 每個資料各自運作, 每個資料各自更新自己的 state

再來物件導向的世界有 class(類別) 的概念, 他可以製做出各式各樣的 object, 且 class 有繼承的效果, 簡單的 Ruby 程式碼範例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Animal
def eat
puts 'I need foods.'
end
end

class Dog < Animal
def speak
puts 'Woof'
end
end

class Cat < Animal
def speak
puts 'Meow'
end
end

dog = Dog.new
dog.eat # I need foods.
dog.speak # Woof
cat = Cat.new
cat.eat # I need foods.
cat.speak # Meow

我們解決了剛剛那四個問題, 物件導向的概念改變了世界!
接下來繼續探討 Ruby 的世界

self

self 在 Ruby 是極其重要的概念, 跟 JavaScript 的 this 概念很像,他代表了三件事情:

  1. 當前是哪個 object
  2. object 裡找到了哪些 instance variables
  3. 定義了 receiver 是 object 讓 methods 去執行它

而可以改變 self 的只有兩件事情:

  1. 呼叫 methods
  2. 定義 class/module

可以寫一個簡單的 ruby 檔案並執行它試試看

ruby.rb
1
2
3
4
5
6
7
8
9
10
11
puts self # main
class Duck
puts self # Duck
def speak
puts self # <Duck:0x00007fe2229284a0>
'QUACK'
end
end

duck = Duck.new
duck.speak.upcase # QUACK

$ ruby ruby.rb
當呼叫了 duck.speak.upcase 時 self = duck.speak = ‘QUACK’
而 ‘QUACK’ 這個 receiver 可以 call upcase

再來一個範例更清楚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Duck
puts self # Duck
@name = 'Duck'
def self.name
puts self # Duck
puts @name
end

def name
puts self # <Duck:0x00007ff960981a00>
puts @name
end
end
Duck.name # 'Duck'
Duck.new.name # nil

永遠要確定當前(self)呼叫的 instance variables 是哪一個 object

繼承鍊

物件導向可以繼承所有狀態和行為:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Duck
def name=(name)
@name = name
end

def name
puts @name
end

def speak
puts 'Quack'
end
end
duck1 = Duck.new
duck1.name = 'Duck'
duck1.name # Duck
duck1.speak # Quack
duck2 = duck1.clone
duck2.name # Duck
duck2.speak # Quack
puts duck2.object_id # 70335517768660

咦! 為什麼有 object_id 這個方法可以用呢? 其實他是繼承於 BasicObject 這個 class 而來的

1
2
3
4
puts duck2.class # Duck
puts duck2.class.superclass # Object
puts duck2.class.superclass.superclass # BasicObject
puts duck2.class.superclass.superclass.superclass # nil

繼承的特性是當前的 class 找不到 method 的話就會往 superclass(父類別) 去找, 而到最上層的父層(BasicObject)都找不到的話就會回傳 undefined method

P.S. 當呼叫 BasicObject.superclass 會回傳 nil 是因為再上去關聯不到東西了所以才回傳 NilClass 的 object

可以測試 object_id 這個 method 是否真的來自於 BasicObject

1
BasicObject.instance_methods.respond_to?(:object_id) # true

所謂的 instance_methods 是由這個 class 實例出來的 object(instance) 可以使用的方法(methods), 所以也可以看到

1
Object.instance_methods.respond_to?(:object_id) #true

singleton class

singleton class 也可以稱呼–匿名class, 他是一個蠻抽象的概念, 甚至 call superclass 也找不到他, 延續 Duck 例子:

1
2
3
4
5
6
7
duck1 = Duck.new
duck2 = Duck.new
def duck1.run
puts 'I am running'
end
duck1.run # I am running
duck2.run # NoMethodError

剛剛提到的繼承要找尋 run 這個 method 的順序應該是
Duck > Object > BasicObject
那 run 呢??

找尋 method 的順序沒有特例也沒有例外, 只是 ruby 會新增一個 singleton class 來放這個 method, 所以其實順序如下:
Duck > Singleton > Object > BasicObject

這個 singleton 屬於每一個 object, 他們是完全獨立的一層 class, 但模式沒有特例也沒有例外, singleton 也跟任何 class 一樣是可以繼承的

1
2
3
4
5
6
7
duck1 = Duck.new
def duck1.run
puts 'I am running'
end
duck2 = duck1.clone
duck1.run # I am running
duck2.run # I am running

everything is object

第一個主題提到 物件導向的世界有 class(類別) 的概念, 他可以製做出各式各樣的 object, 白話的來說, object 就是 class 的產物, 包括所有 class

聽起來很玄對吧! 可以來玩玩看, 物件導向提到最初的概念是, 每個 object 都有自己的 instance variables(實例變數) 和 methods(方法), 我們來對 Object 加點料

1
2
3
4
5
6
7
8
9
class Object
@name = 'HeyHeyHey'

def self.name
@name
end
end

puts Object.name # HeyHeyHey

所以其實沒有 class method 這種東西, def self.name 會讓 Object 這個 object 新增了一層自己的 singleton, 不過現在這種稱呼方法已經很普遍了, 在溝通上知道在講什麼就好

最後還有一個比較語意話的證明方法:

1
2
3
4
5
6
7
8
9
String.superclass # Object
Hash.superclass # Object
Array.superclass # Object
Time.superclass # Object
Numeric.superclass # Object
class Dog; end
Dog.superclass # Object

Object.superclass # BasicObject

所以萬物的最上層 class 都是 BasicObject, 所以所有東西都可以呼叫 object_id

include vs extend

再來回顧找尋 methods 的順序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A
def self.speak
puts 'A hello'
end

def self.run
puts 'A run'
end

def sleep
puts 'A sleep'
end
end

class B < A
def self.run
puts 'B run'
end
end
B.run # B run
B.speak # A Hello
B.new.sleep # A sleep
B.new.class.superclass.speak # A Hello

其實 B < A 這個動作即是在 B 的找尋順序上加上了 A 的順序, 現在 B 的找尋順序如下:
B > B singleton > A > A singleton > Object > BasicObject
不管是 instance methods 還是俗稱的 class methods都是一樣, 只是 instance methods 只會去找 instance methods, 反之 class 只會去找 class 的

再來看 include vs extend, 其實就也只是在找尋順序上多插了一條路線而已, 只不過 include 是安插在 instance methods 上, extend 是安插在 class methods 上, 如果程式碼改成這樣:

1
2
3
4
5
6
7
8
9
10
11
12
class A; end

module C
def speak
puts 'C Hello'
end
end

class B < A
include C
end
B.new.speak # C Hello

這時候找尋順序為
B > B singleton > C > A > A singleton > Object > BasicObject

open object

我們可以重複宣告相同的 class name, 整個 class 不會被覆蓋掉而是再疊上去, 可以把它當作是一個打開同一個 object 在賦予新的東西的概念

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
def self.speak
puts 'Hello'
end
end

class A
def self.run
puts 'Running'
end
end

A.speak # Hello
A.run # Running

所以只要名稱正確, 基本上可以在任何地方打開正確的 object 再給予新東西

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A; end

class B
class ::A
def self.speak
puts 'Hello'
end
end

class A
def self.run
puts 'Running'
end
end
end

A.speak # Hello
Object::A.speak # Hello
puts A == Object::A # true
B::A.run # Running
puts A == B::A # flase

雙冒號(::)會從最外層開始找尋, 其實最外層的 namesapce 就是 Object
如果沒有雙冒號的話在哪個 Object 底下打開的 Object 的 class name 則會被灌上 namespace

參考資源

The Ruby Object Model by Dave Thomas
Ruby 使用手冊
Ruby 的繼承鍊 (1) - 如何實踐物件導向

[Rails]開啟 model unit test 之路

發表於 2020-03-07 更新於 2020-03-08 分類於 Rails , RSpec

最近練習了一些unit test, 這邊直接用一個新專案紀錄基本備置和用法

環境

  • Rails 5.2.4
  • Ruby 2.4.4

安裝的 gem & 注意事項

  • rspec-rails
  • factory_bot_rails
  • simplecov

先設定 Gemfile

Gemfile
1
2
3
4
5
6
7
8
9
group :development, :test do
gem 'pry'
gem 'rspec-rails'
gem 'factory_bot_rails'
end

group :test do
gem 'simplecov', require: false
end

$ bundle install
$ rails generate rspec:install -> 會多spec_helper.rb
設定SimpleCov.start在建立測試環境時越早載入越好
在configure引入FactoryBot所有Methods

rails_helper.rb
1
2
3
4
5
6
7
8
9
10
11
12
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require 'simplecov'
SimpleCov.start

#... 略

RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods

#... 略
end

$ rspec
第一次跑rspec時, SimpleCov會製作一包coverage/是他需要的素材, 建議ignore掉沒關係

.gitignore
1
coverage/

最後, 以後下$ rails g xxxx yyyy都會建立該rspec檔案

Unit test心法

  1. unit test 的原則為 AAA
    • Arrange: 安排 => 設定好 let, allow 等會用到的東西
    • Action: 執行 => 這點還沒參透, 總覺得執行不就已經寫在 method 裡面了嗎?
    • Assert: 結果 => 就是白話, it expect等
  2. 由淺入深有 3 個 level, 會深深影響 coverage
    • method => 最單純的情況, 大多是運算或者是屬性
    • branch => 最簡單的 if else 有兩條路要走, 都要測到
    • condition => if a > b || b < c 等, 會有很多細微的排列組合
  3. 優先只要測 public method 就好
  4. 如果測試很難寫, 很有可能是程式碼寫得不好, 這邊是說真正在執行的程式碼, 不是 rspec 的

基本用法

我們先建立最簡單的 model User(我這邊用devise建立), 只有 email 和 age 兩個欄位

app/models/users.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable

class << self
def all_adults
User.where(["age >= ?", 18])
end
end

def username
email.split('@').first
end
end

model 建立好之後做 FactoryBot

spec/factories/users.rb
1
2
3
4
5
6
7
8
FactoryBot.define do
factory :user do
email { 'rails@gmail.com' }
age { 10 }
password { 'password' }
password_confirmation { password }
end
end

最後寫對於 User 這個 model 寫 unit test

spec/models/user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
require 'rails_helper'

RSpec.describe User, type: :model do
describe '.all_adults' do
context 'when users age greater than 18' do
subject { User.all_adults }
let!(:user1) { create(:user, email: 'user1@gmail.com', age: 17) }
let!(:user2) { create(:user, email: 'user2@gmail.com', age: 18) }
let!(:user3) { create(:user, email: 'user3@gmail.com', age: 20) }

it { is_expected.to eq([user2, user3]) }
end
end
describe '#username' do
context 'shold return username by email' do
subject { user.username }
let(:user) { create(:user, email: 'kurt@gmail.com') }

it { is_expected.to eq('kurt') }
end
end
end

unit test慣例1: describe

心法3提到基本上都先測 public method, 而 class 等級的為., instance 等級的為 #

1
describe '.class_public_methods'

1
describe '#instance_public_methods'

unit test慣例2: context

我們新增一個簡單的 instance method(先不論這判斷很多餘XD)

app/models/users.rb
1
2
3
4
5
6
7
def invalidated_age?
if age < 0
true
else
false
end
end

剛剛心法2有提到三個等級method, branch, condition, 這邊比較單純的到 branch 而已, 通常用 with, when 當開頭

spec/models/user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
describe '#invalidated_age?' do
subject { user.invalidated_age? }
let(:user) { create(:user, email: 'kurt@gmail.com', age: age) }

context 'when user age greater than 0' do
let(:age) { 10 }

it { is_expected.to be_falsey }
end

context 'when user age greater than 0' do
let(:age) { -10 }

it { is_expected.to be_truthy }
end
end

當然很多時候會有巢狀判斷, context 也一樣巢狀測試下去就行了

unit test慣例3: mocking

unit test 專注於一個 method 測試一個東西得到一個結果, 所以 mocking 可以略過其他非重點流程 or 變數

假裝我有一個 class method 要找最出的變種人

app/models/users.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class << self
def the_very_first_mutant
mutants = all_mutants
egypt_mutants = find_egypt_mutants(mutants)
blue_skin_mutants = find_blue_skin_mutants(egypt_mutants)
blue_skin_mutants.order(age: :desc).first
end

def all_mutants
# some coding...
end

def find_egypt_mutants(mutants)
# some coding...
end

def find_blue_skin_mutants(egypt_mutants)
# some coding...
end
end

這個 method 是用 orm 一步步找到最初變種人, 但找的過程不重要, 且很確定最後一個找的條件(method) find_blue_skin_mutants 一定是給我一個 AR array, 所以我直接在測試 mocking 如下即可:

spec/models/user_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
describe '.the_very_first_mutant' do
subject { User.the_very_first_mutant }

let!(:user1) { create(:user, email: 'user1@gmail.com', age: 5000) }
let!(:user2) { create(:user, email: 'user2@gmail.com', age: 50) }

before do
allow(User).to receive(:find_blue_skin_mutants).and_return(User.all)
end

context 'when user is first blue skin mutant in egypt' do
it do
is_expected.to eq(user1)
end
end
end

檢查 coverage

最後在終端機下 $ open coverage/index.html 即可看到 test coverage 的頁面, 由 simpleCov 這個 gem 產生

後記

本身寫的還不夠多, 一開始想掌握 unit test 花了不少時間所以想記錄下來最基本的狀況, testing 肯定還有更多更複雜的狀況會發生要去判斷, 永遠要記住 unit test 單位測試顧名思義就是每次只測一件事情得到一個結果, 沒有一定或最漂亮的寫法, 但 unit test 不要太過複雜化

[生活]Rails找第二份工作&成為曉數碼的一份子

發表於 2020-03-07 分類於 生活 , 找工作

Rails找第二份工作紀錄

目標:

產業: 不限制
語言: Ruby on Rails
工作內容: 不限制(意思是開發產品 or devops都可以)
薪水: 70~80w/Y

No.1
名稱: BoniO Inc.(幫你優股份有限公司)
職位名稱: Ruby on Rails Developer
結果: 感謝函
面試感受: ★★★★☆
自我評價: ★★☆☆☆

這是這波面試的第一間, 自己沒有表現得很好, 一開始筆試, 後來會先快速討論考券上的問題, 並且會主動提問有沒有想問的, 流程下來非常輕鬆蠻像平輩之間的聊天, 面試官會嘗試著問履歷上或依據我回答再延伸下去看能討論得多深
我是沒特別緊張但卻對有些很基本的題目突然回答不出來XD, 面試也是需要多實戰的

No.2
名稱: Meet.jobs
職位名稱: Software Engineer (Web Application)
結果: 感謝函
面試感受: ★★★★☆
自我評價: ★★★☆☆

看似簡單給予一個禮拜時間的作業其實有很多小細節我沒做好, 最後給我的評價是他們希望找比較偏資深的工程師, 回應者很 nice 的有點出我交的作業有哪幾點是他們覺得不足的地方, 並且在我問一些問題的時候也給我很詳細的回應, 這點我覺得很感謝
這個作業真的花了我很多時間去做, 可惜的是一開始我做的方向有點錯誤所以假日的部分有點浪費掉, 導致後面蠻多細節都沒有做好, 這時候我才認知到我只是有經驗的 junior

No.3
名稱: Akatsuki (曉數碼)
職位名稱: Server Engineer
結果: Get offer
面試感受: ★★★★★
自我評價: ★★★★☆

獵人頭介紹的職缺, 公司算有規模, 流程上真的是一關一關來, HR > team leader > CTO > CEO 總共分兩次面試, 且 HR 與 CEO 部分需要用到英文面試, 每個流程上都蠻專業的, 他們著重於是否真的了解這個職位和是否真的是自己想要的, 還有很注重於公司的文化團隊的氣氛是否能接受
英文面試真的準備非常久, 伺服器工程師確實我較不熟悉, 一面跟 team leader 聊資料庫處理以及用了許多第三方服務我那時候有點沒辦法聊太深, 但他也很願意多分享, 二面過了蠻久才開始, 但也與 CTO > CEO 也十分流暢, 最後薪資也算談的十分順利

No.4
名稱: Easyship (一起寄有限公司)
職位名稱: Mid-level Back End Developer
結果: 無聲卡
面試感受: ★★★☆☆
自我評價: ★★★★☆

外商公司, 一面聊得算愉快說會發作業再來二面, 然後隔天主管出國好像一個禮拜左右好像就忘了我QQ
蠻特別的一個面試, 考 Rails 的方式是在投影片上 show 程式碼然後解釋給他們聽 or 找出奇怪的地方和錯誤 or 有沒有更好的寫法, 我個人是覺得一面聊的還 ok, 後來回去問也說一面感覺 ok, 但主管回來後聽到我剛拿到 offer 就不了了之了, 可能是這個職缺可找可不找吧

雖然命中率不是很高, 本來也準備多投幾家(因為那時候感覺曉數碼也是一半一半XD), 不得不說幾家面試都還蠻不錯的, 沒遇到特別糟糕的面試情況, 但也讓我知道我自己的缺點, 果然去面試完就知道自己的能力和行情在哪了!

成為曉數碼的一份子

到職也三個禮拜了, 除了第一個禮拜有一半以上的時間在做新人訓練外, 對自己的表現還沒到很滿意, 不論是更多的AWS服務, 數台server管理以及部署的流程, 許多的第三方服務等等讓我覺得我又變成rookie= =, 就連rspec我都花了不少時間去學unit test(原來T客邦真的是寫的很簡單啊XD), 其實真心有點挫折, 但確實幾乎每天都在成長, 有時候會感到害怕和失落然後有些不好的習慣和心態又會跑出來, 主管跟我兩次1 on 1 talk也幫我點出了一些問題, 再來還是得調整一下, 振作一點調整好自己的心態和目標繼續去衝唄, 再來有過試用的話再多發點心得文吧 lol

[Devops]使用ansible-vault加解密

發表於 2020-02-29 分類於 Devops

ansible是一套基於Python寫的自動部署工具(指令為ansible-playbook), 主要是用來管理機台環境, 這篇主要紀錄簡單使用ansible-vault加解密

安裝

$ brew install python3
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ python3 get-pip.py
$ pip3 -V
以上確認安裝pip3, 是一套python套件管理工具
$ pip3 install ansible
$ ansible-vault --version

使用

$ ansible-vault encrypt file_name
然後輸入兩次相同的密碼就可以看到該檔案被加密
$ ansible-vault encrypt file_name1 file_name2 file_name3
可以同時加密一堆檔案

$ ansible-vault decrypt file_name
然後手動輸入密碼即可解密該檔案, 但這會直接把檔案變成明文, 所以解密使用玩要非常小心不要外洩
$ ansible-vault decrypt file_name1 file_name2 file_name3
當然也可以同時解密一堆檔案

$ ansible-vault view file_name
這不會把檔案變成明文, 只會less明文
這樣就不用每次解密完就得再加密回去, 除了很麻煩之外也怕設密碼的時候有問題, 例如只是要解密pem檔複製貼上而已就非常方便

$ ansible-vault decrypt file_name --output new_file
把解密的東西產生新的檔案, 不會把原本加密的檔案變明文

$ ansible-vault view file_name --vault-id pwd_file
也可以建立存放密碼的檔案, 然後用該檔案去解密

$ ansible-vault rekey file_name
則可以更改密碼

參考文章

ansible 官網
現代 IT 人一定要知道的 Ansible 自動化組態技巧

[讀書心得]極簡閱讀

發表於 2020-01-23 更新於 2020-02-03 分類於 讀書心得

資訊時代的資訊量爆炸, 但人生要不斷的藉由學習和開闊視野投資自己, 讀書是非常划算的一種選擇, 但掉上班和通勤所花的時間和精力佔了一整天可用的一半以上, 如何有效的運用剩下的時間和精力我想已經變成資訊時代的一種必學技能

重點記錄

  1. 學習者不具備學以致用的能力, 所以才用有人說讀萬卷書不如行萬里路, 但事實上是在資訊爆炸的時代我們不知道欠缺的知識是什麼, 需要的東西是什麼, 知識焦慮造就了我們買了一堆書, 訂閱了一堆知識影集, 買了一堆線上課程卻只是給自己心安, 並沒有真正讀完過, 讀書有分幾種目的去讀, 先想好讀了這本書是有什麼樣子的目的, 而我讀的此書是以『解決問題』的目的去讀

  2. 讀書三大困難點

    • 沒時間和精力
    • 抓不到重點看不懂
    • 看完了卻沒有內化成為自己的一部分(也就是看完後自己做事的方式或思維完全沒改變)
  3. 我們容易不認真看待一份知識, 因為在資訊爆炸的時代隨手可得的知識邊際效益已經被縮到極小

  4. 分為三種等級的學習者

    • 初級學習者: 學習方式很原始, 就像學校的填鴨式教育對書上內容較不會反思及應用
    • 高級學習者: 能反思問題以及內化在自己的生活經驗上面
    • 學習促進者: 除了能拆解生活經驗上的問題, 歸納出要點, 不斷更新自己的能力和知識, 引領大家反思和創造多贏的局面
  5. 作者有交一個便利貼內化法, 分為:

    • I: 一段故事以自己的話敘述
    • A1: 這段故事讓你聯想到自己什麼經驗
    • A2: 這讓你想做什麼改變, 寫下來並且量化, 貼在冰箱上試著去改變
      其實這就是把內化的過程實體化出來, 我覺得對想變成高級學習者的人非常有效
  6. 『附會舊知』是作者提出的一個理論, 我們都會以既有的知識去解釋新的知識來消除我們對新知識的不安, 就會造就有人覺得這幾百本書都只是在講同樣的道理, 進而排斥接受新知識

  7. 作者還提到另一類型排斥新知的例子還蠻有趣, 總覺得事情就應該是這樣子, 這樣子做就對了, 做不到肯定是不夠努力以前別人就可以為什麼我們不行, 這種強烈使用感性加上『附會舊知』的人身邊還算不少

  8. 附會與類比是完全不同的東西, 好的類比可以促進學習, 例如顧問也像是醫師的角色, 詢問哪裡出了問題並且提出解決方法, 這個好的類比可以快速學習顧問像是什麼樣的角色, 但如果直接把顧問所有的專業就跟醫師一樣而已這樣混為一談的話就偏向附會了

  9. 學會對所有資訊反思, 才能內化成自己的知識,

    • 連結自己的經驗: 是否有前車之鑑以及前因後果可循, 批判思維去挑戰他這是真的嗎
    • 如何使用它: 使用了會對自己有什麼改變, 不使用會有什麼改變, 使用過頭或相反的實用他會有什麼改變, 使用成本是自己可以接受的嗎? 周邊有資源可以使用它嗎?
    • 分別出它與自己的舊知識: 如果這真的是一條對自己有用的新知識, 能否區分它以之前的知識有何不同
  10. 如果一段資訊能用自己的話敘述出來卻沒辦法聯想到自己的經驗, 有可能自己還不需要這些資訊

  11. 建構知識體系的過程是我們會針對一個問題去學習一個知識點, 再由這個知識點去延伸出其他知識點形成知識網, 最後整合內化將這個網內化收縮成點, 成自己的一套知識, 每個人的內化知識的知識體系過程一定不一樣, 但大多會有三個過程, 這三個過程範例如下:

    • 提出問題: 為什麼運動對身體有益
    • 找到各個知識點並形成網: 因為可以促進新陳代謝, 降低心血管疾病等等
    • 內化收縮成一個點: 經由網上的各種知識點, 確實掌握了運動對身體有益
  12. 我們可以先多強化以下三種能力:

    • 寫作能力: 可以更精準的敘述和找到問題
    • 聯想能力: 可以更加強新知識帶來的體驗與思考力
    • 教學能力: 最有效的內化知識方式
  13. 每個人要找到最適合自己學習的核心能力, 有人提倡學後做, 有人提倡做中學, 其實兩個都沒錯, 視當時情況找到最適合自己的比例, 除了有好的內化知識能力之外, 找尋適合自己資訊的能力也是核心能力之一

  14. 學習促進者不只是拆書就好, 要抓到重點, 引起對象經驗或情感上的共鳴, 想辦法引領他們思考重點, 才能真正傳授自己的知識改變他們的作法

  15. 大多人不喜歡承認自己的不對以及相信比自己資淺人, 所以會不跟著自己的重點反過來說服他人為什麼不行或做不到, 甚至會把整個團隊帶往不正確的地方發展

心得

在重點4的時候提到如何成為學習促進者其實還蠻困難的, 作者舉了一個例子是孩子問:『為什麼水會往下流動?』
當爸爸回答:『因為水是液體』的標準答案卻抹殺了孩子反思的機會
作者覺得更棒的方式是給孩子更深層的問題, 『那為什麼水變成冰就部會流動了呢?』『我們一起去詢問流體力學老師好嗎?』
個人認為這種方式雖然很好但挺困難實行的, 第一是學習促進者的技能非常之難培養, 第二是在資訊爆炸的時代下已經我們長期在填充式教育的學習下面對這種事情通常會是非常沒耐性以及厭煩的

其實在生活上成為學習促進者很重要, 也與生活息息相關, 書本上最後舉了非常多的例子, 主要都是讓對象引起對象經驗或情感上的共鳴, 想辦法引領他們思考重點才能真正傳授並且讓對象內化新的知識

對於重點15我覺得講得非常好, 在自己老婆的工作上確實看到血淋淋的例子, 但老婆相較之下只是沒有權力的下屬, 在面對說服他人和老闆的能力較強, 又只想自保和邀功以及不懂狀況的老闆來說老婆確實很難去改變團隊的重點狀況, 要怎麼去解決確實需要更大量的知識強度以及情感自信

最後這本書讀完只要身為高級學習者以上等級的人我十分推薦, 甚至想成為高等學習者以上的人也很推看這本書, 確實給了我許多想法, 但也確實像書上講的如何養成內化為自己的知識並且改善和讓自己的生活更好也不是一件容易的事情!

[Devops]Simple ways to clean sever space

發表於 2020-01-19 更新於 2020-01-20 分類於 Devops

最近自己做的一個小網站放在免費的EC2磁碟空間只有7.7G, 加上因為虛擬記憶體不夠用又分給swap 1G, 基本上網站run起來後可用磁碟空間只剩不到1G, 要怎麼善用每個空間變成一個很有趣的問題

基本ubuntu指令

  1. 查看硬碟空間: $ df -h

    /dev/xvda1 7.7G 7.3G 484M 94% /
    代表總共7.7G的硬碟我已經用掉了94%

  2. 找出大於指定大小的檔案: $ sudo find / -type f -size +80M -exec ls -lh {} \;

    +80M是大於80M以上的檔案, 可以自己改動, 我是從80M開始找

  3. 顯示該資料夾下所有檔案的大小: $ ls -lh

專案log處理

在ubuntu的機台上就可以對指定的log做一些設定
首先先找到專案production.log位置
$ cd your_app/current/log
顯示路徑並且複製起來
$ pwd
再來切換到root比較方便做事
$ sudo su -
找到設定檔
vim /etc/logrotate.conf
拉到最下面新增以下

1
2
3
4
5
6
7
8
9
/home/user/your_app/current/log/production.log {
daily
missingok
rotate 7
compress
delaycompress
notifempty
copytruncate
}

  • daily – 每天都執行Rotate, 也可以設定每個禮拜(weekly)或者每個月 (monthly)
  • missingok – 找不到log就忽略沒關係
  • rotate 7 – 設定留下幾天的Rotate, 這邊是7天
  • compress – 使用GZip壓縮Rotate的檔案
  • delaycompress – Rotate the file one day, then compress it the next day, 這樣才不會干擾Rails server
  • notifempty – 如果logs是空的就不執行Rotate
  • copytruncate – 新增完壓縮檔後就把原本的logs檔案內容清空而不是刪除, 這樣也可以確保原本寫入的log檔案一直存在才不會出錯, 如果沒有設定這個每次Rotate完都要重啟rails server

再來可以等隔天或直接執行$ /usr/sbin/logrotate -f /etc/logrotate.conf然後到專案current/log底下查看結果

參考文章:
Rotating Rails Production Logs with LogRotate

我有另外找到可以防止test or deveopment log太肥的做法, 但似乎不適用於production:
Limit the size of your Rails test and development logs

清理套件管理APT沒用到的東西

在做之前可以下一下$ df -h查看, 然後清理完後再下$ df -h做比對
$ sudo apt-get autoremove
$ sudo apt-get autoclean
$ sudo apt-get clean

  • clean: clean clears out the local repository of retrieved package files. It removes everything but the lock file from /var/cache/apt/archives/ and /var/cache/apt/archives/partial/. When APT is used as a dselect(1) method, clean is run automatically. Those who do not use dselect will likely want to run apt-get clean from time to time to free up disk space.

  • autoclean: Like clean, autoclean clears out the local repository of retrieved package files. The difference is that it only removes package files that can no longer be downloaded, and are largely useless. This allows a cache to be maintained over a long period without it growing out of control. The configuration option APT::Clean-Installed will prevent installed packages from being erased if it is set to off.

  • autoremove: is used to remove packages that were automatically installed to satisfy dependencies for some package and that are no more needed.

直接把參考文章的說明貼上

參考文章:
https://askubuntu.com/questions/3167/what-is-difference-between-the-options-autoclean-autoremove-and-clean

清理APT cache

先查看cache檔案大小
sudo du -sh /var/cache/apt
延續上面的指令

  • sudo apt-get autoclean - 清理過期用不到的package cache
  • sudo apt-get clean - 清理全部

清理 journal logs

Linux系統也會紀錄機台上發生了什麼事情, 可以參考various services in Ubuntu

如果真的不需要的話可先下指令查看log檔案多大
$ journalctl --disk-usage
或者清除老舊於指定天數的log, 例如3天
$ sudo journalctl --vacuum-time=3d

清理舊版本的Snap applications

這個倒是佔了我不少空間, 先下以下指令查看佔用多少空間
$ du -h /var/lib/snapd/snaps
/var/lib/snapd/snaps/partial 沒什麼關係
主要是/var/lib/snapd/snaps蠻大空間的

我們可以找個地方寫個shell檔案直接清理
$ vim snaps_cleaner
寫入以下代碼

snaps_cleaner
1
2
3
4
5
6
7
8
#!/bin/bash
# Removes old revisions of snaps
# CLOSE ALL SNAPS BEFORE RUNNING THIS
set -eu
snap list --all | awk '/disabled/{print $1, $3}' |
while read snapname revision; do
snap remove "$snapname" --revision="$revision"
done

$ wq存檔後離開
$ ./snaps_cleaner直接執行它
$ du -h /var/lib/snapd/snaps在查看一次是否所佔空間有減少了

心得

以上幾個方法簡單又可以直接清空間, 發現cache檔和log檔真的是空間殺手呀!
7.7G的硬碟空間真的是要斤斤計較, 很多套件產生cache的機制真的沒有去研究過, 也深怕刪除了會影響機台什麼, 不過以上方法應該是最簡單又最沒有影響的手法了

統整參考文章

7 Simple Ways To Free Up Space On Ubuntu and Linux Mint
Rotating Rails Production Logs with LogRotate
Limit the size of your Rails test and development logs
https://askubuntu.com/questions/3167/what-is-difference-between-the-options-autoclean-autoremove-and-clean

[讀書心得]富爸爸有錢有理

發表於 2020-01-18 更新於 2020-01-23 分類於 讀書心得

重點記錄

  1. 此書將人分成四種象限:

    E象限 -> 受雇者也就是上班族
    S象限 -> 自由工作者, 接案或中小企業老闆
    B象限 -> 大型企業老闆(500員工以上)
    I象限 -> 投資者
    收入來源哪個比重最多就是待在該象限的人, 例如我目前完全在E象限

  2. 想清楚為什麼想賺錢, 更多的時間陪家人以及教育小孩, 可以回饋社會, 而不是花了一輩子大部分的時間無目的的追著錢跑

  3. 四個象限的思維:

    E象限 -> 請給我好的工作保障和福利, 工作越穩定越好
    S象限 -> 有多少錢做多少事, 我可以做到最好, 我很有自己的想法, 但與B最大的不同是越成功越忙
    B象限 -> 我可以領導各象限的菁英為我創造一個系統, 與S最大的不同是越成功越有時間
    I象限 -> 用錢賺錢, 錢在為我工作, 這些收入以後不會花太多我的時間

  4. 財富不在於身上有多少錢, 而是在於這些錢能讓自己生活過上多少時間, 為自己買下多少時間

  5. 認識以及控制風險, 時間之報酬率才是最大的風險, 如果自己會投資可以算出一段時間內可承受或者可獲利的結果以及風險, 這是很好的認識以及控制風險, 許多人依賴退休計畫, 或者把錢交給投資顧問, 在這最大的風險是要到退休之後才會知道這樣的做法是否成功, 政府可能會舉債太多而倒或扣退休金, 投資顧問的失敗對他們也沒損失, 但對我們來說將是退休不可承擔的結果

  6. 收入是需要主動付出自己的時間和經歷獲得的資金, 資產則是被動式獲得資金, 工作獲得收入是必然的, 但高收入意味著需要付出更多的時間和精力, 最後終究是資產讓人獲得自由

  7. 如何成為B ,成為B的好處之一是更能掌控I, B要能自己創造一個系統, 成為B的方式有幾種:

    1. 讀管理學校, 認識成為B的技能和要素
    2. 找一位導師
    3. 加盟一個已經成功的系統
  8. 投資者有分七個等級, 先不詳細介紹, 我們需要趁早達到第四級 -> 長期投資者, 找出適合自己長期都投資組合, 有效地控制花錢, 不要跟較低級別的投資者依樣純儲蓄, 或把投資當作賭博而怨天尤人, 甚至不斷借錢購買許多負債(奢侈品, 好車, 立馬有房子等), 而第五級是老練的投資者和第六級資本家, 是除了有龐大的資本之外擁有許多金融知識和經驗等

  9. 認清楚什麼是資產和負債, 什麼是建議以及事實, 當有人建議貸款買一棟房子, 因為他會漲, 這是一筆增加負債(貸款)的建議(還沒漲就不是事實), 除非貸款的當下就可以馬上租給別人抵過每月貸款, 他才會變成資產之事實

  10. 成為 -> 去做 -> 擁有才是最正確的的方式而不是倒過來

    正確的的方式: 我想成為什麼人 -> 需要做什麼事情 -> 所以擁有了什麼
    相反過來則是: 我有了什麼 -> 只能做什麼樣的事情 -> 理所當然成為別種人

  11. 每個象限的核心觀念都不一樣, 所以合作起來會彼此衝突, 即使是夫妻, 況且我們的情感力量是理性力量的24倍, 這說明光改變自己的核心觀念是很困難的, 更不用說要去改變別人核心觀念

  12. 貸款兩句原則

    個人借貸要保證金額較小, 如果金額大要保證有人幫自己支付(例如貸A租B)
    如果自己舉債且承擔所有風險, 自己應該得到支付(舉債要舉資產)

  13. 訓練自己擁有B和I的思維是要制定目標後一步一步的來(例如我想在明年有每個月有多少的被動式收入), 但最重要的是開始去做

  14. 完全掌控自己的現金流才是最重要的, 先支付自己才去支付生活開銷, 意思是每個固定存下或投資一訂的金額, 接下來才是支付開銷, 所以我們也得精準的控制開銷

  15. 分別出什麼是好的債務和壞的債務

    好的債務 -> 別人來幫自己還債(買A租B)
    壞的債務 -> 存粹貸款和還錢(貸款買車, 自用房, 手機等不能創造收入的負債)

  16. 分得出什麼是資產, 什麼是負債

    資產 -> 錢流入自己口袋
    負債 -> 前流出自己口袋

  17. 成為找到問題解決的投資者類型, 那些問題是其他人現金流上的問題, 所以他們的負債將會變成自己的資產

  18. 擁抱失敗, 傾聽自己內心的聲音, 許多人責怪別人而不是改變自己(例如我的愛人不會理解我, 但卻不是說我的溝通技巧沒讓愛人理解我), 我們要積極和有信心, 即使有壞結果, 也是獲取了寶貴經驗然後離成功更進一步

心得

在我的身邊確實很少有這些思維的人, 自己算是蠻標準老鼠賽跑的類型, 現在還不是太晚, 好好思考一下自己想成為什麼樣子, 馬上計畫啟動去做, 才有可能搭上現金流的快車道

[Rails]Know the gem meta_tags

發表於 2020-01-07 更新於 2020-01-08 分類於 Rails , Gem

其實 SEO 一直都很簡單, 叫流行的 gem 大概是 MetaTags 和 SEO Helper, 這篇記錄一下怎麼使用以及原理

參考環境

ruby -> 2.6.3
rails -> 5.2.3

安裝

在 Gemfile 新增後 bundle install
再來一行指令 $ rails generate meta_tags:install
但我不知道為什麼我安裝失敗, 直接去複製官方網站的meta_tags.rb到自己的config/initializers/也可以

基本使用

有三種使用方法

  1. 直接在 view 使用
page.html
1
2
3
4
5
<head>
<%= display_meta_tags(title: 'Member Login',
description: 'Member login page.',
keywords: 'Site, Login, Members') %>
</head>

好處是最簡單, 壞處當然是比較沒彈性, 流程和判斷基本上要包在 helper 裡面不然很肥一包在 html 裡面降低維護性

  1. 在 controller 使用
page.html
1
2
3
<head>
<%= display_meta_tags %>
</head>
page_controller.rb
1
2
3
4
5
def show
set_meta_tags(title: 'Member Login',
description: 'Member login page.',
keywords: 'Site, Login, Members')
end

注意 view 裡的 display_meta_tags 帶參數的話會覆蓋過去, 因為 view 的helper 是比 controller 的晚跑

好處是很好處理邏輯, 即使沒有 set_meta_tags 也不會發生什麼事, 很彈性, 我比較喜歡這種方式

  1. 在 model 使用
page.html
1
2
3
<head>
<%= display_meta_tags %>
</head>
document.rb
1
2
3
4
5
6
7
8
class Document < ApplicationRecord
def to_meta_tags
{
title: title,
description: summary,
}
end
end
page_controller.rb
1
2
3
4
def show
@document = Document.find(parmas[:id])
set_meta_tags @document
end

好處是跟 controller 一樣, 而且把邏輯整理到 model 裡面更簡潔, 就看團隊覺得這種事在 controller or model 哪裡做比較好即可

運作原理

  1. 最終 render meta tags 的 method 是 view_helper.rb 的 display_meta_tags
  2. 所有的操作都是在更新 @meta_tags 這個由 MetaTags::MetaTagsCollection 這個 class 所生成的 instance value
  3. 然後用 MetaTags::Renderer 去生成 html meta tags(會在 display_meta_tags 被呼叫)
  4. 而更新 @meta_tags 的 method 是 controller_helper.rb 的 set_meta_tags

最後值得一提的是, 我們可以在 model 裡面直接更新 @meta_tags 是因為在下面這段 code 裡:

meta_tags_collection.rb
1
2
3
4
def update(object = {})
meta_tags = object.respond_to?(:to_meta_tags) ? object.to_meta_tags : object
@meta_tags.deep_merge! normalize_open_graph(meta_tags)
end

利用respond_to?(:to_meta_tags)這邊的判斷, 而 respond_to? 可以判斷這個 instance value 有沒有這個 method 可以呼叫
簡單來說如果傳入的 object 可以執行 object.to_meta_tags 就會回傳 true 了

[Rails]Asset sync after wepacker compile

發表於 2019-12-29 分類於 Rails , Gem

當設定好Asset sync且自動把檔案sync到S3, 在官方的敘述是

If AssetSync.config.run_on_precompile is true (default), then assets will be uploaded to S3 automatically after the assets:precompile rake task is invoked
意思就是沒有設定run_on_precompile是false, 就會跑sync rake上傳檔案到S3

再來這個rake是包在Gem裡面的, 且預設為在asset:precompile之後執行, 這樣會造成webpacker還沒compile好檔案就先執行了, 會造成沒有把webpacker的東西sync到S3

所以我們要客製化rake流程, 就直接在lib/tasks底下新增一個asset_sync.rake的檔案

asset_sync.rake
1
2
3
4
5
if defined?(AssetSync)
Rake::Task['webpacker:compile'].enhance do
Rake::Task["assets:sync"].invoke
end
end

最後AssetSync預設只sync asset資料夾底下的東西, webpacker compile出來的東西預設在publick/packs, 所以要更新設定

config/initializers/asset_sync.rb
1
2
3
4
5
6
7
8
config.add_local_file_paths do
# Support webpacker assets
public_root = Rails.root.join('public')
Dir.chdir(public_root) do
packs_dir = Webpacker.config.public_output_path.relative_path_from(public_root)
Dir[File.join(packs_dir, '/**/**')]
end
end

[Rails]Rails 5.1+ import react by webpacker

發表於 2019-12-29 更新於 2020-04-22 分類於 Rails , Webpacker , React

Rails 5.1+ 新專案已經可以在新建Rails專案的時候順便安裝webpakcer
$ rails new myapp --webpack
但如果是舊有專案的話就得自己安裝,

參考環境

rails 5.2.3
ruby 2.4.4
node v12.14.0
yarn 1.10.1
假設已經裝好

install webpack

參考官方網站的步驟

Gemfile
1
gem 'webpacker', '~> 4.x'

$ bundle install
$ bundle exec rails webpacker:install

其中 babel.config.js 顧名思義就是設定babel的東西, 先用預設的即可
再來 bin/webpack bin/webpack-dev-server config/webpack/ config/webpacker.yml 都是webpack的設定檔, 先注意 config/webpacker.yml這隻檔案最上面的四個參數

webpakcer.yml
1
2
3
4
source_path: app/javascript
source_entry_path: packs
public_root_path: public
public_output_path: packs
  • source_path:

    webpakcer在rails server開啟的時候也會開啟, 預設webpakcer會去偵測source_path底下的所有檔案有沒有更新過, 如果有的話只要一刷新頁面server log就會有Compiling…的訊息, 就是webpakcer在作動了

  • source_entry_path:

    會去packs這個資料夾底下找尋manifest.json這個檔案, 然後去讀取指定的js or css file, 預設自帶版本控制

  • public_root_path:

    webpacker檔案的根目錄, 預設是public, 所以剛剛的source_entry_path真正的路徑會是public/packs

  • public_output_path:

    webpacker壓縮檔案完要放到哪, 真正的路徑會是public/packs, 可以在public/packs/js底下找到壓縮出來的js檔案

引入webpacker file

webpacker檔案引入方法跟rails原本的javascript_include_tag與stylesheet_link_tag一樣, 不同的是js引入要放在body最底下, helper是javascript_pack_tag stylesheet_pack_tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
<title>RailsWebpackerReact</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application' %>
</head>

<body>
<%= yield %>
<%= javascript_pack_tag 'application' %>
</body>
</html>

在開發者模式應該就會看到預設的console.log

最後官方網站補充說在一開始的js印入

1
2
import "core-js/stable";
import "regenerator-runtime/runtime";

core是加強js, regenerator是可以讓開發者不論寫ES6 or ES5最後都會輸出一致, 直接收尋名稱都可以找到該官方Github

import react

webpacker已經包裝的很好, 只要一鍵指令
$ bundle exec rails webpacker:install:react
會發現babel什麼的都已經幫我們更新好, 甚至還給了一個最基本的檔案hello_react.jsx, 會在畫面最下面印出一個Hello React!

最後我們只要在webpacker(app/javasript/packs)資料夾底下的application.js加入一行

1
import "./hello_react";

重整頁面後就可以在Rails專案開發React了

參考文章:
Webpacker官方網站

1234…18

Kurt Hsu

Progress One Percent Every Day
171 文章
55 分類
163 標籤
RSS
© 2020 Kurt Hsu
由 Hexo 強力驅動 v3.8.0
|
主題 – NexT.Muse v7.3.0