about 3 years ago

設定Android SDK環境變數:

Android SDK路徑通常在:
C:\Users\<使用者帳號資料夾>\AppData\Local\Android\sdk\
(找不到[AppData]資料夾,請開啟顯示隱藏資料夾與檔案的設定!)

新增變數名稱ANDROID_HOME值於C:\Users\<使用者帳號資料夾>\AppData\Local\Android\sdk
這樣就有了ANDROID_HOME這個變數,值就是後面那串
修改變數Path,在值的最前面加上%ANDROID_HOME%\tools\;%ANDROID_HOME%\platform-tools\;
%xxx%是呼叫環境變數的方式,我們會用到該路徑下的tools和platform-tools裡面的執行檔,所以在此加上

測試:命令提示字元下打*adb version,*adb應該回應版本號!有就表示成功。

 
over 3 years ago

Websocket server的實作極多,本篇目標是想以 faye-websocket 這個 gem 做一個純websocket service。玩熟之後可能還會玩玩看 em-websocket 和 plezi。那麽,開始吧!

從Wikipedia的定義

WebSocket是HTML5開始提供的一種在單個 TCP 連線上進行全雙工通訊的協定。WebSocket通訊協定於2011年被IETF定為標準RFC 6455,WebSocketAPI被W3C定為標準。

在WebSocket API中,瀏覽器和伺服器只需要做一個交握的動作,然後,瀏覽器和伺服器之間就形成了一條快速通道。兩者之間就直接可以資料互相傳送。

不過有另外兩個資料寫得比較好
知乎原理介紹
Railsfun JC 大解釋

參考資料

Ruby & Websocket & websocket-js 支援與詳解 by JC

菜鳥教程
websocket之入门(一)
Demo code - EM-websocket
使用Ruby打造一個簡單的Websocket server
Demo code by Heroku

幕凡投影片

Ruby Gem
https://faye.jcoglan.com/ruby.html
https://github.com/faye/faye-websocket-ruby
https://github.com/igrigorik/em-websocket
http://www.plezi.io/

阮一峰介紹Websocket
WebSocket 通訊協定簡介:比較 Polling、Long-Polling 與 Streaming 的運作原理
Ruby SSE Server 動手做
Ruby EventMachine Web Socket Redis Pub/Sub Chat Room
WebSockets From Scratch
Building a simple websockets server from scratch in Ruby
Chat Example App Using Server-Sent Events
websocket之实现简易聊天室(四)

 
over 3 years ago

先安裝Git

Client端要裝Git才能在本機端進行版本控制和程式碼上傳下載。

Windos:
下載後安裝
Git for Windows

Ubuntu:
在terminal打
sudo apt-get install git

Mac:
使用圖形化界面的Git安裝程式,可從SourceForge下載:
http://sourceforge.net/projects/git-osx-installer/

想要順便裝圖形介面的,就自行google "git GUI for window/linux/OSX"吧~

參考來源

必要的設定

Ubuntu 和 Mac 在 terminal,windows 就開 git bash(程式集會有,或者任意資料夾按右鍵也有)

git config --global user.name "your_username"
git config --global user.email "your_email"
git config --global color.ui true

生成公開金鑰

這一步驟會在Client產生公鑰和私鑰,生成的私鑰會放在自己家,不被別人知道,而公鑰可以給其他人。公私鑰成對出現,公鑰加密過的東西只可用私鑰解密還原,被私鑰加密過的東西只可用公鑰解密還原。
我們會把公鑰提交給Bitbucket(一個雲端程式碼倉庫,五人以下的私有專案免費)和Github(公開專案免費),將來我們使用ssh協定傳送資料時,就不用每次再輸入密碼。我們也可以把公鑰傳給Raspberry Pi,這樣使用ssh連線時就可以不用輸入密碼,而且傳輸資料都會加密,比較安全。

