2017/11/8
rails - encode & decode
markdown
HTML encode
```
require 'cgi'
CGI.escapeHTML('<')
# "<"
```
HTML decode
```
require 'cgi'
CGI.unescapeHTML('<')
# "<"
```
2017/10/26
Rails - Apartment 的使用方法
markdown
#簡介
Apartment : [https://github.com/influitive/apartment](https://github.com/influitive/apartment)
這個套件的功能是讓你在 rails 可以透過切換 db schema,來存取不同的資料。
在 postgresql 中,預設的 schema 名稱為 public ,而所有的 table 都在 schema 'public' 下。
你可以簡單地把 schema 看成 namespace,在 apartment 中,這被稱為是 tenant。
#安裝步驟
請參考 : [https://github.com/influitive/apartment](https://github.com/influitive/apartment)
#使用方法
## 在 rails c 環境下
新增 tenant
```
Apartment::Tenant.create('abc')
```
這個指令會在 schema 'abc' 下複製 public 上的所有表格。所以 tenant 就是具有相同表格的 schema。
在以下的示範,看到 abc 的地方,代表的是要填入你的 schema name。
顯示目前所在的 tenant
```
Apartment::Tenant.current
# "public"
```
切換 tenant
```
Apartment::Tenant.switch!('abc')
Apartment::Tenant.current
# "abc"
Apartment::Tenant.switch!('public')
Apartment::Tenant.current
# "public"
Apartment::Tenant.switch('abc') do
Apartment::Tenant.current
# "abc"
end
Apartment::Tenant.current
# "public"
```
有暫時切換跟永久切換兩種
## 在 rails db 環境下
列出所有 schema
```
\dn
# List of schemas
# Name | Owner
#--------+----------
# public | postgres
# abc | etrex
# (2 rows)
```
顯示目前所在的 schema
```
SHOW search_path;
# search_path
#-----------------
# "$user", public
# (1 row)
```
切換 schema
```
SET search_path TO abc;
# SET
SET search_path TO 'abc';
# SET
```
字串要不要加引號都可以,當 search_path 的值不在 schema 的列表上時不會跳 error,可以想像成連接到一個空的 schema,而這樣並不代表新增了一個 schema。
在 select 的當下使用 schema
```
SELECT * FROM schema_name.table_name;
```
## 關於 excluded_models
在 apartment 提供的功能中有一個功能,這個功能是可以設定在切換 schema 的時候,讓某些表格不要被切換,也就是做成全域的表格。
舉例來說,假設我有兩個 table,是使用以下 code 所生成。
```
# 這裡是 bash
rails g model a
rails g model b a:references
rails db:migrate
```
然後我將 B 做成全域的表格
```
# 這裡是 /config/initializers/apartment.rb 約 18 行的位置
config.excluded_models = %w{ B }
```
然後建立一個 tenant 'abc' 並且切換過去,在這個環境下做測試
```
# 這裡是 rails c 環境下
Apartment::Tenant.create('abc')
Apartment::Tenant.switch!('abc')
```
對 A 做一些事情
```
A.count
# (3.8ms) SELECT COUNT(*) FROM "as"
# => 0
```
對 B 也做一些事情
```
B.count
# (0.5ms) SELECT COUNT(*) FROM "public"."bs"
# => 0
```
在這裡可以看到,當你對 B 進行操作時,其實是會強制加上 "public" 的,也就是說,對於 B ,不管在哪一個 tenant 下,都只會去存取 schema 'public' 下的表格,所以其他的 schema 的表格 bs 應該會都是空的。
嘗試在 tenant 'abc' 下新增資料
```
a = A.create()
# (0.2ms) BEGIN
# SQL (0.4ms) INSERT INTO "as" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2017-10-25 17:16:22.084365"], ["updated_at", "2017-10-25 17:16:22.084365"]]
b = B.create(a:a)
# (0.2ms) BEGIN
# SQL (1.5ms) INSERT INTO "public"."bs" ("a_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["a_id", 3], ["created_at", "2017-10-25 17:16:24.957753"], ["updated_at", "2017-10-25 17:16:24.957753"]]
# (0.2ms) ROLLBACK
#ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "bs" violates foreign #key constraint "fk_rails_ddf8c0c4b5"
#DETAIL: Key (a_id)=(3) is not present in table "as".
#: INSERT INTO "public"."bs" ("a_id", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id"
# from (irb):31
```
這時候出大問題了,因為 A 建立在 abc 下,但是 B 建立在 public 下。而 public.bs 有一個外來鍵限制是 a_id 必須要在 public.as 中有出現。
使用 [pgAdmin](https://www.pgadmin.org/) 連進去看,對 db_name/Schemas/public/Tables/bs/Constraints/fk_rails_ddf8c04b5 按右鍵選 CREATE Script:
。
會看到生成的 script 內容為:
```
-- Constraint: fk_rails_ddf8c0c4b5
-- ALTER TABLE public.bs DROP CONSTRAINT fk_rails_ddf8c0c4b5;
ALTER TABLE public.bs
ADD CONSTRAINT fk_rails_ddf8c0c4b5 FOREIGN KEY (a_id)
REFERENCES public."as" (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION;
```
在這裡明確地指出跟 public.bs 有關係的表格是 public.as ,而不是其他 schema 下的 as,但是 Apartment 不處理,所以會出問題。
2017/9/14
rails debug 技巧
markdown
在 controller 裡的 action 可以取得 request 物件
```
class TestController < ApplicationController
def index
p request.class
render plain: "ok"
end
end
```
會發現 request 物件是 [ActionDispatch::Request](http://api.rubyonrails.org/classes/ActionDispatch/Request.html) 類別
request.headers 是 [ActionDispatch::Http::Headers](http://api.rubyonrails.org/classes/ActionDispatch/Http/Headers.html) 類別
我們可以用 each 去印出 headers 的所有內容:
```
request.headers.each{|header| p header }
```
仔細觀察會發現 header 是一個長度為2的陣列, header[0] 代表 key,header[1] 則代表 value。
```
def index
headers = request.headers.to_a.keep_if { |header|
header[0].index('HTTP') == 0
}.map{ |header|
"#{header}"
}
render plain: headers.join("\n")
end
```
可以透過 keep_if 保留想要關注的資訊
2017/8/28
rails 直接執行 SQL 且避免 sql injection
markdown
以下是一個 sql update 的範例程式:
```
old_value = ActiveRecord::Base.sanitize(old_value)
new_value = ActiveRecord::Base.sanitize(new_value)
updated_at = ActiveRecord::Base.sanitize(Time.current)
results = ActiveRecord::Base.connection.execute(
"update table
set field=#{new_value},
updated_at=#{updated_at}
where field=#{old_value}"
)
```
2017/7/19
rails db migrate 寫錯時如何重跑 migrate
markdown
1. 先把開發db跟測試db還原到上一步驟
```
rails db:rollback
rails db:rollback RAILS_ENV=test
```
2. 修改 migration 檔案
3. 再重新執行 migrate (他會把開發db跟測試db都更新)
```
rails db:migrate
```
如果一開始忘記跑測試db的 rollback 也沒關係,執行下面的指令就好
```
rails db:rollback RAILS_ENV=test
rails db:migrate RAILS_ENV=test
```
2017/3/21
rails db & postgresql cli 使用方法
markdown
# postgresql 相關指令
查看有哪些資料庫
```
\l
```
連線到資料庫
```
\c db_name
```
查看有哪些表格
```
\dt
```
查看有哪些欄位
```
\d+ table_name
```
查看執行過的資料庫遷移
```
select * from schema_migrations;
```
# rails db 相關指令
開啟含有 rails 環境的 irb
```
rails console
```
連線到資料庫
```
rails dbconsole
```
建立資料庫,如果資料庫已經存在則不做事
```
rails db:create
```
刪除資料庫
```
rails db:drop
```
根據 db/schema.rb 檔產生對應的資料表(此動作會將資料庫內的資料清空)
```
rails db:schema:load
```
根據資料庫狀態更新 db/schema.rb 檔 (但只要叫 db:migrate 就會自動執行這個了)
```
rails db:schema:dump
```
執行db/seeds.rb
```
rails db:seed
```
##快速功能
###setup
```
rails db:setup
```
等於以下指令
```
rails db:create
rails db:schema:load
rails db:seed
```
###reset
```
rails db:reset
```
等於以下指令
```
rails db:drop
rails db:setup
```
在這裡要注意的是setup跟reset指令不是重新執行migrate,所以如果你的schema檔是壞的,你下這兩個指令完還是壞的。
如果想要根據migration檔重新建立一個資料庫應該下的指令是
###migrate:reset
```
rails db:migrate:reset
```
等於以下指令
```
rails db:drop
rails db:create
rails db:migrate
```
這裡要注意的是, db:migrate:reset 指令並不會執行 db:seed
執行資料庫遷移,會把所有還沒執行過的都執行完,然後自動更新 db/schema.rb 檔
```
rails db:migrate
```
查看資料庫遷移結果
```
rails db:migrate:status
```
還原上一筆資料庫遷移
```
rails db:rollback
```
執行下一筆資料庫遷移
```
rails db:forward
```
##參考資料
[RailsGuide Migrations(資料庫遷移)](http://guides.ruby.tw/rails3/migrations.html)
# schema 相關 psql 指令
列出所有 schema
```
\dn
# List of schemas
# Name | Owner
#--------+----------
# public | postgres
# abc | etrex
# (2 rows)
```
顯示目前所在的 schema
```
SHOW search_path;
# search_path
#-----------------
# "$user", public
# (1 row)
```
切換 schema
```
SET search_path TO abc;
# SET
SET search_path TO 'abc';
# SET
```
字串要不要加引號都可以,當 search_path 的值不在 schema 的列表上時不會跳 error,可以想像成連接到一個空的 schema,而這樣並不代表新增了一個 schema。
在 select 的當下使用 schema
```
SELECT * FROM schema_name.table_name;
```
##參考資料
[https://stackoverflow.com/questions/34098326/how-to-select-a-schema-in-postgres-when-using-psql](https://stackoverflow.com/questions/34098326/how-to-select-a-schema-in-postgres-when-using-psql)
訂閱:
文章 (Atom)
