2018年4月26日 星期四

同時定義 class method 和 instance method 的潮潮寫法

You can delegate method to your `class` since your class is also a object in Ruby, fanny~


```ruby
class Foo
  def self.bar
    return "Hello"
  end
  delegate :bar, to: :class
end
```

```ruby
Foo.bar
# => "Hello"

Foo.new.bar
# => "Hello"
```


Awesome.

為子 class 寫 class method 在 class 裡調用


老生常談的小技巧了,剛好看到 kickstarer 的一段 code,貼過來回溫一下


```ruby
    # Define attributes to be serialize in the `data` column.
    # It generates setters and getters for those.
    #
    # Example:
    #
    # class MyEvent < Events::BaseEvent
    #   belongs_to :post
    #
    #   data_attributes :title, :description
    # end
    #
    # MyEvent.create!(post: post, title: "Hello", description: "", metadata: {})
    def self.data_attributes(*attrs)
      @data_attributes ||= []

      attrs.map(&:to_s).each do |attr|
        @data_attributes << attr unless @data_attributes.include?(attr)

        define_method attr do
          self.data ||= {}
          self.data[attr]
        end

        define_method "#{attr}=" do |arg|
          self.data ||= {}
          self.data[attr] = arg
        end
      end

      @data_attributes
    end
```

https://gist.github.com/pcreux/d094affd957a336af4f59b85f6ec0e6d


Rails 不常用但可以變得很實用的 callback



## after_initialize && after_find

```ruby

class User < ApplicationRecord
  after_initialize do |user|
    puts "You have initialized an object!"
  end

  after_find do |user|
    puts "You have found an object!"
  end
end

>> User.new
You have initialized an object!
=> #<User id: nil>

>> User.first
You have found an object!
You have initialized an object!
=> #<User id: 1>
```

主要是 after_initialize 的部分,用來設定初始值挺方便的

http://guides.rubyonrails.org/active_record_callbacks.html#after-initialize-and-after-find


little funny learn from ruby private

I usually make my private methods like this:

```
class Foo
  private

  def private_method
  end
end
```

However, there is inline-style private method like this:

```
class Foo
  private def private_method
  end

  def puiblic_method_yo!
  end
end
```

Additionally, attr_reader is also available for inline-style private method:

```
class Foo
  private attr_reader :bar
end
```

So fancy, isn't it? lol

kickstarter 的 event sourcing approach

也解釋了一些 event sourcing 相關的東西,還不錯的深入淺出好文,而且還有 gif 圖加分XD

這張就很棒的解釋了他們所謂的 Aggregation (綠色, state), Reactor (黃色), Calculator (藍色箭頭), Event (藍色方塊)



https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224

How rails store your migration history


有時候手動操作 database 刪掉了 table,於是乎 rails migration 跟 database 就對不起來了,要怎麼樣騙 rails 說上一個 create table 的 migration file 沒跑過呢?how does rails track this?

Ans: Rails 存了一個 schema_migrations 的 table 在database 裡面,只要把相對應的 record 刪掉就行了,easy peasy


https://stackoverflow.com/questions/12057408/how-does-rails-keep-track-of-which-migrations-have-run-for-a-database



2018年4月25日 星期三

ActionCable and AWS ELB 的坑



ActionCable old fix

在 puma 前面裝一層 nginx 已經解決 ActionCable 透過 ELB failed 的問題,主要是用 nginx 手動加上 `Connection "Upgrade"` 的 header

```
  location @puma {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Ssl on;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://puma;
  }

  location /cable {
    proxy_redirect     off;
    proxy_set_header   Host $host;
    proxy_set_header   X-Forwarded-Host $host;
    proxy_set_header   X-Forwarded-Server $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
    proxy_buffering    on;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection "Upgrade";
    proxy_set_header   X-Forwarded-Proto https;
    proxy_pass         http://puma;
    gzip off;
  }
```

https://blog.jverkamp.com/2015/07/20/configuring-websockets-behind-an-aws-elb/
https://in1004kyu.github.io/2017/06/14/rails-cable-aws.html
https://unboxed.co/blog/actioncable-on-aws/ (這一個是用 ALB 不過一樣可以參考看看)
https://keithpblog.org/post/rails-5-tutorial-chat-app-can-we-deploy-it/
https://github.com/rubytaiwan/AMA/issues/135


最後決定拿掉,原因:

Quote by CTO

大概就是 aws 的传统 load balancer elb 基本也只支持 http 的网页请求
但是 actioncable wss 是一种 tcp 协议,load balancer 也要支持 tcp 连线方式才行
改了 elb 设定支持 tcp 连线方式,又会让原本 http 的行为有点问题
就是 deploy 时 puma restart load balancer 不会先 queue 住用户连线就会喷 502
然后 aws 比较好的 alb 功能比较强的 load balancer 好像无法调整支持 tcp 连线
但是 alb 可以支持 load balancer 去规划防火墙连线规则挡 ddos
所以前几天我找 aws 技术支持关于挡 ddos 他也是建议我们快点改架构可以用 alb