簡單的原理說明:我們機器是A,要連線的機器是B,我們要做的是在A生成公私鑰,然後把A的公鑰記錄在B(在~/.ssh/authorized_keys文件中)。

  1. A發起一個公鑰認證給B
  2. B檢查是否存在該公鑰,沒有則斷連
  3. B使用先前得到的A的公鑰對一組隨機字串加密,並回傳給A
  4. A使用自己的私鑰解密,再回傳給B(實際上會結合session id生成MD5值才發出去)
  5. B檢查回傳值是否是原先的隨機字串,是的話就完成認證(實際上會用同樣的方式生成MD5值才比較)

Windows:
先檢查是否已經有 /c/users/your_username.ssh 資料夾,如果有就不要再生成了,否則會被覆蓋。
開git bash,進入/c/users/your_username
cd /c/users/your_username
建立公私鑰
ssh-keygen -t rsa -C "your_email@emailhost"
之後就可以在
c:\users\your_username.ssh裡面找到公私鑰

Private Key - id_rsa 私鑰
Public Key - id_rsa.pub 公鑰

Ubuntu:
先檢查是否已經有 ~/.ssh 資料夾,如果有就不要再生成了,否則會被覆蓋。
ssh-keygen
如果不想輸入密碼可以enter兩次跳過,這樣就完成了。
公鑰位置預設在.ssh裡,打cat ~/.ssh/id_rsa.pub可以在terminal印出公鑰。

Mac沒研究@@,JL加油~

請上Github和Bitbucket申請帳號吧

Github
Bitbucket

下次開會就預設大家都做完上述工作了喔

雖然有時也會用圖形介面,但我最常用的還是command line,下次會講最重要的幾個指令,和git work flow,內容會選自下面這篇,不講太多,把握重要觀念,須要用到再查即可。
Git指令

 
over 3 years ago

兩個Model如果是繼承關係,可以用子類別可以繼承父類別的方法或特性,如果兩個Model沒有繼承關係,想要讓一個類別使用另一個類別的方法,則可以用delegate。

本文翻譯自此https://simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate/
翻譯是為方便自己理解,建議直接看原文~

Delegation 是一個Ruby專案中常見的例子,如果你把proxies, mixins 和 composition 當成一種delegation模式的話。

Delegation 設計模式是讓物件或實例曝露一些性質,但這些性質其實是取用與當前實例相關連的其它實例的性質而來的。

常見技巧,當呼叫一個不存在的方法時會得到method_missing,然後回傳該呼叫到正確的handler。然而,這不總是好辦法。在Ruby中還有更好方法去做delegation。

Ruby標準函式庫中包含 Delegate module,為Delegation提供支援。不幸的,我發現它比傳統的做法更複雜,因此我從未用過它。

ActiveSupport Delegate module

如果你的專案有ActiveSupport你可以有更簡便的方式做出delegation:Module#delegate擴充。你可以使用它提供的delegate 模組在你的類別或模組裡,可以delegate一個方法給一個關連的實例。

舉例來說,考慮Post belongs_to a User.

class Post < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :posts
end

你也許想要呼叫post.name,並讓它回傳user.name,該user為post的關連實例。通常來說,你須要創建一個name方法。如下所示:

class Post
  belongs_to :user

  def name
    # let's use try to bypass nil-check

    user.try(:name)
  end
end

相同的程式可以用delegate方法表示。惟須注意,delegate的方法要是public。

class Post
  belongs_to :user
  delegate :name, :to => :user, :allow_nil => true
end

delegate方法可以被使用在任何情境,不只侷限於ActiveRecord models。舉例來說,你的 queue wrapper 可以 delegate 到內部的 queue implementation 的特定方法。

class QueueManager

  attr_accessor :queue

  # Delegates some methods to the internal queue

  delegate :size, :clear, :to => :queue

  def initialize
    self.queue = []
  end

end

m = QueueManager.new
m.size
# => 0

m.clear
# => []

方法可以被 delegated 到實例變數,類別變數或常數(使用symbol)。:to 之後至少須要接一個方法。

Options

delegate 方法可以加入一些選項來客製特性。

