Skip to main content

· 13 min read

這次官網的目的是要做出一個靜態的網站

然後提供非技術人員能夠寫稿然後發表(包含上傳圖片還有一些比較客製化的樣式)

這種東西比較精確的名詞是 Static Site Generators

可以參考 https://www.staticgen.com/ 看看每個框架的 star, issue, fork 數量

看看這些數值的變動來決定使用什麼框架

當不知道要選什麼框架的時候可以從一些方向下手

  • 程式語言
  • 使用人數多寡,使用人數比較多的話也會比較多人遇到相同問題,這樣有解的機率也會比較高

主要需求:

  • i18n

/zh/index.html

顯示中文的頁面

/en/index.html

顯示英文的頁面

  • 不同頁面呈現不同 category 的文章

像是 /blog

會顯示出 front matter 的 category 是 blog 的文章

  • 能夠照 i18n 顯示對應語系的文章

像是 /zh/blog

顯示出 front matter 的 category 是 blog 而且 locale 是 zh 的文章

  • 各自 category 的文章照 tag 分類顯示

像是 /blog/life

會顯示出 front matter 的 category 是 blog 的文章而且 tag 是 life 的文章

i18n

主要是參考這篇文章 https://forestry.io/blog/creating-a-multilingual-blog-with-jekyll/

index.html

---
permalink: /
i18n_prefix: index
lang: en
---

{% t title %}
// hello

zh/index.html

---
permalink: /zh
i18n_prefix: index
lang: en
---

{% t title %}
// 你好

_data/en.yml

index:
title: hello

_data/zh.yml

index:
title: 你好

_plugins/i18n_tag

module Jekyll
class I18nTag < Liquid::Tag
def initialize(tag_name, text, tokens)
super
@text = text.strip
end

def render(context)
site = context.registers[:site]
page = context.registers[:page]
path = @text.split('.')
if site.data[page['locale']][page['i18n_prefix']]&.dig(*path)
site.data[page['locale']][page['i18n_prefix']].dig(*path)
else
site.data[page['locale']].dig(*path)
end
end
end
end

Liquid::Template.register_tag('t', Jekyll::I18nTag)

Pagination

使用這個套件 https://github.com/sverrirs/jekyll-paginate-v2

這個套件能夠依照 locale 和 category 和 tag 來產生 pagination

index.html

---
layout: index
permalink: /
pagination:
per_page: 5
enabled: true
locale: en
---

resource.html

---
layout: resource
permalink: /
pagination:
categories:
- resource
per_page: 5
enabled: true
locale: en

---

resource-browse-by-tag-technical

---
layout: resource-browse-by-tag-technical
permalink: /
pagination:
categories:
- resource
tags:
- technical
per_page: 5
enabled: true
locale: en
---

這邊用比較智障的方法產生 browse by tag 的網頁

原本是想說用 Jekyll 的 generator plugin 先把某個 category 的文章 tag 拿出來,然後依照 tag 產生 page

我大概寫了三天放棄了

這個方法是行不通的如果要用 jekyll-pagination-v2 的話

我猜可能用原生的 pagination 有可能可以

但是沒有想要去嘗試了

Jekyll 有個爛的點就是他的 plugin 都是要用猜的去寫

我的方法是去參考現在有的各個 plugin

然後開 Jekyll::Page 的文件邊寫邊猜

給上稿人員使用的網站

很推薦這個網站 https://forestry.io/

其實 i18n 的作法就是這個網站的文章

CSS 小技巧或是

image wrapper

我自己是比較喜歡在外面再加一層 wrapper 然後用外框來控制寬度和高度

寬度 300px 然後高度保持比例放大

HTML

<div class="logo">
<img src="...">
</div>

CSS

.logo{
width: 300px;
img{
width: 100%;
}
}

Aspect Ratio Boxes

可以參考這篇文章 https://css-tricks.com/aspect-ratio-boxes/

比如說想要比例是 4:3 (寬 vs 高)

寬度佔滿 100% 垂直至中對齊多餘的隱藏起來

HTML

<div class="ratio-box">
<img src="...">
</div>

Original CSS

