2018年5月30日 星期三

ruby proc 可以傳default 值since ruby 1.9

所以

scope :latest, ->(size=5) { recent.limit(size) }

這樣是可以的

好廢的一篇文

呵呵

2018年5月29日 星期二

ssl-cookie-without-secure-flag-set


 HTTPOnly flag


https://portswigger.net/kb/issues/00500200_ssl-cookie-without-secure-flag-set

https://stackoverflow.com/questions/3773605/how-can-i-make-cookies-secure-https-only-by-default-in-rails



2018年5月25日 星期五

潮汐對浪大小的影響

還沒完全看懂,有空的時候可看一下

https://magicseaweed.com/news/are-waves-bigger-on-a-pushing-tide/10943/


各種 performance 相關的技巧 - rails related and general



https://www.monterail.com/blog/actionable-tips-to-improve-web-performance-with-rails


2018年5月24日 星期四

github issue 和 pr 帶不同 params 就有不同功能

潮潮的,可以指定 title, labels, template 和 assignees 之類的


https://help.github.com/articles/about-automation-for-issues-and-pull-requests-with-query-parameters/


Vue computed v.s. methods



computed 會 cached methods 不會
computed 回傳一個值,methods 不一定

https://cn.vuejs.org/v2/guide/computed.html

Rails 內建的加解密 MessageEncryptor


http://edgeapi.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html



2018年5月23日 星期三

How to Create Postgres Indexes Concurrently in ActiveRecord Migrations


之前的文章提到要避免 deployment downtime 其中一個方式是 create index concurrently
http://everyday1percent.blogspot.com/2018/05/rails-deployment-downtime.html

原來 rails 有內建的 API 做到這件事

```ruby
class AddIndexToBuildMetricIdOnBuilds < ActiveRecord::Migration
  disable_ddl_transaction!

  def change
    add_index :builds, :build_metric_id, :algorithm => :concurrently
  end
end
```

好潮。




https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in
https://semaphoreci.com/blog/2017/06/21/faster-rails-indexing-large-database-tables.html


Devise 每次都會同時下 ORDER BY 和 LIMIT 的 sql 去找 users


同事提出的建議:

```
SELECT "users".* FROM "users" WHERE "users"."id" = $? ORDER BY "users"."id" ASC LIMIT $?

id 是 primary key, 理論上不會有重複的資料.

SELECT id, COUNT(id) AS cid FROM users GROUP BY id ORDER BY cid DESC;
id | cid
--------+-----
1 | 1
58291 | 1
(以下省略)

也確認目前的資料中, 的確沒有出現重複的 id.

這個 query 的 ORDER BY 跟 LIMIT 確認不會被 pgsql 的 query plan / optimizer 省略掉, 還是會產生 procedure call 消耗些微的 cpu / memory, 所以還是建議修改這個 query.
```

查了一下主要罪魁禍首是


Devise::Models::Authenticatable.serialize_from_session

這個 method 是每次已登入的 user 訪問頁面時就會 trigger 的,裡面用了 User.to_adapter.get([id]) 的方式來拿資料

```
 :014 > User.to_adapter.get([1])
  User Load (2.8ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
```

另外比較不重要但是也會影響的地方就是登入時的驗證,確切來說就是 Devise::Models::DatabaseAuthenticatable.find_for_database_authentication ,裡面用了 to_adapter.find_first

```
:005 > User.to_adapter.find_first(id: 1)
  User Load (1.7ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 ORDER BY "users"."id" ASC LIMIT $2  [["id", 1], ["LIMIT", 1]]
```

可以參考的解法:

* customize serializer: https://github.com/plataformatec/devise/wiki/How-to:-Write-your-own-serializer
* overwrite, ex:

```
class User < ApplicationRecord
  def self.serialize_from_session(key, salt)
    record = find_by(id: key)
    record if record && record.authenticatable_salt == salt
  end
end
```