這是我最愛的,:prefix可以設定為true使得關聯的實例會出現在方法名稱中。你也可以自行設定prefix。

class Post
  belongs_to :user

  delegate :name, :to => :user, :prefix => true
  # post.user_name


  delegate :name, :to => :user, :prefix => "author"
  # post.author_name

end

:allow_nil選項允許類別delegate該方法到一個nil實例中。在此情況下,該方法會回傳nil。預設行為是發出NoMethodError

class Post
  belongs_to :user
  delegate :name, :to => :user, :prefix => true
end

Post.new.user_name
# raise NoMethodError


class Post
  belongs_to :user
  delegate :name, :to => :user, :prefix => true, :allow_nil => true
end

Post.new.user_name
# => nil

:to不是選項,是強制須要輸入的東西。

Documentation

作者建議直接看source code

 
over 3 years ago

因為實在少一台電腦螢幕,所以要玩RPi都得用電視。想想其實大多數時候我用不到螢幕,那就用ssh登入吧!

這篇文章說明如何用個人電腦登入Raspberry Pi的文字介面。當RPi已經安裝完成,且連接上網路後,就可以開始了。步驟不多,簡單記錄下來,順便給隊員參考。

RPi連接網路

首先,確定要用來登入的電腦與RPi在同一個網域下。
進入RPi後打開terminal ctrl + alt + t,輸入ifconfig,找wlan0中的inet addr這行,會找到192.168.xxx.xxx,這是AP給這台RPi的內網IP,先記錄下來。

新增使用者

雖然Raspbian預設就有一個root帳號,但習慣上還是會開一個自己的帳號。一樣在terminal打

# 新增sharefun為新的使用者,根據提示輸入密碼和確認密碼後即完成
sudo adduser sharefun
# 取回管理員權限,如果已有管理者權限可跳過
sudo su
# 賦與sharefun管理者權限
adduser sharefun sudo
# 切換到sharefun
sudo su sharefun
# 上述指令會幫你將工作目錄一起建好,打cd即可切到工作目錄

下載putty

下載putty,一個ssh連線的工具。
不用安裝,即可打開。

登入RPi

打開putty後,在session頁面,Host Name (or IP address)處輸入剛剛記下的IP,Connection Type選擇ssh,再按Open鈕。
Terminal會打開,叫你輸入帳號密碼,把剛剛做好的的帳號密碼打進去,或者使用RPi預設的帳號密碼就可以登入了!

解決 ssh 進RPi會lag的問題

# /etc/ssh/sshd_config 最後加入
UseDNS no
# 儲存後重啟ssh
sudo service ssh reload

參考
http://raspberrypi.stackexchange.com/questions/15568/raspberry-pi-ssh-login-slow

 
over 3 years ago

第一次使用grape。看了JC大的Ruby / Sinatra / Rails : Grape & Swagger大亂鬥的日常:Rails Grape Swagger,搭配超複雜的github文檔,整理成這篇文章。

Grape是一個使用Ruby做的Restful API framework,可以單獨跑Rack或是與現有的app搭配一起使用。使用Grape創建的API,會在middleware層就打掉,不會有log,也比較快速。

首先安裝grape,在Gemfile

gem 'grape'
gem 'grape-swagger
gem 'grape-swagger-rails'

可以只裝第一個,這篇的所有內容用第一個gem可以了。記得跑bundle install

config/application.rb上加上兩行,讓rails可以讀到等一下要寫的檔案。(預設只會讀rails new產生的那些檔案而已)等一下要寫的所有檔案將會放在app/api裡面。

config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]

routes.rb加入

mount GeneralApi::ApiV1 => '/api/v1'