.ratio-box{
overflow: hidden;
position: relative;
&::after {
content: "";
display: block;
padding-top: 75%;
}
img {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 100%;
}
}

Inspired by one of my friend's CSS

.ratio-box{
&::after {
content: "";
display: block;
padding-top: 75%;
}
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}

https://css-tricks.com/nested-links/

HTML

<a href="...">
<a href="...">
...
</a>
</a>

這樣其實就會有非預期的現象發生應該避免

Stop box-sizing everything

https://aastudio.fr/box-sizing.html

一開始在寫的時候發現有 box-sizing 這東西好方便喔

你設寬度是多少就是多少

就直接把全部東西都這樣寫了

HTML

  html { box-sizing: border-box; }

後來會發現有些東西根本不需要或是能用 padding + border 算就可以了

就全部改回來了

Mobile first

一開始在刻的時候先刻 desktop 版再刻 mobile 版

事實上應該反過來

這個我刻到一半的時候發覺這件事後就改過來了

Don't affect positioning of other elements on border-width changes

你可能會想要有 hover 的時候

元素下面加一下底線

但是你加了 border 元素的大小就改變了

就會產生移位

  1. 使用 box-shadow
  2. 你可以先把 border 加上去但是 transparent 等到 hover 的時候再改變顏色顯示出來

CSS Naming

OOCSS(Object Oriented CSS)

HTML

<button class="btn btn-small btn-primary"></button>

SMACSS

HTML

<ul class="nav">
<li class="nav-item">
<a class="nav-link active" href="#">Active</a>
</li>
</ul>

BEM

There are only two hard problems in Computer Science: cache invalidation and naming things — Phil Karlton

Block

block-name

HTML

<div class="block">
...
<span class="block__elem"></span>
</div>

CSS