* 想辦法在不必要驗證的地方 skip 上述 find user 的 middelware



bcrypt, devise, and rails secret_key_base

devise 用 secret_key_base 當作產生 token 的依據
devise 用 bcrypt hashify password 然後儲存
rails 的 has_secure_password 也是用 bcrypt 實現


bcrypt() is a hashing algorithm designed by Niels Provos and David Mazières of the OpenBSD Project.


題外話,如果要改 secret_key_base ,那 devise 裡的token, 包含confirmation, reset_password 都會 invalid 需要重新生成。

https://github.com/codahale/bcrypt-ruby
https://coderwall.com/p/sjegjq/use-bcrypt-for-passwords

2018年5月22日 星期二

explain https again


CA 憑證方發憑證,所謂的憑證就是包含

1. 公鑰
2. 用 CA 方的私鑰加密過的 Hash


## 具體步驟是:


  1. client端發起Https request
  2. Server端返回CA發的憑證
  3. Client端收到憑證,並驗證憑證是否可信,如果可信(或是使用者選擇相信)則隨機產生一段對稱式加密用的key(稱之為Key S好了),並且用憑證內的公鑰加密Key S,送給Server
  4. Server收到加密過的Key S,用自己的私鑰解密,並且回給Client一個ready的訊息
  5. Client開始和Server用Key S做對稱式加密通訊


轉載 from

https://medium.com/@kingapol/3%E5%88%86%E9%90%98%E5%85%A7%E4%BA%86%E8%A7%A3https-595b7d29c16

2018年5月21日 星期一

deploy vue to github page

兩個不錯的方式:

1. 用 push-dir + webpack 做
https://medium.com/@codetheorist/vue-up-your-github-pages-the-right-way-955486220418


2. 用直接寫好的 plugin
https://github.com/KieferSivitz/vue-gh-pages


目前是用後者

vue-cli v3.0.0.beta10 + vue-gh-pages v1.0.1

是很方便但是問題是

1. 必須用 /docs folder 當 gh-pages 的 root
2. 如果沒有綁 domain 的話會讀不到 js, css files,因為會連到 username.github.io/js/xxx.js 而不是 username.github.io/project_name/js/xxx.js,但後者才是正確的連結
3. docs/ folder 會一直搞爛 git 很煩

更新:找到用 vue-gh-pages 但不使用 master docs/ folder 的方法了,把 deploy script 改成下面這樣就行了:

"deploy": "node ./node_modules/vue-gh-pages/index.js -b gh-pages -o ./"

目前是就乾脆買個 domain,但比較 prefer push-dir + webpack 就是了,只是比較麻煩懶得嘗試。

墮天使路西法 Lucifer

剛好在研究七原罪的時候看到傲慢的代表惡魔是 Lucifer,於是想知道是怎樣傲慢。

教會的解釋是 Lucifer 忘了自己天使的身份,把自己看的跟神一樣等級(所以才叛變?)

正常來說 Lucifer 的履歷如下:

* 率兵叛變的天使首腦
* 化身伊甸園的蛇
* Some people says Lucifer == 撒旦



Wiki:

文學裡的路西法

在但丁的《神曲》與約翰·彌爾頓的《失樂園》之中,都是說路西法因為拒絕臣服於聖子基督,率天眾三分之一的天使於天界北境起兵反叛。經過三天的天界劇戰,路西法的叛軍被基督擊潰,在渾沌中墜落了九個晨昏才落到地獄。此後神創造了新天地和人類,路西法為了復仇兼奪取新天地,乃化為蛇潛入伊甸園,引誘夏娃食用了禁忌的知善惡樹之果實,再利用她引誘亞當也犯下了這違抗神靈的罪。於是路西法如願使神的新受造物一同墮落,而且為諸魔神開啟了通往這新世界的大門,自此罪、病、死終於遍布地面。


https://zh.wikipedia.org/wiki/%E8%B7%AF%E8%A5%BF%E6%B3%95