mount關鍵字是告訴rails app說我們有另一個app在跑,在此例中,我們的app是一個API Rack app。
它會先跑middleware,如果沒找到path才會到Rails端,所以應該把api的routes都寫到routes的上方。
而這樣的寫法,等一下就會開一個去找GeneralApi::API這個class。GeneralApi這個module,裡面有一個class叫ApiV1。
這樣表示把GeneralApi::ApiV1掛在/api/v1下。雖然grape有支援版本號,可以在同一個class內打不同版本的api,但是如果所有的版本都打在同一個class裡,裡面的hepler一改動,會影響到其他版本,所以JC建議把不同的版本寫成不同的class。像這樣:

mount GeneralApi::ApiV1 => '/api/v1'
mount GeneralApi::ApiV2 => '/api/v2'
...

這樣寫在routes.rb裡,如果版本很多,就會打很長一票,等一下會有比較常見的做法,先打一個最簡單的吧~

再來是重頭戲,app/api/general_api.rb的內容,就循序漸進來打吧,由簡至繁。

module GeneralApi
  class ApiV1 < Grape::API
    format :json

    desc "我的第一支grape api"

    params do
    end

    get '/sayhi' do
      {greeting: "Hi hi!"}
    end
  end
end

好了,這樣基本上可以快樂的跑rails server了,而且也完成了一個簡單的api。
在網址列輸入localhost:3000/api/v1/sayhi,console下會看到
Started GET "/api/v1" for 127.0.0.1 at 2016-04-30 14:01:37 +0800
瀏覽器會得到一個json,就是上面打的{greeting: "Hi hi!"},在get下的最後一行可以直接帶hash就好,grape會自動轉成你要求的格式(此例是寫在最上面的format :json)。

重點是,desc, params, get/post等三個為一組,不可少。desc是對此api的描述,params是參數,get是http method。

再來說明params

module GeneralApi
  class ApiV1 < Grape::API
    format :json

    desc "我的第一支grape api"

    params do
      requires :name, type: String, desc: "名字為必需"
      optional :gender, type: String, desc: "性別可有可無"
    end

    get '/sayhi' do
      title = ""
      if params[:gender] == "female"
        title = "小姐"
      elsif params[:gender] == "male"
        title = "先生"
      end
      {greeting: "Hi hi! #{params[:name]} #{title}"}

    end
  end
end

打這個http://localhost:3000/api/v1/sayhi?name=sharefun

{"greeting": "Hi hi! sharefun "}

打這個http://localhost:3000/api/v1/sayhi?name=sharefun&gender=male會得到

{"greeting": "Hi hi! sharefun 先生"}

而如果沒有給requires的東西,就會自動回錯誤,相當方便!
http://localhost:3000/api/v1/sayhi?gender=male

{"error": "name is missing"}

但是有打name但是給空值也是可以...也就是還要另外再過濾
http://localhost:3000/api/v1/sayhi?name=&gender=male
只要加上validator就行了!名字那行改成

requires :name, type: String, allow_blank: false, desc: "名字為必需"

可以參照文檔,還有許多的validator可用~

再來加一個helper玩玩~

module GeneralApi
  class ApiV1 < Grape::API
    format :json
    prefix ''

    helpers do
      def find_title(title, gender)
        if gender == "female"
          title = "小姐"
        elsif gender == "male"
          title = "先生"
        else
          title = ""
        end
      end
    end

    desc "我的第一支grape api"

    params do
      requires :name, type: String, allow_blank: false, desc: "名字為必需"
      optional :gender, type: String, desc: "性別可有可無"
    end

    get '/sayhi' do
      title = find_title(title, params[:gender])
      {greeting: "Hi hi! #{params[:name]} #{title}"}
    end
  end
end

基本上功能沒變,只是熟悉一下helper寫法。而prefix會掛在/api/v1下,如果prefix '123',這個sayhi就會變成在/api/v1/123/sayhi。

再來我們分層mount。也就是routes.rb只mount一個class,該class再mount其他class。修改下列兩個檔案
routes.rb

# mount GeneralApi::ApiV1 => '/api'

mount GeneralApi::API => '/api'

app/api/general_api.rb整個改成

