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