一篇講 OO cohesion 和 coupling 簡單易懂的文章

一直想找個好方式跟同事解釋怎麼 decoupling 和 design better code,這篇文章涵蓋了最重要的兩個概念而且用很簡單易懂的 code 解釋了,很不錯。

cohesion: class 裡的 methods 是不是 share 一樣的 context?

coupling: dependency

http://www.rubyguides.com/2018/05/ruby-cohesion-and-coupling/

2018年5月20日 星期日

使用 Vagrant 練習 chef-solo deployment

每次都自己調 server 太煩了,用 chef-solo 調一次以後一直套用比較方便,可以用 Vagrant 練習會快一點

先安裝 VirtualBox 和 Vagrant

https://www.virtualbox.org/wiki/Downloads
https://www.vagrantup.com/downloads.html

Ubuntu 16.04 的 Vagrant box (Vagrant box 就是包好的環境的意思)

https://app.vagrantup.com/ubuntu/boxes/xenial64

```
➜  Codes mkdir vagrant-test
➜  Codes cd vagrant-test
➜  vagrant-test vagrant init
```

在 Vagrantfile 裡設定

```
Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
end
```

然後開啟 Vagrant

```
vagrant up
```

然後就可以 ssh 進去玩了

vagrant ssh

//關閉VM
$ vagrant halt

//暫停VM
$ vagrant suspend

//恢復暫停的VM
$ vagrant resume

//重啓VM
$ vagrant reload


## Chef solo

首先要 init 一個 chef-solo 的 project


檢查 vagrant 的 ssh-config

```
➜  vagrant-test git:(master) ✗ vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/wayne/Codes/vagrant-test/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL
```

PasswordAuthentication 被設為 no 了所以要嘛去把 local 的 ssh key 加到 vagrant 裡要嘛就把 password 設成 yes,預設 password 是 vagrant


我個人是很懶所以就這樣:

```
ssh-copy-id -i ~/.ssh/id_rsa.pub -p 2222 vagrant@localhost
```
但其實還是有一些其他做法可以參考:

https://github.com/puphpet/puphpet/issues/1253



https://blog.vvtitan.com/2015/10/%E4%BD%BF%E7%94%A8vagrant%E7%B7%B4%E7%BF%92%E4%BD%88%E7%BD%B2%E4%BC%BA%E6%9C%8D%E5%99%A8/
https://medium.com/@brianmayrose/using-vagrant-on-ubuntu-16-04-to-create-a-virtual-machine-c767f6e2d876
https://gist.github.com/nicholasklick/af98d5814d5b8ba6d844


2018年5月19日 星期六

新的做 soft delete 的 gem - discard


https://github.com/jhawthorn/discard


沒有 default 加上 default scope 我覺得是很棒的決定,這個 gem 留給開發者更多的彈性實作 soft delete,我覺得在大多數的 case 下都是很夠用了,相較之下一樣是 soft delete 的 gem paranoia 就太多 magic 了



2018年5月18日 星期五

檢查 gem verison 的方法


在devise 裡看到的

```rb


  def self.rails51? # :nodoc:
    Rails.gem_version >= Gem::Version.new("5.1.x")
  end

  def self.activerecord51? # :nodoc:
    defined?(ActiveRecord) && ActiveRecord.gem_version >= Gem::Version.new("5.1.x")
  end

```


replay attacks


最基本的防禦方式就是每個 request 都帶一個 uniq id 來避免,向 bitfinex API 的 nonce 參數之類的



2018年5月17日 星期四

Profiling Rails Boot Time


Infos:

https://mildlyinternet.com/code/profiling-rails-boot-time.html
https://tenderlovemaking.com/2011/12/05/profiling-rails-startup-with-dtrace.html
http://guides.rubyonrails.org/initialization.html#config-environment-rb
http://brewhouse.io/blog/2015/04/27/fixing-a-slow-rails-development-server.html