2018年4月23日 星期一

slug 是啥意思?

https://stackoverflow.com/questions/4230846/what-is-the-etymology-of-slug

簡單說就是代稱的意思



2018年4月22日 星期日

第一年回顧

2017/4/4 ~ 2018/4/3

共發布 138 篇文章
草稿 27 篇


只達標 37.8%......

Rspec aggregate_failures syntax


意思就是在 aggregate_failures block 底下任一個的 expectation 有 error 時會針對每個 expectation 顯示 error 的 message

example

```ruby
    it "returns false if any of balance, locked, saving is negative" do
      negative_balance_account = build(:account, balance: -1)
      negative_locked_account = build(:account, locked: -1)
      negative_saving_account = build(:account, saving: -1)

      aggregate_failures "testing all senarios" do
        expect(negative_balance_account.has_negative_asset?).to be true
        expect(negative_locked_account.has_negative_asset?).to be true
        expect(negative_saving_account.has_negative_asset?).to be true
      end
    end
```

假設這個 test failed 了,那 console 的訊息會是像這樣:

```
  1) Account#has_negative_asset? returns false if any of balance, locked, saving is negative
     Got 2 failures from failure aggregation block "testing all senarios".
     # ./spec/models/account_spec.rb:15:in `block (3 levels) in <top (required)>'

     1.1) Failure/Error: expect(negative_locked_account.has_negative_asset?).to be true

            expected true
                 got false
          # ./spec/models/account_spec.rb:17:in `block (4 levels) in <top (required)>'

     1.2) Failure/Error: expect(negative_saving_account.has_negative_asset?).to be true

            expected true
                 got false
          # ./spec/models/account_spec.rb:18:in `block (4 levels) in <top (required)>'
```

會顯示哪幾個 test failed 了

當有很多 test case 很相近其實可以寫在一個senario 裡時用 aggregate_failures 會讓 test 變得更簡潔,但要小心不要亂用,該分開的 test 還是用 it 分開比較好。


https://relishapp.com/rspec/rspec-core/docs/expectation-framework-integration/aggregating-failures

2018年4月18日 星期三

Git don't blame, we are asking for context bot blaming people

Nice little trick to avoid blaming mindset

```
➜  ~ cat .gitconfig
[alias]
context = blame
```

git bisect

debugging 的時候可以使用,原理就是

1. 先找到確定沒有問題的 commit
2. git bisect 會用二分法切換到中間的 commit,然後就可以 test 是否存在相同的 bug,持續使用二分法直到找到有問題的 commit

2018年4月3日 星期二

PaperTrail whodunnit find object (ex: find User object)


PaperTrail::Verison object 的 methods 可以去 PaperTrail::VersionConcern 裡面查

查了一下沒有可以找到 whodunnit object 的方法,只好自己加一個:

```rb
#initializers/paper_trail.rb
PaperTrail::Version.class_eval do
  def whodunnit_object
     User.find_by(id: whodunnit)
  end
end
```


http://blog.nrowegt.com/papertrail-polymorphic-whodunnit/

2018年4月2日 星期一

釐清 DatabaseCleaner strategy 跟 clean_with


```rb
config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end
```

### 釐清 strategy 跟 clean_with

上面這段 code 指的是「當 test 開始時,用 truncation,然後之後都用 transaction」

為什麼要這樣呢?因為 truncation 比較慢 transaction 比較快

所以這就引發另一個問題,什麼是 truncation 什麼是 transaction?還有什麼其他的 strategy?

## Strategy

https://github.com/DatabaseCleaner/database_cleaner#what-strategy-is-fastest

可以選擇:

* transaction (優先使用)
* truncation
* deletion


這些 startegy 之間的差異:

https://stackoverflow.com/questions/10904996/difference-between-truncation-transaction-and-deletion-database-strategies



* transaction (優先使用): 代表把執行的 SQL 包在 `BEGIN TRANSACTION` 裡然後用 `ROLLBACK` 回逤成初始狀態
* truncation: 使用SQL `TRUNCATE TABLE` 直接把 table 清空
* deletion: 使用 SQL `DELETE FROM` 來刪除資料,速度慢

關於 delete 與 truncate:

https://dba.stackexchange.com/questions/30325/delete-vs-truncate

https://stackoverflow.com/questions/139630/whats-the-difference-between-truncate-and-delete-in-sql/12900557#12900557

有時間再寫關於 delete 與 truncate 之間的差別。