module GeneralApi
  class ApiV1 < Grape::API
    format :json
    prefix ''

    helpers do
      def find_title(title, gender)
        if gender == "female"
          title = "小姐"
        elsif gender == "male"
          title = "先生"
        else
          title = ""
        end
      end
    end

    desc "我的第一支grape api"

    params do
      requires :name, type: String, allow_blank: false, desc: "名字為必需"
      optional :gender, type: String, desc: "性別可有可無"
    end

    get '/sayhi' do
      title = find_title(title, params[:gender])
      {greeting: "Hi hi! #{params[:name]} #{title}"}
    end
  end

  class ApiV2 < Grape::API
    format :json

    desc "我的第二支grape api"

    params do
    end

    get '/sayhi' do
      {greeting: "大家好!"}
    end
  end

  class API < Grape::API
    mount GeneralApi::ApiV1 => '/v1'
    mount GeneralApi::ApiV2 => '/v2'
  end
end

原本的api的url基本保持不變,但我新增另一個第二版的sayhi,因為屬不同的class,改原本的helper不會影響第二版,但此分法不是絕對,可依個人喜好去調整。惟須記得class API要放在下面,不然會因為讀不到ApiV1而出錯。

補充:

因為跑rake,這些routes在routes.rb是找不到的。我找到一個別人寫好的lib可以讀grape api。放在lib/task/routes.rake

namespace :grape do
  desc 'Print compiled grape routes of Airpopo'
  task :routes => :environment do
    GeneralApi::API.routes.each do |route|
      puts route
    end
  end
end

打rake grape:routes就可以看到目前的api routes。要注意,因為這邊只進去module裡看,所以寫在routes.rb裡的prefix不會被讀到,所以...自己心裡有底就好,我打的api是在/api/v1/而不是在/

最後,因為打API有很多時候是要給別的網站串的,所以我們須要開放跨來源資源共享(CORS),詳見高手文章:
http://blog.toright.com/posts/3205/%E5%AF%A6%E4%BD%9C-cross-origin-resource-sharing-cros-%E8%A7%A3%E6%B1%BA-ajax-%E7%99%BC%E9%80%81%E8%B7%A8%E7%B6%B2%E5%9F%9F%E5%AD%98%E5%8F%96-request.html
https://developer.mozilla.org/zh-TW/docs/HTTP/Access_control_CORS

Rack based的app server可以用這邊提供的方法解掉。
https://github.com/cyu/rack-cors

 
over 3 years ago

Part 1 虛擬環境設置

在這裡小卡關,因為機器沒有開啟Visualization,又因為一開始沒有在Vagrantfile裡打開virtualbox視窗的選項,所以看到錯誤訊息一直不明究理。總之,到BIOS設定開啟visualization後就好了。

參考資料

使用Vagrant + VirtualBox,使用ubuntu-trusty-64鏡像檔開啟後
vagrant ssh登入(預設會使用vagrant/vagrant登入)
因為user和ssh已經被Vagrant設定好了,這邊先跳過

之前用慣VirtaulBox的圖形介面,把Vagrant CLI與其相對應一下
vagrant init = 從image開新的vm
vagrant up = 開啟該vm
vagrant halt = 把該vm關機
vagrant destroy = 從VirtualBox移除該vm(image並不會移除)
vagrant box list = 看有那些image(box)
vagrant box remove _box_name_ = 移除特定image(box)