Gems:

https://github.com/MiniProfiler/rack-mini-profiler
https://github.com/SamSaffron/flamegraph
https://github.com/nevir/Bumbler
https://github.com/ruby-prof/ruby-prof
https://github.com/tmm1/perftools.rb


2018年5月16日 星期三

Rails deployment 如何避免 downtime?



有哪些造成 downtime 的可能性?


1. Code 有問題
2. Migration 跑完後 Web Server 還沒 restart
3. Deploy 完後需要重啟 Server
4. Request 沒有被 queue 起來



可能的解決方式:

## 1. Code 有問題

1. 勤勞寫 Test
2. Manually test
3. Code review
4. Staging -> Production

## 2. Migration 跑完後 Web Server 還沒 restart

有篇文章寫得很不錯, cover 了一些之前沒想到的技巧:

https://blog.codeship.com/rails-migrations-zero-downtime/

1. read only method
2. 先讓 AR ignore columns 後再刪除
```
class User
  def self.columns
    super.reject { |c| c.name == "notes" }
  end
end
```
3. renaming column 時可以用 `super || attributes["new_name"]`
```
def first_name
  super || attributes["fname"]
end
```
4. 加 index 時有時會卡住 migration 造成問題,可以考慮 create it concurrently



## 3. Deploy 完後需要重啟 Server

1. 可以先把新的 instances run 起來,然後把 request 切換到新的 instances 後再 shut down 舊的
2. 靠 Load balancer 把 request queue 起來等重啟完再執行


## 4. Request 沒有被 queue 起來

之前就遇過因為 ActionCable and AWS ELB 的各種坑

https://everyday1percent.blogspot.com/2018/04/actioncable-and-aws-elb.html

所以也是有這個可能性,解決方式是要讓 load balancer 確保有 queue request 等 server 恢復時才執行


剩下有想到再補充

2018年5月10日 星期四

cc 系列 license,看到一個還不錯的,以後文章可以用


https://creativecommons.org/licenses/by-nc/2.5/

可以允許分享也可以改動

分享要 credit 原作者,如果有改動也要指出改動哪裡。

Query jsonb data type in postgres with Rails

https://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails

2018年5月9日 星期三

rspec test protocal (interface) - part 1 - define what's interface and find it


通常我們會希望繼承於 Base class 底下的 class 會有相同的 interface,for example, 每個 service 都要有一個 #call 的 instance method,應該要有個很簡單的方法來對這些 services 測試是否有 implement 這個 protocal,理想情況如下:


```rb
class BaseService
  def call
    raise "NotImplemented"
  end
end
class FooService < BaseService
  def call
    puts "called"
  end
end
class BarService < BaseService
end
module Callable
  def call
    puts "called from module"
  end
end
class IncludeService
  include Callable
end
class PrivateService
  private

  def call
    puts "private call"
  end
end


RSpec.describe FooService do

  describe "interfaces" do
     specify do
        expect(FooService).to has_interface :call
        expect(FooService).to has_implemented_interface :call
     end
  end

end
```

it_has_interface => 有 respond_to 這個 method 就行 (個人覺得沒什麼用...)
it_has_implemented_interface => 有自己 implement (排除母 class) 或是 include method 進來


it_has_implemented_interface 希望達到的效果是:

FooService 和 IncludeService 會 passed
BarService 和 PrivateService 會 failed


來看看要怎麼樣做 :D


已知使用 respond_to? 是不行的,因為母 class 定義了 call,所以

```ruby
2.4.4 :013 > f = FooService.new
 => #<FooService:0x00007fed06152098>
2.4.4 :014 > b = BarService.new
 => #<BarService:0x00007fed06148700>
2.4.4 :015 > f.respond_to?(:call)
 => true
2.4.4 :016 > b.respond_to?(:call)
 => true
```

用 method_defined? 當然也不行,因為都是繼承於 BaseService 所以都有 defined,但來試來試一下