.block { color: #042; }

Element

block-name__element-name

HTML

<div class="block">
...
<span class="block__elem"></span>
</div>

CSS

Good
.block__elem { color: #042; }
Bad
.block .block__elem { color: #042; }
div.block__elem { color: #042; }

Modifier

其實 Modifier 的寫法比較有歧異

有兩種寫法

block-name__element-name--modifier-name-modifier_value

block-name__element-name_modifier-name_modifier_value

我自己是比較喜歡後者

以下例子都用後者的寫法

HTML

Good
<div class="block block_mod">...</div>
<div class="block block_size-big
block_shadow_yes">...</div>
Bad
<div class="block_mod">...</div>

CSS

Use modifier class name as selector:
.block_hidden { }
To alter elements based on a block-level modifier:
.block_mod .block__elem { }
Element modifier:
.block__elem_mod { }

Example

HTML

<form class="form form_theme_xmas form_simple">
<input class="form__input" type="text" />
<input
class="form__submit form__submit_disabled"
type="submit" />
</form>

CSS

.form { }
.form_theme_xmas { }
.form_simple { }
.form__input { }
.form__submit { }
.form__submit_disabled { }

Youtube

詳細可以參考這篇文章 https://codeburst.io/understanding-css-bem-naming-convention-a8cca116d252

HTML

<header class="youtube-header">
<img class="youtube-header__logo"/>
<div class="youtube-header__search"></div>
<ul class="youtube-header__list">
<li class="youtube-header__item></li>
<li class="youtube-header__item></li>
</ul>
</header>

SASS

.youtube-header {
// rules
&__logo {
// rules
}
&__search {
// rules
}
&__list {
// rules
}
&__item {
// rules
}
}

手好癢好想寫 nested element

可以先看完這篇 https://en.bem.info/methodology/quick-start/#nesting-1

要遵守三點:

  • Elements can be nested inside each other.
  • You can have any number of nesting levels.
  • An element is always part of a block, not another element. This means that element names can't define a hierarchy such as block__elem1__elem2.

Example

可能會遇到一種情況有 block 和 elem1 和 elem2

elem1 和 elem2 相對於 block

那就可以寫成 block__elem1block__elem2

但是 elem2 是相對於 elem1 的

可以忽視 elem2 對於 elem1 的關係

畢竟他們都是相對於 block

要是你將 elem2 nested 在 elem1 裡

那你就無法讓 elem2 在沒有 elem1 的情況下獨立存在

<form class="search-form">
<div class="search-form__content">
<input class="search-form__input">

<button class="search-form__button">Search</button>
</div>
</form>

或是將 elem1 加上 elem2 當成組合字當成一個新的 element elem1-elem2

block__elem1-elem2

<form class="search-form">
<div class="search-form__content">
<input class="search-form__content-input">

<button class="search-form__content-button">Search</button>
</div>
</form>

Block in Block

https://gist.github.com/Integralist/4960210

HTML

<div class="blocka">
<div class="blocka__blockb">
<div class="blocka__blockb__elementb>
</div>
</div>
</div>

Alternative HTML(1)

<div class="blocka">
<div class="blocka__elementa blockb">
<div class="blockb__elementb>
</div>
</div>
</div>

以 blocka__elementa 來代表和 blocka 的關係

Alternative HTML(2)

<div class="blocka">
<div class="blockb">
<div class="blockb__elementb>
</div>
</div>
</div>

如果 blocka 和 blockb 沒有相對關係的話可以省略 blocka-elementa

Block container

HTML

<div class="product-container>
<div class="product>
<div class="product__content">
</div>
</div>
</div>

Alternative HTML(1)

<div class="product>
<div class="product__inner>
<div class="product__content">
</div>
</div>
</div>

將 product 往上拉一層原本的用 product__inner 取代

Alternative HTML(2)

<div class="product-container>
<div class="product>
<div class="product__content">
</div>
</div>
</div>

不予理會 product-container 和 product 的關係

我自己是比較喜歡這個寫法

主要的分界點是在於 product 是否能獨立存在 product-container 外

Don't use nested selectors

只有你要改變 element 的 style based on block 的 modifier 的時候

.button_hovered .button__text
{
text-decoration: underline;
}
.button_theme_islands .button__text
{
line-height: 1.5;
}

或是你可以把原本的 SCSS 用 @at-root 改寫

既可以用巢狀結構包起來但是輸出卻不是 nested

bad SCSS

.block{
.block__element{
...
}
}

Good SCSS

.block{
@at-root #{&}__element{
...
}
}

Good CSS Output

.block{
...
}
.block__element{
...
}

Don't use combined selectors

HTML

<button class="button button_theme_islands button_active">...</button>

Bad CSS

.button.button_theme_islands {}
.button.button_active {}

你必須將兩個 modifier 拉到同樣 .button 的 level 才能有相同的 CSS 權重

Bad SCSS

.button {
.button_active{
...
}
.button_theme_islands{
...
}
}

Specific page HTML

<div class="index-page-button button button_active">
</div>

Specific page CSS

.index-page-button{
...
}

共用 button 的 style 將特定頁面的 style 寫在 index-page-button 裡

但是你會發覺蓋不過去 button 的 modifier 因為 button_active 和 button_theme_islands 的 Specificity

都比 index-page-button 高

Good CSS

.button_theme_islands {}
.button_active {}
.button {}

可以這樣寫利用 css import 的 order 來決定複寫的優先程度

// component (button)
@import "component";

// Specific page(index)
@import "index";

Exception

比如你有可能需要用 markdown 生成 html tag

在 .content class 的 block 套用你自己寫的 style

<div class="content">
... <!-- the user’s text -->
</div>
CSS rules:
.content a {
...
}
.content p {
font-family: Arial, sans-serif;
text-align: center;
}

Don't use tag selector

HTML

<header>
<a href="/">
<img src="img.logo.png" alt="Logo">
</a>
</header>

SCSS

header{
a{
...
}
}

像這樣的命名方式很難讓人去聯想 header 和 a 之間的關係

HTML 雖然有 semantic elements 但是現在能表示的語意還是比較有限

用 class name 去表示比較清楚

對於簡單和複雜的元素的策略

簡單的元素可以想辦法用 utilities class 來達成

HTML

<div class="once-block font-bold text-align-center">
</div>

複雜的元素其實就比較適合自己寫成一個 class 並撰寫 style,不然你的 html 裡的 class 可能就會太多,很複雜不好維護

HTML

<div class="muzukashii-block">
</div>

Nested nested block and element

Bad HTML

<header class="navber">
<div class="navbar-menu">
<div class="navbar-menu-item>
<div class="navbar-menu-item-submenu">
<div class="navbar-menu-item-submenu-item>
</div>
</div>
</div>
</div>
</header>

Good HTML

<header class="navber">
<div class="navbar-menu">
<div class="navbar-item>
<div class="navbar-submenu">
<div class="navbar-submenu-item>
</div>
</div>
</div>
</div>
</header>

這個我自己是覺得比較主觀的想法不一定符合 convension

寫 class 的關鍵就是不要撞名

主要的概念是 namespace 將這整個 navbar component 關在 navbar 這個 namespace 下

在適當的層數切成另一個新的 block

只要每個元素都關在 navbar namespace 底下就不會和其他元件撞名

CSS 感想

盡量讓元素都在同一層

讓 CSS 的 import order 來覆蓋相同的屬性

後來想了一下其實 BEM 可能沒有比較好

又再去看了一次 youtube 的網站

他們改回用 - 分隔了

當然如果能用 CSS Module 是最好的拉

這樣 class 名字都不會撞

一些下次可以改進的點

  • 可能需要跟設計師協調設計稿的 breakpoint,會是希望可以套框架的 convension 可以善用 utilities class 用 class 就組出簡單的 style
    • xs
    • sm(min-width: 576px)
    • md(min-width: 768px)
    • lg(min-width: 992px)
    • xl(min-width: 1200px)
  • 一開始就將所有頁面看完再開始寫,先將可以共用的 style 刻出來,再依照各個頁面不同的要求稍作調整,我一開始寫是一個頁面一個頁面刻,每次刻完一個頁面才發覺到其他頁面可以共用這個 style,但是為了共用 style 又必須稍做調整
  • 使用 rem 和 em 取代 px,這個有溝通過只是在設計的時候就用了 px 了,來不及

References

· One min read
Yangshun Tay

Blog features are powered by the blog plugin. Simply add files to the blog directory. It supports tags as well!

Delete the whole directory if you don't want the blog features. As simple as that!

· 2 min read

記錄一下寫 bamboofox 平台的心得

其實當初這個平台是為了 migrate 以前的課程所建置的

當初的課程平台只能容納一個課程

所以主要的設計是要能容納多個課程為主

為了註冊方便就寫了 OAuth 登入

大部分的時間都是在 survey OAuth 是個怎麼樣的東西

還有網站要怎麼架起來之類的

比較麻煩的點是想要一個帳號能夠 link 多個 OAuth provider

像是原本用 facebook 註冊登入後

又想要 link 別的 social media (ex: github, google)

主要是參考這篇文章 https://sourcey.com/rails-4-omniauth-using-devise-with-twitter-facebook-and-linkedin/

把和 OAuth 有關的欄位移出 User table 建立一個新的 table Identity

每個 user has_many identities

主要的邏輯是使用者登入可以 link 其他 OAuth

如果登入 OAuth 成功就新增 identity 給那個 user

下次登入的時候就會靠 identity 來找到同一個 user 了

主要架設平台的搭配是 nginx + passenger + rvm

migrate 的方式我是直接先架一個 phpmyadmin 起來

先新增新網站的 schema

然後慢慢把舊網站的資料改成新網站的形式

最後再直接 import 到新網站的資料庫

· 4 min read

簡單記錄一下 Ruby on Rails server setup 這裡用的是最新版的 Ubuntu 17.04 server 版 這篇會教你設定

  • nginx
  • passenger
  • Let's Encrypt(https)
  • postfix(mail server)

因為要設定 Let's Encrypt 和 postfix,所以要有一個 DNS 的 A Record 指向你的 ip 位置

Passenger

這裡主要有三種方法可以選

詳細內容可以參考這篇

  1. Standalone mode

只有 passenger

  1. Nginx integration mode

passenger 和 nginx 整合

  1. Apache integration mode

passenger 和 apache 整合

這裡選用的是第二種方法

因為 nginx 的效能比 apache 好

接著就開始安裝 nginx + passenger,大部分的步驟都跟這篇一樣

稍微要注意一下 ubuntu 的 code name,像是 17.04 的 code name 是 Zesty 在加 apt 的 repo 的時候要稍微改一下 code name

Install passenger packages

deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main => deb https://oss-binaries.phusionpassenger.com/apt/passenger zesty main

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

# Add our APT repository
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger zesty main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

# Install Passenger + Nginx
sudo apt-get install -y nginx-extras passenger

Enable the passenger nginx module and restart nginx

修改 /etc/nginx/nginx.conf 檔案 將 # include /etc/nginx/passenger.conf 取消註解

重啟 nginx

sudo service nginx restart

Notice

另外網路上還有另一種裝 passenger 和 nginx 的方法

是自行下載下來編譯,所有的套件最後會放在 /opt 資料夾底下

我自己是不推薦這種方法,因為還要自己去設定 service script

Let's Encrypt

詳細可以參考這篇

Postfix

設定好 Let's Encrypt 後就會有 ssh key 了

Install postfix package

sudo apt install -y postfix

Configure postfix

修改 /etc/postfix/main.cf

原本應該是

# TLS parameters
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key

修改成

# TLS parameters
smtpd_tls_cert_file=/etc/letsencrypt/live/bamboofox.nctucs.net/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/bamboofox.nctucs.net/privkey.pem
smtp_tls_security_level=may
smtpd_tls_security_level=may

我的 domain name 是 bamboofox.nctucs.net,這裡要修改成自己的 domain name

smtp_tls_security_level=maysmtpd_tls_security_level=may 是讓 email 寄信和收信加密

將自己的 ip 也加進 relay 的 list

修改 /etc/postfix/main.cf

mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128

修改成

mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 140.113.209.18 

這邊很重要因為之後用 devise 寄信的時候會有 certification 的問題

Restart postfix

sudo service postfix restart

Deploy ruby on rails project

參考這篇

直接從創 deploy 帳號開始做

Create deploy user

sudo adduser deploy
sudo adduser deploy sudo
su deploy

Install rvm

gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable
echo "source ~/.rvm/scripts/rvm" >> .bashrc
source ~/.rvm/scripts/rvm
rvm install 2.4.0
rvm use 2.4.0 --default

Install basic gems

gem install bundler
gem install rails

Show passenger config

執行指令

passenger-config about ruby-command

會得到

passenger-config was invoked through the following Ruby interpreter:
Command: /home/deploy/.rvm/gems/ruby-2.4.0/wrappers/ruby
Version: ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]
To use in Apache: PassengerRuby /home/deploy/.rvm/gems/ruby-2.4.0/wrappers/ruby
To use in Nginx : passenger_ruby /home/deploy/.rvm/gems/ruby-2.4.0/wrappers/ruby
To use with Standalone: /home/deploy/.rvm/gems/ruby-2.4.0/wrappers/ruby /usr/bin/passenger start

這要用在等下的 nginx config

Install nvm

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm
nvm install stable

Install yarn

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt-get update && sudo apt-get install yarn

Create basic app

rails new myapp --webpack=react

Deploy on nginx

修改 myapp 裡的 config/secrets.yml

這裡 secret 可以使用 rake secret 產生

production:
secret_key_base: d7913c3e87fadd4312ee1c5c1b13320caeecc72548f20b9122e2a1bf9ccdf0e9ecb86675168e578b5b3e960a81daa967c0081f69b082eb0c0e5df4b5810d71a9

修改 /etc/nginx/sites-available/default

server {

# SSL configuration
#
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
include snippets/ssl-bamboofox.nctucs.net.conf;
include snippets/ssl-params.conf;

# Add these three lines

passenger_enabled on;
server_name bamboofox.nctucs.net;
root /home/deploy/myapp/public;
}

重啟 nginx

sudo service nginx restart

· 2 min read

Rails best practices 是一個可以幫你檢查 Rails 專案架構的 gem

比如說你在 route 裡面增加了一些 routing path 但是你的 controller 沒有相對應的 action 的話

它就會幫你檢查出來,並且顯示警告訊息

Installation

Add following line to Gemfile.rb

gem 'rails_best_practices', require: false
gem 'rails_best_practices-rake_task', require: false

rails_best_practices 是本身檢查架構的 gem rails_best_practices-rake_task 是方便寫 rake task 的 gem

Customize

要是想要 custom 一些 config

可以打指令產生 config

rails_best_practices -g

會產生 /config/rails_best_practices.yml

config 大概長得像這樣

#MoveModelLogicIntoModelCheck: { use_count: 4 }
NeedlessDeepNestingCheck: { nested_count: 2 }
NotRescueExceptionCheck: { }
NotUseDefaultRouteCheck: { }
NotUseTimeAgoInWordsCheck: { }
#OveruseRouteCustomizationsCheck: { customize_count: 3 }
ProtectMassAssignmentCheck: { }
RemoveEmptyHelpersCheck: { }
#RemoveTabCheck: { }

不想要檢查的 rule 可以直接註解掉即可

Rake task

如果要整合 travis ci 在每次 code push 的時候檢查架構的話,就要把 rails_best_practices 寫成 rake task,然後寫成 rake default 會跑的 task

lib/tasks/rails_best_practices.rake 加上這幾行

require 'rails_best_practices/rake_task'

RailsBestPractices::RakeTask.new

然後修改一下 Rakefile

require_relative 'config/application'

Rails.application.load_tasks
task default: [:rails_best_practices]

這樣當你打 rake 的時候就會自動幫你檢查架構了

References

· 2 min read

Rubocop 是一個 Ruby static code analyzer 主要用來 format coding style

Installation

Add following line to Gemfile.rb

gem 'rubocop', require: false

Configuration

Config 可以參考 github 上的 Config 資料夾

裡面有三個檔案

  • default.yml(主要的)
  • enabled.yml(預設開啟的)
  • disabled.yml(預設關掉的)

改好後命名為 .rubocop.yml 放在 repo 裡,當你執行 rubocop 指令的時候就會讀取這個檔案的設定開始檢查語法

以下是我自己用的 Rules

把一些比較麻煩的 rules disable 掉了

AllCops:
Exclude:
- 'db/migrate/*'
- 'vendor/**/*'
Rails:
Enabled: true

Rails/HasAndBelongsToMany:
Enabled: false

Bundler/OrderedGems:
Enabled: false

Style/ClassAndModuleChildren:
Enabled: false

Style/ConditionalAssignment:
Enabled: false

Style/Documentation:
Enabled: false

Style/FrozenStringLiteralComment:
Enabled: false

Style/Next:
Enabled: false

Style/GuardClause:
Enabled: false

Metrics/LineLength:
Enabled: false

Metrics/BlockLength:
Enabled: false

Metrics/MethodLength:
Enabled: false

Metrics/AbcSize:
Enabled: false

rubocop 可以幫你自動修正一些語法,只要加上 --auto-correct 參數

rubocop --auto-correct

但是有時候想要一步一步修語法,我只想修正有關 Style 的語法,就要加上 --only 參數,其他以此類推

rubocop --auto-correct --only Style
rubocop --auto-correct --only HashSyntax
rubocop --auto-correct --only StringLiterals

Rake task

如果要整合 travis ci 在每次 code push 的時候檢查語法的話,就要把 rubocop 寫成 rake task,然後寫成 rake default 會跑的 task

lib/tasks/rubocop.rake 加上這幾行

begin
require 'rubocop/rake_task'

RuboCop::RakeTask.new

rescue LoadError # rubocop:disable Lint/HandleExceptions
end

然後修改一下 Rakefile

require_relative 'config/application'

Rails.application.load_tasks
task default: [:rubocop]

這樣當你打 rake 的時候就會自動幫你檢查語法了

要是有不懂的名詞可以查 Official document

· 3 min read

主要記錄一下兩者的差別

有興趣的話可以看一下這個 talk

簡單摘錄一下裡面的重點

像是這兩句就道出了主要的差別

Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.

Concurrency 是一種概念,意思是說你能一次做很多事 Parallelism 則是其中一種實踐這種概念的做法

這個 talk 裡舉的例子很好理解

今天有一群 gopher ( golang 的吉祥物 ),可以想像每隻 gopher 就是一個 task (這裡指的是 Linux 裡的 process 或是 thread 的通稱),它們要去燒書,書就是我們想要執行的程式。

當然我們會想要程式能夠執行得更快,所以增加了一個 gopher

但是沒有用,gopher 也需要有相對的工具 ( 推車 ) 才能夠改善效率,這裡的推車可以類比為有限的資源,像是 cpu bus

有車了!,但是必須考慮 synchronization 的問題,比如說 A 書必須比 B 書早燒,但是由於有多個 gopher 在燒書,所以有可能發生 B 書比 A 書早燒的情況

好那要是我們把書都分成 independent 的情況那這個問題是不是就解決了

那講到這裡就可以開始解釋,concurrency 和 parallelism 的差異了

Concurrency

concurrency 是一種概念,是說將一個工作分成很多個獨立不影響的片段並去執行

Parallelism

parallelism 則是說,現在我的 CPU 核心不只一顆,既然工作是獨立的,那我就可以同時去做這些工作,所以有機會發生,兩堆不同的書,在同一時間被拿去燒

· 2 min read

在 Ruby on rails 裡面

通常都是因為要做檔案權限控管

所以才會使用 send_file 這個 method

如果不需要做檔案下載的權限控管的話

直接把檔案放在 public 資料夾即可

有 access control 的檔案下載可以參考這篇 carrierwave secure upload 這樣做完後你的 model 裡就會有一個 download method

來幫你讀檔再用 send_file 來送出去

app/controllers/challenges_controller.rb

def download
send_file file_path, disposition: 'inline'
end

重點有兩個

  1. :disposition 參數用來指定是 inline 還是 attachment,default 是 attachment,所以要指定成 inline
  2. 設定 :type,設定 HTTP content type,瀏覽器知道要怎麼呈現這個檔案,就是所謂的 preview,通常這個參數可以不用設,它會自動從 :filename 裡抓取 file extension 並選擇適當的 MIME type 當作 HTTP content type

#Reference

· One min read

這篇主要是紀錄對 Carrierwave 寫 db/seeds 的方法

單一檔案

Model 是 Material 其中 attachment 欄位是紀錄檔案的欄位

app/models/material.rb

class Material < ApplicationRecord
mount_uploader :attachment, AttachmentUploader
end

通常會把測試的檔案放在 /test/fixtures/ 資料夾下

我有一個檔案路徑在

  • /test/fixtures/magic

db/seeds.rb

material = Material.new()
material.attachment = File.new(File.join("test/fixtures/files/","magic"))
material.save!

多個檔案

Database 是使用 sqlite

所以是用 string 格式來存檔案資訊

官方 issue

我有兩個檔案路徑在

  • /test/fixtures/magic
  • /test/fixtures/gdb.txt

app/models/challenge.rb

class Challenge < ApplicationRecord
mount_uploaders :attachments, AttachmentUploader
end

db/seeds.rb

challenge = Challenge.new()
attachments = [ "magic", "gdb.txt" ]
attachments.map! do | attachment |
attachment = File.new(File.join("test/fixtures/files/",attachment))
end
challenge.attachments = attachments
challenge.save!

· One min read

比如說你現在有兩個 Database Models

一個是 Cars 另一個是 Wheels

他們的關係會是 one to many

一台車會有多個輪子

每個輪子都會有一個 column: car_id 指向一台車

那比如說我今天想要得到每台車的資訊和每台車有多少個輪子

假設資料庫裡有 N 台車

先搜尋所有的車 (query count: 1)

SELECT * FROM Cars;

在對每台車搜尋所擁有的輪子 (query count: N)

SELECT * FROM Wheels WHERE car_id = ?;

總共的 Query 數是 N+1

那解決的方法就是

SELECT * FROM Cars;

SELECT * FROM Wheels;

就能把原本的 Query 數 N+1 縮減到 2

Reference