(Vagrant基礎說明 http://www.codedata.com.tw/social-coding/vagrant-tutorial-4-guest-host-communication/

Part 2 更新

sudo apt-get update
sudo apt-get upgrade

Part 3 新增使用者sharefun

sudo adduser sharefun or sudo adduser --disabled-password sharefun
sudo passwd sharefun
sudo su # retrieve root
adduser sharefun sudo # make sharefun have sudo power
exit
sudo su sharefun # switch to sharefun

Part 4 ssh 登入

沒有公私鑰才使用下列指令
ssh-keygen -t rsa
複製本機的 ~/.ssh/id_rsa.pub 到機器的 /home/sharefun/.ssh/authorized_keys
cat ~/.ssh/id_rsa.pub | ssh sharefun@xxx.xxx.xxx.xxx 'cat >> ~/.ssh/authorized_keys'
chmod 644 /home/sharefun/.ssh/authorized_keys
chown sharefun:sharefun /home/sharefun/.ssh/authorized_keys

更改ssh登錄port,並拒絕密碼登入,在server的修改
sudo vi /etc/ssh/sshd_config

PasswordAuthentication no

執行sudo service ssh restart使之生效
如果之後搞壞.bashrc,導致登不進去(我就發生這個問題),可以在不跑.bashrc的情況登入(因為已經不能用密碼登入了)
ssh -t username@xxx.xxx.xxx.xxx /bin/sh

Part 5 裝機

這兩篇寫得蠻詳細的,本篇主要照這兩篇來做,並在下方輔助一些參考資料

(極客技術網 http://www.rails365.net/articles/bu-shu-zhi-zai-a-li-yun-ubuntu-zhu-ji-shang-an-zhuang-ruby-on-rails-huan-jing-liu
(Ruby on Rails 實戰聖經部署篇 https://ihower.tw/rails4/deployment.html
(xdite http://rails101s.logdown.com/posts/247891-project-deploy-on-the-server

  • 先裝git sudo apt-get install git
  • 調整時區 sudo dpkg-reconfigure tzdata
  • 安裝常用套件,以實戰聖經為基礎,新增一些常用的套件,包含nodejs, imagemagick, redis
// Build tool and library for ruby
sudo apt-get install build-essential zlib1g zlib1g-dev libssl-dev libreadline6-dev libyaml-dev git-core bison openssl curl libsqlite3-0 libsqlite3-dev sqlite3 autoconf libc6-dev libpcre3-dev curl libcurl4-nss-dev libxml2-dev libxslt-dev libffi-dev redis-server imagemagick nodejs ruby-dev liblzma-dev libgmp-dev

// Nodejs升到最新版4.x
curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
sudo apt-get install -y nodejs

// Install rvm and Ruby
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
\curl -sSL https://get.rvm.io | bash -s stable // 預設裝在使者目錄
\curl -sSL https://get.rvm.io | sudo bash -s stable // 裝在/usr/local/rvm

rvm install 2.3.0
rvm --default use 2.3.0
gem install bunlder --no-doc --no-ri
  • 裝postgres
    請見postgresql on ubuntu
    安裝完發現無法啟動
    sudo service postgresql start
    發現錯誤訊息,是因為語系沒有先設定好,所以安裝過程出錯
    * No PostgreSQL clusters exist; see "man pg_createcluster"
    根據這篇繼續找原因 http://dba.stackexchange.com/questions/50906/why-wont-postgresql-9-3-start-on-ubuntu
    sudo vi /etc/default/locale
    加入三行

    LANG="en_US.UTF-8"
    LANGAUGE="en_US.UTF-8"
    LC_ALL="en_US.UTF-8"
    

    sudo dpkg-reconfigure locale
    sudo pg_createcluster 9.3 main --start
    再回去打第一條,打完收工~

  • 裝Nginx + Passenger
    移除系統預裝的nginx
    sudo apt-get purge nginx nginx-full nginx-light nginx-naxsi nginx-common
    sudo rm -rf /etc/nginx

gem install passenger --no-ri --no-rdoc
which passenger-install-nginx-module
rvmsudo 上面的輸出
碰到虛擬記憶體不足的問題,依建議輸入指令即可解決
驗證安裝是否成功
rvmsudo passenger-config validate-install
片尾有訊息說要改passenger和ruby的路徑,可用下列指令查詢,並在其下找passenger path
which ruby
which passenger

如果無法啟動rails server,出現這樣的訊息

The program 'rails' can be found in the following packages:

  • ruby-railties-3.2
  • ruby-railties-4.0 Try: sudo apt-get install

在.bashrc加入
source ~/.profile
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

接著我們設定 Nginx 啟動腳本:

git clone git://github.com/jnstq/rails-nginx-passenger-ubuntu.git
sudo mv rails-nginx-passenger-ubuntu/nginx/nginx /etc/init.d/nginx
sudo chown root:root /etc/init.d/nginx

nginx操作

sudo service nginx start
sudo service nginx stop
sudo service nginx restart
sudo service nginx configtest

Part 6 用 Capistrano 部署 rails app

Capistrano在本機運行,它的工作是登入你的伺服器,進行一系列shell操作。
首先先在本地端安裝capistrano gem
gem install capistrano
然後在Gemfile中加上(實際上依applicatoin server的不同,下面會有所不同)

gem 'capistrano-rails', :group => :development
gem 'capistrano-passenger', :group => :development

在專案下執行初始化capistrano
cap install 會產生Capfile(基礎設施宣告,宣告會用到的外掛和自訂任務), config/deploy.rb(全域變數設定檔,會被所有stage引用), config/deploy/production.rb(不同的部署環境), config/deploy/staging.rb(不同的部署環境)

基本上,Capfile裡面就有說明文檔的連結,可以知道怎麽設定deploy.rb。看這篇了解這些生出來的檔案都寫了些什麽

做 deploy 前的檢查,並把遠端 (production) 需要的資料夾建立,如果有 error 請檢查錯誤訊息去 debug
cap production deploy:check

把secret.yml, database.yml複製一份到server,並且使用rake secret得到本地端的secret_key_base,貼至server。
gitignore上述兩個檔案。

執行 deploy 程序正式運作自動化 deploy
cap production deploy

bundle exec passenger start
bundle exec passenger restart

passenger-config validate-install

passenger-status

passenger-config --root

If you want to restart using touch tmp/restart.txt, add this to your config/deploy.rb:
set :passenger_restart_with_touch, true

在第二台機器部署時,跑passenger錯誤:

/home/deploy/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/rubygems/specification.rb:2158:in `method_missing': undefined method `this' for #<Gem::Specification:0xcbfb74 passenger-5.0.27> (NoMethodError)

gem -v 可以看目前版本
是因為rubygem在2.5.1有問題,所以降版改為2.4.8
gem update --system 2.4.8 --no-ri --no-rdoc
ref

Part 7 第一次部署

bundle exec rake db:create RAILS_ENV=production
bundle exec rake db:migrate RAILS_ENV=production
RAILS_ENV=production passenger start

Production environment build up 其他參考資料

Passenger
https://www.phusionpassenger.com/library/

Rocodev
https://github.com/rocodev/guides/wiki

Ruby China
https://ruby-china.org/wiki/deployment-rails

其他
http://wulfric.me/2016/03/ruby-on-rails-on-aliyun/

Linode + capistrano
https://github.com/rocodev/guides/wiki/setup-production-development

Ruby China
https://ruby-china.org/topics/18616

Unicore + nginx
http://tech.gadii.net/blog/2013/03/07/rails-deploy-ji-chu-jiao-xue/

Nginx範例和說明
http://docs.bitshares.org/testnet/8-nginx.html
http://rexlnamp.blogspot.tw/2015/09/nginx.html
http://z00w00.blog.51cto.com/515114/1031287

SSL

https://www.peterdavehello.org/2015/10/build-an-a-plus-best-practice-https-web-server-via-nginx-chinese-version/

Vagrant 參考資料

Vagrant多機器
http://gogojimmy.net/2013/05/26/vagrant-tutorial/

Vagrant Official site
https://www.vagrantup.com/docs/cli/

Vagrant + Docker
http://samchu.logdown.com/posts/288889-docker-fast-with-vagrant-and-construct-development-and-test-environments

 
over 3 years ago

參考ihower的Ruby on Rails實戰聖經的ActionMailer - E-mail 發送章節。

在development.rb裡加入寄信的設定。

...
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.default_url_options = { host:"http://localhost:3000" }
  config.action_mailer.smtp_settings = {
    :address => "smtp.gmail.com",
    :port => "587",
    :domain => "gmail.com",
    :authentication => "plain",
    :user_name => "我的帳號@gmail.com",
    :password => "我的密碼",
    :enable_starttls_auto => true
  }
  ...

建立Mailer寄信程式,並嘗試寄給自己

> rails generate mailer UserMailer notify_comment

# In user_maiiler.rb
class UserMailer < ActionMailer::Base
    default :from => "我的帳號@gmail.com"

    def notify_comment
        mail(:to => "我的帳號@gmail.com", :subject => "New Comment")
    end
end

# 寄信
> rails c
> UserMailer.notify_comment.deliver_now!

但是出現錯誤Net::SMTPAuthenticationError: 534-5.7.9 Application-specific password required.

這是因為我使用了二階段驗證,如果要用gmail寄信,必須先從google設定App Password。

以下是解法網址Sign in using App Passwords設定App Password的網址

得到密碼後,直接取代掉development.rb中的:password => "我的密碼"即可。

 
almost 4 years ago

User story reference

Reference (read):
User Stories (1) 什麼是 User Story? - by ihower
人人都該學習的技術:從 Idea 到成品,撰寫 User Story 的能力 - by xdite
如何寫出 User Story
Agile Atlas - User story 執行細節
10 Tips for Writing Good User Stories - 簡明的10個Tips

Agile Reference (unread):
Displined Agile 2.0
All About Agile
Extreme Programming
Agile Modeling

 
almost 4 years ago

One-to-one

Database level

在migration內的 t.references, index: true, foreign_key: true
會產生 CREATE INDEX "parent_table" ON "child_table" ("parent_table_id"); 這個SQL
(或使用add_index在migration中)

Model level

在parent_table和child_table中各加入has_one和belongs_to

.build_xxx
.create_xxx

前者只建立在記憶體
後者直接建立在資料庫
但兩者都會把使用該method的實例的id移除(因為這筆新資料才要指向原擁有者,而不是舊資料)

One-to-many

當parent被刪除時,可以用dependency設定其他所屬資料的處理方式
has_many :jobs, dependent: :nullify # set foreign key to null (預設值)
has_many :jobs, dependent: :delete # 刪除該子資料使用delete
has_many :jobs, dependent: :destroy # 刪除該子資料使用destroy

Many-to-many

# 假設現在已經有people table和Person model
rails g model hobby name
rails g migration create_hobbies_people person:references hobby:references  # 產生join table於database,但不用做model
# in migration
def change
    create_table :hobbies_people, id: false, do |t|
    t.references :person, :index => true, :foreign_key => true
    t.references :hobby, :index => true, :foreign_key => true
  end
end
# in model
has_and_belongs_to_many

Rich many-to-many

# person -> job -> salary_range, 三個 model,想知道一個person所有的salary_range
# 建立person和job的關係,再以job作為join table
rails g model salary_range, min_salary:float, max_salary:float, job:references

# in job.rb
belongs_to :person
has_one :salary_range

# in salary_range.rb
belongs_to :job

# in person.rb
has_many :jobs
has_many :approx_salaries, through: :jobs, source: :salary_range

# in person.rb 再加入
def max_salary
    approx_salaries.maximum(:max_salary)
end
# 可使用someperson.max_salary,會生出INNER JOIN的query,還不太了解through的功用

Scope - 永遠回傳ActiveRecord::Relation (等同class method), 可以使query更具有表達力

default scope 預設的資料如何被取出來的method

class SomeModel < ActiveRecord
    default_scope {order :name}
end
在query中會自動插入ORDER BY

named scopes

# 格式為 scope :name, lambda

scope :order_by_age, -> {order age: :desc}
scope :starts_with, -> (starting_string){ where {"first_name LIKE ?","#{starting_string}%"}}

Custom validation

validate :min_is_less_than_max     # single

def min_is_less_than_max
    if min_salary > max_salary
    errors.add(:min_salary, "Error on value")       # min_salary為欄位名,第二參數為錯誤值
  end
end