```ruby
2.4.4 :017 > FooService.method_defined?(:call)
 => true
2.4.4 :018 > BarService.method_defined?(:call)
 => true
```

我們要找的是一個方法檢測子 class 是否有定義這個 method,不管是不是用 mixin 的方式加進去的,有幾個 methods 可以用

* public_methods
* instance_methods
* public_instance_methods

instance_methods default 還會包含 protected 的 methods,不如我們先來試試看 public_instance_methods

```ruby
2.4.4 :030 > FooService.public_instance_methods.include?(:call)
 => true
2.4.4 :031 > BarService.public_instance_methods.include?(:call)
 => true
2.4.4 :032 > IncludeService.public_instance_methods.include?(:call)
 => true
2.4.4 :033 > PrivateService.public_instance_methods.include?(:call)
 => false
```

看起來還是不行,讓我們換個方法,用 public_methods(false)


```ruby
2.4.4 :068 > FooService.new.public_methods(false).include?(:call)
 => true
2.4.4 :069 > BarService.new.public_methods(false).include?(:call)
 => false
2.4.4 :070 > IncludeService.new.public_methods(false).include?(:call)
 => false
2.4.4 :071 > PrivateService.new.public_methods(false).include?(:call)
 => false
```

結果 public_methods(false) 沒辦法讓 include 進來的 method 包含在裡面,為什麼呢?可以看一下 IncludeService 的 ancessor

```
2.4.4 :072 > IncludeService.ancestors
 => [IncludeService, Callable, Object, Kernel, BasicObject]
```

可以想像成 include 其實就是在 parent class 以及 child class 中間插隊,所以也是算在 ancestors 裡面。


所以看起來沒有任何 built-in 的 method 可以直接做到這個功能,那不如我們就自幹一個看看吧?XD

由於要 support module 裡的 methods 所以想到最原始的方法就是去找 ancestors 裡在 superclass 之下的每個 instance_methods, code example:

```ruby
class Utils
  class << self
    def get_public_instance_methods_without_parent_class(klass)
      countable_ancestors = klass.ancestors[0...klass.ancestors.index(klass.superclass)]
      countable_ancestors.flat_map do |ancestor_class|
        ancestor_class.public_instance_methods(false)
      end.uniq
    end
  end
end
```

這個版本目前只能對instance method 做檢測,不過對我們來說已經足夠了,以後再想辦法擴充,接下來就是來研究自定義 rspec DSL 的部分。


2018年5月6日 星期日

Deploy rails to Ubuntu 16.04 with puma and nginx and capistrano



Install Nginx at Ubuntu

https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-16-04


設定Nginx

/etc/nginx/site-availables/myconfig

```
upstream puma {
  server unix:///home/apps/arbitrage/shared/tmp/sockets/puma.sock;
}

server {
 # listen 80 default_server;
 # listen [::]:80 default_server;
  server_name www.yourdomain.com;
  root /home/apps/arbitrage/current/public;
  access_log /home/apps/arbitrage/shared/log/nginx.access.log;
  error_log /home/apps/arbitrage/shared/log/nginx.error.log info;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @puma;
  location @puma {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    proxy_pass http://puma;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;
}
```


cd /etc/nginx/site-enabled

sudo ln -nfs /home/username/apps/appname/current/config/nginx.conf /etc/nginx/sites-enabled/appname

sudo service nginx restart



也要記得裝 redis http://everyday1percent.blogspot.com/2017/12/deploy-rails-production-redis.html






https://coderwall.com/p/ttrhow/deploying-rails-app-using-nginx-puma-and-capistrano-3

一鍵安裝 shadowsocksR



https://github.com/teddysun/shadowsocks_install


Mac OS 用 Geth 跑 ETH ethereum node 全節點


https://github.com/ethereum/go-ethereum/wiki/Installation-Instructions-for-Mac


brew tap ethereum/ethereum
brew install ethereum

然後跑

geth --syncmode "fast" --cache=1024

就會開始 sync 主鏈的全節點,節點會存在

database=/Users/wayne/Library/Ethereum/geth/chaindata

路徑裡,在geth 啟動時就可以看到 database 的路徑

fast 參數是 syncmode,詳細介紹: https://everyday1percent.blogspot.com/2018/05/eth-syncmode.html



如果要 sync testnet 可以這樣:

Sync testnet (Ropsten)

```
geth --testnet --syncmode "fast" --datadir /usr/local/var/ethereum-testnet-data console 2>> /usr/local/var/log/geth.testnet.log
```

開啟 API
```
geth -- testnet  -- syncmode "fast"  -- rpc  -- rpcapi db,eth,net,web3,personal,admin  -- cache=1024  -- rpcport 8545
```

開啟 console
```
geth --testnet --syncmode "fast" console 2>> /usr/local/var/log/geth.testnet.log
```


```
echo "Geth at work!"
screen -dmS geth geth --testnet --syncmode "fast" --rpc --rpcapi db,eth,net,web3,personal --cache=1024  --rpcport 8545 --rpcaddr 0.0.0.0 --rpccorsdomain "*"
```
然後
```
geth attach http://localhost:8545
```
就可以進 console


https://ethereum.stackexchange.com/questions/392/how-can-i-get-a-geth-node-to-download-the-blockchain-quickly/4210#4210

https://medium.com/@jacksonngtech/syncing-geth-to-the-ethereum-blockchain-9571666f3cfc

eth syncmode


還不是很確定,先翻譯貼過來

Full - 拿到所有資料
Fast - 差不多的意思,但不執行transaction
Light - 只拿到現在的狀態,但不能驗證


https://ethereum.stackexchange.com/questions/11297/what-is-geths-light-sync-and-why-is-it-so-fast



更好的解釋:

主要是fast 跟 full 的差別

full 可以驗證每個 state,但 fast 因為只有 process 最近的 transactions,所以不能驗證舊的 state

https://ethereum.stackexchange.com/questions/1161/what-is-geths-fast-sync-and-why-is-it-faster

https://github.com/ethereum/go-ethereum/pull/1889


2018年5月1日 星期二

PG::ConnectionBad: FATAL: Peer authentication failed for user XXX



 sudo vim /etc/postgresql/9.5/main/pg_hba.conf


加一行:

```
local   all             your_user_name                               trust
```

編輯
```
# DO NOT DISABLE!
# If you change this first entry you will need to make sure that the
# database superuser can access the database using some other method.
# Noninteractive access to all databases is required during automatic
# maintenance (custom daily cronjobs, replication, and similar tasks).
#
# Database administrative login by Unix domain socket
local   all             postgres                                peer

# TYPE  DATABASE        USER            ADDRESS                 METHOD

local   all             your_user_name                               trust

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local   replication     postgres                                peer
#host    replication     postgres        127.0.0.1/32            md5
```

然後重啟 server

sudo service postgresql restart



https://www.postgresql.org/docs/9.3/static/auth-methods.html

rails 5.2 credentials capistrano deploy 的各種坑



Deploy 一直遇到 Error:

```
ArgumentError: Missing `secret_key_base` for 'production' environment, set this string with `rails credentials:edit`
```

試了好多方式,理論上設個環境變數就行,但我一直失敗,中途還嘗試把credentials disable 也是沒用,後來是直接上傳 master.key 解決


用這行指令上傳
```
scp config/master.key apps@DOMAIN:PROJECT_NAME/shared/config
```

然後 capistrano link 過去
```
set :linked_files, fetch(:linked_files, []).push("config/database.yml", "config/master.key"
```


http://guides.rubyonrails.org/security.html#custom-credentials
https://keithpblog.org/post/encrypted-secrets/
https://github.com/rails/rails/issues/32413