over 1 year ago

Rails 5 實作第三方登入 (Facebook + google)

雖然我去年寫過一篇如何在 Rails 4 Devise使用Google實作登入

但後來要用的時候,發現原本的文章只能對應一種第三方登入,如果要實作多個第三方登入在同一個網站的時候,可擴充性就顯得相當重要了。

再加上這種套件或是API接口的升級變化越來越快,還是寫一篇新的吧(應該不會每年都寫一篇啦XD)

Gem install

Gemfile
gem `devise`
gem 'omniauth'
gem 'omniauth-facebook'
gem 'omniauth-google-oauth2'
gem 'koala' # 可以將錯誤的 session 刪掉避免註冊失敗

因為 devise 有支援第三方登入,所以實作起來並不特別難。

申請官方接口 Facebook

先去 Facebook 開發者官方註冊,如果已有帳號直接登入應該就行了。

然後右上角,下拉選單選擇「新增應用程式」

輸入你的 app name

接著會跳轉到這個 app 下的後台設定,點擊左邊 Side bar 去建立新平台

新建網站平台

輸入網址,這邊注意一下,如果你想要先在 localhost 測試,可以先填入 http://localhost:3000 ,等到上去正式站或測試站在調整正確網址就行了,因為這個欄位是 Facebook 要確定打回去的 redirect url 是否跟設定一樣

最後 Deploy 上去要給別人測試的時候,記得到這裡把應用程式設定公開,否則只有開發者帳號能夠用 Facebook 的第三方登入哦

申請官方接口 Google

Google 開發者官方申請 API

點下拉選單來新增一個應用程式的專案

點擊「+」符號

接著會讓你輸入這個專案的名稱,輸入完之後會跳回原本的畫面,在選一次下拉選單,應該就會看到他正在建立了,等待建立好(約十秒)就可以點進去

然後啟用 Web Fonts Developer API and Google+ API

點擊新增 API 就會出現搜索欄可以找了,

找到之後點選啟用,接著會叫你建立憑證,憑證建立後就會拿到 api key 和密鑰

Controller 設定

新建 Users::OmniauthCallbacksController

指令 rails g controller users::omniauth_callbacks

代碼如下

app/controller/users/omniauth_callbacks_controller.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController

  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"], current_user)
    if @user.persisted?
      sign_in_and_redirect @user, :event => :authentication
      set_flash_message(:notice, :success, :kind => "Facebook") if is_navigational_format?
    else

      if @user.email.nil?
        @user.delete_access_token(request.env["omniauth.auth"])
        redirect_to new_user_registration_url, alert: "需要您同意 Email 授權唷!"
      else
        session["devise.facebook_data"] = request.env["omniauth.auth"]
        redirect_to new_user_registration_url
      end
    end
  end

  def google_oauth2
    @user = User.from_omniauth(request.env["omniauth.auth"], current_user)

    if @user.persisted?
      flash[:notice] = I18n.t "devise.omniauth_callbacks.success", :kind => "Google"
      sign_in_and_redirect @user, :event => :authentication
    else
      session["devise.google_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end

  def failure
    redirect_to new_user_session_path, alert: "無法獲得驗證!"
  end
end

建立一個紀錄第三方資料的 model

rails g model identity user:references provider:string uid:string

rake db:migrate

初始化 model

app/models/identity.rb
class Identity < ApplicationRecord
  belongs_to :user

  validates_presence_of :uid, :provider
  validates_uniqueness_of :uid, :scope => :provider

  def self.find_for_oauth(auth)
    find_or_create_by(uid: auth.uid, provider: auth.provider)
  end
end

驗證如果有相同 email 帳戶直接登入,沒有的話新建一個並登入,如果你有用驗證信請記得要 skip confirm

app/models/user.rb
devise: :omniauthable, :omniauth_providers => [:facebook, :google_oauth2]

  def self.from_omniauth(auth, signed_in_resource = nil)
    identity = Identity.find_for_oauth(auth)

    user = signed_in_resource ? signed_in_resource : identity.user

    if user.nil?
      email = auth.info.email

      user = User.where(email: email).first if email

      if user.nil?
        user = User.new(name: auth.info.name.gsub(/\s+/, '_'),
                        email: auth.info.email,
                        avatar: auth.info.image,
                        password: Devise.friendly_token[0,20])
        user.save!
      end
    end

    if identity.user != user
      identity.user = user
      identity.save!
    end

    user
  end

  def delete_access_token(auth)
    @graph ||= Koala::Facebook::API.new(auth.credentials.token)
    @graph.delete_connections(auth.uid, "permissions")
  end

devise 的 routes 用我們剛建立的 controller 覆蓋

config/routes.rb
  devise_for :users, :controllers => { omniauth_callbacks: "users/omniauth_callbacks" }

將你拿到的第三方金鑰與密碼填在這裡,如果不想把密鑰寫在 commit 裡面,可以使用 dotenv 這個 gem 來管理變數

app/config/initializers/devise.rb
Devise.setup do |config|
...
  config.omniauth :facebook, "KEY", "SECRET"
  config.omniauth :google, "KEY", "SECRET"
...
end

到這邊其實就好了,你會疑問,那 link button 呢?,可以到 devise 註冊頁面看一下,應該會有連結了,因為他在原先預設的頁面裡面就有提到這第三方登入,請參考

app/views/devise/shared/_links.html.erb
<%- if devise_mapping.omniauthable? %>
  <%- resource_class.omniauth_providers.each do |provider| %>
    <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %><br />
  <% end -%>
<% end -%>

如果你要任意放你的 link buuton 就用

Google user_google_oauth2_omniauth_authorize_path
Facebook user_facebook_omniauth_authorize_path

如果要擴充其他第三方,就依照此模版繼續新增就可以了

參考來源:

 
over 1 year ago

Implement background animation colors for comments(like stack over flow share answer link)

Rails version 5.1.3
Ruby version 2.3.2

原本如果實作一個 Post has_many Comments 的 CRUD

最陽春的版本就是在留言之後,跳出一個 flash 顯示

「你已留言成功」

不過因為是用 http post,所以在 controller action 就會 redirect_to 一個 path 回來,

在這裡我們可以用

  • Rails 4 redirect_to :back
  • Rails 5 redirect_back(fallback_location: root_path)

來做到留言後返回同一頁。

不過看似沒有導去任何的頁面,但是頁面卻彈到最頂部了。

因為這其實是在重新 load 當前頁面。

所以當留言越來越長,我們每次留言完就會又彈回頂部。這樣實在太糟糕了

秉持著現代流行的 SPA 精神,我們用 ajax 來完成吧

目標讓留言系統有

  • Load more 按鈕載入更多留言
  • ajax 留言
  • 留言後下拉至留言處,並對留言區塊產生顏色漸層

對於漸層從亮色到底色這種標記區塊的方法,是從 StackOverFlow 分享 answer 裡面看到的,所以就思考了一下並實作,你可以點看看這個在 stackoverflow 經典的答案分享體驗一下。

Gem install

Gemfile
gem 'jquery-rails'
gem 'will_paginate', '~> 3.1'
application.js
//= require rails-ujs

//= require jquery

Load more 按鈕載入更多留言

CRUD 這邊就略過不贅述了 XD

在 post#show 顯示留言,直接先分頁

app/controllers/posts_contoller.rb
def show
    @comments = @post.comments.paginate(page: params[:page], per_page: 5).order('created_at DESC')

    respond_to do |format|
      format.html
      format.js
    end
end

先寫 helper

app/helpers/application_helper.rb
  def render_ajax_more_comments_link(post, comments)
    link_to("SHOW MORE", post_path(post, page: comments.next_page), remote: true, class: 'btn coin-comment__load-more-btn', id: "js-load-more-comments" )
  end

再 show 頁面使用這個 load more 按鈕,並且在 form 的地方補上 remote: true

app/views/posts/show.html.erb
// 留言表單獨立出來做 partial
<%= render "comment_form"%>

// Load more 按鈕
<% if @comments.next_page %>
  <div class="coin-comment__load-more">
     <%= render_ajax_more_comments_link(@post, @comments) %>
  </div>
<% end %>

comment form 的 partial

app/views/posts/_comment_form.html.erb
<%= simple_form_for [@post, Comment.new], remote: true, html: { id: 'js-comment-form' } do |f| %>

    <%= f.input :message %>

    <%= f.submit "COMMENT", class: "btn btn-sm", data: { disable_with: "COMMENT..." } %>
<% end %>

設定 js.erb

app/views/posts/show.js.erb
$('#js-comments-list').append("<%= j render @comments %>");

<% if @comments.next_page %>
  $('#js-load-more-comments').replaceWith('<%= j render_ajax_more_comments_link(@post, @comments) %>');
<% else %>
  $(window).off('scroll');
  $('#js-load-more-comments').remove();
<% end %>

Load more button 示範

ajax 留言

controller 在 create 留言時會紀錄哪位使用者留言的

app/controllers/comments_controller.rb
  def create
    @post = Post.friendly.find(params[:post_id])
    @comment = @post.comments.build(comment_params)
    @comment.user = current_user

    respond_to do |format|
      if @comment.save
        format.html { redirect_to(post_path(@post)) }
        format.js
      else
        format.html { redirect_back(fallback_location: root_path) }
        format.js
      end
    end
  end

因為 create 行為沒有 html.erb ,不過要有 js 行為,所以

app/views/comments/create.js.erb
$('#js-comments-list').append("<%= j render @comment %>");

// clear form input 留言後能夠把 form 上面的 input 欄位清掉

$('#js-comment-form')[0].reset();

// scroll to bottom 依照當前網頁高度,去計算拉到底部的高度,這邊採用 jquery 裡面的 animate 實作

height = $('#js-comments-list').offset().top;
$('html, body').animate({scrollTop : height+ $('#js-comments-list').height()},500);

// comment moment height light 先給予顏色,之後再用 animate 將顏色動畫漸變回底色

$('#js-comments-<%= @comment.id %>').css("backgroundColor", "rgba(255, 210, 46, 0.39)");
$('#js-comments-<%= @comment.id %>').animate({backgroundColor: "#fff"}, 800);

由於原本的 jquery 是沒有加入 backgroundColor 的功能,這是額外的擴充,所以請在 layout 裡面加入

一定要在 javascript 載入之後再載入 jquery.color-animation ,因為他是依賴在 jquery 之下

app/views/layout/appliaction.rb
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<script src="//cdn.jsdelivr.net/jquery.color-animation/1/mainfile"></script>

記得做一塊 partial ,裡面是每一個 comment

app/views/comments/_comment.html.erb
<div id="js-comments-<%= comment.id %>">
  <%= comment.user.name %>
  <%= comment.message %>
</div>

這邊的 js-comments-<%= comment.id %> 用意是為了能夠辨別每一個唯一的 comment 加上的標籤,這樣一來,我們就可以做到留言後自動標記了

到這邊,應該可以實現如下圖的功能了

 
over 1 year ago

CSS開發規範

命名禁止縮寫

請精簡扼要的對 class 命名,請勿使用自定義縮寫

class name 的命名必須是行為、有語意的

禁止在非特殊情況寫 !important

CSS裡面本有權重設計,這樣一來任意的使用 !important 會造成權重混亂而無法維護,最後的情況就是互相
!important來!important去

不可輕易侷限寬高

請不要忘記用戶可以設定自己的瀏覽器,例如 andorid 的手機可以設定顯示字體大小,寫死的高度會讓字體相互重疊

RWD 失效

行動裝置(手機, ipad)的高是無限,寬是有限

請不要把寬寫死

當你在桌機板把高寫死

  • 頂多就是往下掉

當你在桌機板把寬寫死

  • 手機版就破版
  • 手機版就破版
  • 手機版就破版

img 請讓他自動縮放

請不要替 img 的容器設定寬或高,請讓他依裝置縮放

請使用 bootstrap 的 img-responsive

如果你非要設定請用

width:100%;
height: auto;

假設如果要給 img border-radius 的 style

請用父元素控制行為,保持 img 只載入圖片,不有樣式

優先使用 grid 排版

請不要花一堆時間在寫 media query,定一堆 breakpoint,自己寫元件樣式,自己控制每種裝置上的容器寬度。

請使用 grid sysytem,這些都是已經成熟的框架,而且有些也已經幫你處理了瀏覽器相容問題,請不要引入了然後都不使用,都自己寫

不可直接 over write 或加料在原本框架的 class

請直接寫一個新的 class,不要覆蓋原有的設計,舉例來說

如果你真的很討厭 bootstrap container 自帶的 padding

那你可以寫一隻

.no-padding {}

在使用的情況下

<body class="container-fluid no-padding">
</body>

可以一目了然這是沒有 padding 的 container。

也請不要在以有的 class 上加料,請額外單獨寫一個

.container-fluid {
 font-size :18px
}

手機先決 如果製作 RWD網站,設計請以手機排版優先設計

設計師,設計順序,以桌面版優先,再設計手機版
前端工程師,拿到視覺圖,欲刻 HTML / CSS 時以手機版為第一優先

手機開啟網頁很吃手機效能和網路狀況
關於前端工程師開發
一開始就以Mobile為優先,
可以讓 HTML一開始載入,在不必推移下,動用最少的效能快速載入網頁
當你開始製作 Desktop 時,只會些微跑版,畫面不易跑版到無法找到的位置。

情況反過來,先作桌面版時,當手機版畫面被切掉或是跑位
你需要花更多時間去通靈,要移動多少px才能回到畫面上?

再來是Iphone 上的 retina ,會將圖片放到手機上時,自動做兩倍縮小
在一開始製作時即可發現圖片載入是否吃效能
那也當然為了讓圖片能在iphone 上有更好的體驗,會花較多工時
所以建議Mobile first

Mobile First 優點是
在 mobile 時沒有任何 media query 載進來

當偵測到 media query 時,表示效能已經提升了(因為已經不是行動裝置了)

RWD 精隨

行動裝置(手機, ipad)的高是無限,寬是有限

請不要把寬寫死

當你在桌機板把高寫死

  • 頂多就是往下掉

當你在桌機板把寬寫死

  • 手機版就破版
  • 手機版就破版
  • 手機版就破版

不可使用 html tag selector

樣式不必要讓子子孫孫都背負,請直接定義 class 的樣式就好,不需要指定 html tag。
如果我要換 html tag, 那 CSS 不就要跟著改?

層級不可以超過三層

超過表示耦合度太高,不具有彈性、可維護性

用一樣的 Element 時不要把一堆東西全部寫在裡面,請把排版相關的獨立出來

把 border-radius 做在 img 上面,請把 img 保持乾淨,
定位,例如 postition absoulate

不要隨意 none 掉畫面上的 tag 或行為

請注意如果要 none 掉一些樣式,請依照使用程度決定

使用程度遍及整個網站,請直接做 reset.css

使用程度中等以下,請定義一個 class

例如 Rails 裡面的 simple form

<%= f.input :title %>

他會多帶一個 label tag 出來,如果你想關掉,請直接寫

<%= f.input :title, label: false %>

reset.css

常見的

  • a tag 不要有 underline
  • list 消除原有樣式

請在 reset.css 上定義,並且設為第一隻載入

(套用框架 bootstrap 可以在文件上統一 overwrite 掉)

有 JavaScript 行為的 class 可以為命名加入 name space

#js-project-show {}

請勿任意使用 br hr tag

br是換行,請使用在 p tag 裡面,當 p 裡面文字過多時可以使用。
hr 是快速劃線,但是快被時代淘汰了,請直接用 border 寫在 class 裡面

br 必須去思考父區塊是不是 display:block;,如果要換行,應該思考是不是下一段文字?

線條請都使用 border 去寫

 
over 1 year ago

Infinite Scrolling to load more records from database using Ruby on Rails

Gems installed

gem 'will_paginate', '~> 3.1'

gem 'bootstrap-sass', '~> 3.3', '>= 3.3.6'

gem 'bootstrap-will_paginate', '~> 0.0.10'

Gem removed

gem 'turbolinks'

建立 Post Controller

運用場景:

Post#index 的 view ,可以無限下拉來達到 Load more

controller 要能 respond JS

class PostsController < ApplicationController
  def index
    @posts = Post.paginate(page: params[:page],
                           per_page: 5).order('created_at DESC')

    respond_to do |format|
      format.html
      format.js
    end
  end
end
assets/javascripts/infinite_scrolling.js
$( document ).ready(function() {

  //infinite scrolling

  if ($('.loading-next-page').size() > 0) {
    $(window).on('scroll', function() {
      more_posts_url = $('.loading-next-page').attr('href');
      if (more_posts_url && ($(window).scrollTop() > $(document).height() - $(window).height() - 60)) {
        $('.loading-next-page').replaceWith('<p class="loading-next-page">Load more...</p>');
        $.getScript(more_posts_url);
      }
    });
  };
});

先做一個 Ajax helper

app/helpers/application_helper.rb
  def render_ajax_next_page_link(posts)
    link_to("next page", posts_path(page: posts.next_page), remote: true, class: 'btn btn-primary loading-next-page hidden' )
  end

class 裡面的 hidden 可加可不加,不加的話在 Load more 會有一瞬間跑出來這個按鈕就是了

app/views/posts/index.html.erb
<div id="listing-infinite-scrolling">
    <%= render @posts %>
</div>

<div>
    <%= render_ajax_next_page_link(@posts) %>
</div>

記得把 partial 補上

app/views/posts/_post.html.erb
<h2><% post.title %></h2>
<p><% post.description %></p>

這邊 tag 跟樣式可以自訂,本文章不多贅述

寫 js.erb

app/views/posts/index.js.erb
$('#listing-infinite-scrolling').append('<%= j render @posts %>');

<% if @posts.next_page %>
  $('.loading-next-page').replaceWith('<%= j render_ajax_next_page_link(@posts) %>');
<% else %>
  $(window).off('scroll');
  $('.loading-next-page').remove();
<% end %>

接下來應該可以在 post#index 頁面,做到無限捲軸下拉載入了

如果要做成用按鈕載入新的樣式,只要把 JS 行為拔掉,改用 render_ajax_next_page_link 這個 helper 出來的 button 就可以了(記得把 class 裡面的 hidden 移除掉,否則你會看不到這個按鈕)

Done.

參考來源

3 ways to load more records from database using Ruby on Rails

 
over 1 year ago

我們平時最開始學習寫 CSS 代碼的時候,我們都是這麼寫的:

.main-part .tabs {
  /*...*/
}

其實這樣的寫法是不好的,因為它過度的與頁面中的某一部分耦合了。我們在軟件工程的思想中追求的就是解耦。

BEM 風格不只一種

BEM 的命名規範不是死的,可以參考 bem-conventions

這裡有一些可供選擇的基於 BEM 命名約定的方案。

Two Dashes style(雙連字符風格)

block-name__elem-name--mod-name

  • 名字全部使用小寫。
  • BEM 實體的名稱中的每一個單詞使用一個連字符分隔。
  • 使用雙下划線(__)將元素的名稱和模塊的名稱分離開。
  • 使用雙連字符(--)分隔 Boolean 類型的修飾符。
  • 不使用 key-value 類型的修飾符
  • 重要提示!在注釋中,雙連字符被視為注釋的一部分,因此在文檔驗證時,雙破折號可能會引起錯誤。HTML5 Specification

CamelCase style(駝峰命名風格)

MyBlock__SomeElem_modName_modVla

這種風格的命名方案的不同點在於,在 BEM 實體中分隔單詞時使用駝峰命名法代替了一個連字符(-)。

"Sans underscore" style(無下划線風格)

blockName-elemName--modName--modVal

名稱使用駝峰命名法書寫。
元素名稱與模塊名稱使用一個連字符(-)分隔。
修飾符使用雙連字符(--)與模塊或元素分隔,
修飾符的名稱和值使用雙連字符(--)分隔。
重要提示!

在注釋中,雙連字符被視為注釋的一部分,因此在文檔驗證時,雙破折號可能會引起錯誤。HTML5 Specification

BEM 說明(這裡用 Two Dashes style(雙連字符風格))說明

在這邊使用 BEM 的命名規範,每行 CSS 代碼都只有一個選擇器

BEM代表 區塊(block),元素(element),修飾符(modifier)」,我們常用這三個實體開發組件。

在選擇器中,由以下三種符合來表示擴展的關係:

-   dash :僅作為連字符使用,表示某個塊或者某個子元素的多單詞之間的連接記號。
__  Two underline:雙下划線用來連接塊和塊的子元素
--  Two dash:雙減號用來描述一個塊或者塊的子元素的一種狀態

type-block__element--modifier

區塊(block)

一個區塊是設計或佈局的一部分,它有具體且唯一地意義 ,要麼是語義上的要麼是視覺上的。

在大多數情況下,任何獨立的頁面元素(或複雜或簡單)都可以被視作一個區塊。它的HTML容器會有一個唯一的CSS類名,也就是這個區塊的名字。

針對塊的CSS類名會加一些前綴( ui-),這些前綴在CSS中有類似 name space 的作用。

一個區塊的定義有下面三個基本原則:

  1. CSS中只能使用類名(不能是ID)。
  2. 每一個區塊名應該有一個命名空間(前綴)
  3. 每一條CSS規則必須屬於一個區塊。

例如:一個自定義列表 .product 是一個區塊,通常自定義列表是算在 mod 類別的,在這種情況下,一個 product 列表的block寫法應該為:

.product 

元素(element)

塊中的子元素是塊的子元素,並且子元素的子元素在 BEM 里也被認為是塊的直接子元素。一個塊中元素的類名必須用父級塊的名稱作為前綴。

如上面的例子,li.item 是列表的一個子元素,

.list{}
.list .item{}


.list{}
.list__item{}

這裡可以使用偽嵌套寫成

.list {
    &__item {}
}

修飾符(modifier)

一個「修飾符」可以理解為一個塊的特定狀態,標識著它持有一個特定的屬性。

用一個例子來解釋最好不過了。一個表示按鈕的塊默認有三個大小:小,中,大。為了避免創建三個不同的塊,最好是在塊上加修飾符。這個修飾符應該有個名字(比如:size )和值( smallnormal 或者 big )。

如上面的例子中,表示一個選中的列表,和一個激活的列表項

原本的寫法

.list {}
.list.select {}
.list .item {}
.list .item.active {}
    
用 BEM 的寫法

.list {}
.list--select {}
.list__item {}
.list__item--active {}

用偽嵌套優化之後為

.list {
  &--select {}
  &__item {
    &--active {}
  }
}

使用偽嵌套

使用 & 符號進行偽嵌套

.product {}
.product__item {}

等同於

.product {
    &__item {}
}

嵌套不得大於兩層

原則以 B + E + M 完成,不得使用 B + E + E + M,或是 B + B + E + M 之類的寫法

兩層是指從 sass compiler 後的 CSS 不得有超過兩層階層

.product {
  &__item {
    &--selected {}
  }
}

上面的 SASS 寫法這樣其實也才一層而已哦,因為 compiler 出來之後是

.product {}
.product__item {}
.product__item--selected {}

適當的使用 >

.list {
  &__item > a {}
}

這樣寫只會在 list__item 下一階層的第一個 a tag 會有效果而已,如果在這底下有兩個 a tag ,第二個會失效,這是特別要注意的點。

不要使用 Tag selector

BEM是不允許用 Tag selector 的

.menu li 能搞定的事情需要每個 li 都寫 .menu-item

優點:
就是避免 li 裡的 li 受影響
舉個例子,商品詳情頁是允許商家自定義標籤的,那麼商家展示區域標籤的祖先元素一旦用標籤選擇器定義了樣式,子子孫孫都要背負.

所以十分贊同在無法百分百確定不會嵌套同樣標籤的情況下不用 Tag selector

BEM 解決問題

組件之間的完全解耦,不會造成命名空間的污染,如: .mod-xxx ul li 的寫法帶來的潛在的嵌套風險。

性能

BEM 命名會使得 Class 類名變長,但經過 gzip 壓縮後這個帶寬開銷可以忽略不計

延伸閱讀 - 常用的CSS命名規則

  • 頭:header
  • 內容:content/container
  • 尾:footer
  • 導航:nav
  • 側欄:sidebar
  • 欄目:column
  • 頁面外圍控制整體佈局寬度:wrapper
  • 左右中:left right center
  • 登錄條:loginbar
  • 標誌:logo
  • 廣告:banner
  • 頁面主體:main
  • 熱點:hot
  • 新聞:news
  • 下載:download
  • 子導航:subnav
  • 菜單:menu
  • 子菜單:submenu
  • 搜索:search
  • 友情鏈接:friendlink
  • 頁腳:footer
  • 版權:copyright
  • 滾動:scroll
  • 內容:content
  • 標籤:tags
  • 文章列表:list
  • 提示信息:msg
  • 小技巧:tips
  • 欄目標題:title
  • 加入:joinus
  • 指南:guide
  • 服務:service
  • 註冊:regsiter
  • 狀態:status
  • 投票:vote
  • 合作夥伴:partner

注釋的寫法:

/* Header */
內容區
/* End Header */

Id的命名:

頁面結構
  • 容器: container
  • 頁頭:header
  • 內容:content/container
  • 頁面主體:main
  • 頁尾:footer
  • 導航:nav
  • 側欄:sidebar
  • 欄目:column
  • 頁面外圍控制整體佈局寬度:wrapper
  • 左右中:left right center
導航
  • 導航:nav
  • 主導航:mainnav
  • 子導航:subnav
  • 頂導航:topnav
  • 邊導航:sidebar
  • 左導航:leftsidebar
  • 右導航:rightsidebar
  • 菜單:menu
  • 子菜單:submenu
  • 標題: title
  • 摘要: summary
功能
  • 標誌:logo
  • 廣告:banner
  • 登陸:login
  • 登錄條:loginbar
  • 註冊:register
  • 搜索:search
  • 功能區:shop
  • 標題:title
  • 加入:joinus
  • 狀態:status
  • 按鈕:btn
  • 滾動:scroll
  • 標籤頁:tab
  • 文章列表:list
  • 提示信息:msg
  • 當前的: current
  • 小技巧:tips
  • 圖標: icon
  • 注釋:note
  • 指南:guild
  • 服務:service
  • 熱點:hot
  • 新聞:news
  • 下載:download
  • 投票:vote
  • 合作夥伴:partner
  • 友情鏈接:link
  • 版權:copyright

注意事項::

  1. 一律小寫;
  2. 盡量用英文;
  3. 盡量不縮寫,除非一看就明白的單詞。

CSS樣式表文件命名

  • 主要的 master.css
  • 模塊 module.css
  • 基本共用 base.css
  • 佈局、版面 layout.css
  • 主題 themes.css
  • 專欄 columns.css
  • 文字 font.css
  • 表單 forms.css
  • 補丁 mend.css
  • 打印 print.css
 
over 1 year ago

在這邊我們預計實現團隊中使用 sublime + Atom 的開發人員都能夠對 .scss 做自動縮進,並且效果必須一樣。

  • 縮進書寫規範:兩格空白

每個團隊或個人使用的 CSS 規範不盡相同,可以在細節部分自行調整,也可以參考一些CSS编码规范

有的人喜歡四格縮進,也有喜歡兩格的。

Atom

安裝 atom-beautify

command line 裡面輸入 apm install atom-beautify

安裝完畢之後,請完全關閉 Atom 後重新啟動。

接著嘗試打開 .scss 的檔案,按下 command + shift + p 並輸入 Beautify,選擇 run Beautify Editor。

你就會看到 SASS/SCSS 自動進行縮進排列。

如果要做到存檔自動 beautify ,請鍵入 cmd + ,,進入 package setting 的介面,找到 atom-beautify 點擊 settings,並往下拉找到 Sass 或是 Scss,將 Beautify On Save 打勾。

  • 將 Default Beautifier 換成 SassConvert
  • 打勾 Bautify on save (option,看個人,非必選)

那麼未來在開發 Sass 的時候,將在存檔(Command + S)的時候自動整理縮進。

Sublime

Install Package 安裝 SassBeautify

注意:正常來說不需任何設定就可以直接使用,預設自帶 4 格縮排,可以依照團隊規範調整,依此文章設定是兩格縮排,所以我們客製化內容如下

安裝開始

Preferences >> Package Settings >> SassBeautify >> Settings - User

將下面的內容複製上去

{
  // How many spaces to use for each level of indentation. "t" means use hard tabs.
  "indent": 2,
  // Convert underscores to dashes.
  "dasherize": false,
  // Output the old-style ":prop val" property syntax. Only meaningful when generating Sass.
  "old": false,
  // Custom environment PATH.
  "path": fasle,
  // Custom environment GEM_PATH.
  "gemPath": false,
  // Beautify the sass file after saving it?
  "beautifyOnSave": false,
  // Keep "inline" comments inline?
  "inlineComments": false,
  // Add a new line between selectors?
  "newlineBetweenSelectors": false,
  // Use single quotes everywhere
  "useSingleQuotes": false
}

這邊可以直接打開 .scss 檔案,用 command + shift + p 來搜尋 SassBeautify,按下 enter,就會自動縮排了

如果有跳 error ,應該是你的相依檔案路徑他找不到,可以依照官方的解法進行排除

Compatibility with RVM/rbenv

You need to specify the custom PATH and GEM_PATHvalues in your SassBeautify user settings.

Follow the steps below:

  1. Open up terminal
  2. Run: echo $PATH
  3. Copy the entire PATH into the 'path' setting
  4. Run: echo $GEM_PATH
  5. Copy the entire GEM_PATH into the 'gemPath' setting

Done.

後記心得

Atom 編輯器在使用 atom-beautify 插件後,可以選擇處理 sass 的 beautifier,預設是使用 pretty diff ,而且最坑的是下方的客製化選項 只支援pretty diff,簡單說,如果你想用 SassConvert,那你就無法調整預設 indent 為兩格的設置。

因為選項竟然都只支援 pretty diff...

偏偏 pretty diffSassConvert 整理出來的 SASS 又有些微不同

  • pretty diff 的註解與樣式中間不會有空一行
  • SassConvert 的註解與樣式中間會有空一行

所以如果團隊中用兩個不同的編輯器,只要 beautifier ,設定的不一樣,你就會看到 git commit 永遠一直在變動一大堆結構,這會使 PR 更難閱讀。

所以本文章實現了兩個不同編輯器卻能有同樣的自動化整理風格是退一步求其次,將原先的 SassConvert 自帶的四格縮進改為兩格,與 Atom 的 beautify 一樣,就可以排除這奇怪的問題了。

 
almost 2 years ago

依照前一篇,在 在 Google platform 上架設 ShadowSocks(SS) + BBR,打造一台有 SS + BBR 提速的機器之後,接下來就是大家最關心的如何在 iphone 上面翻牆了。

不過基本上走到這一步都得花錢,花多花少而已,如果想要完全免費的話這篇文章可能幫不上你Q_Q

Surge(USD39.99 昂貴付費,但首推)

基本上這 APP 比較偏向開發者使用,過程會較繁瑣一些,操作也不是那麼平易近人,更令人不敢接近的一點就是付費昂貴,約 1500 NTD,不過這軟體對我來說,「付費就是撿便宜」,省了我很多麻煩,只要有連上網路的地方,都可以自動翻牆,並且智能的選擇路線,如果瀏覽大陸地區的資源,就不翻牆,眾多智能的功能,大概很符合我這種常常要用到網路的工程師了XD

還有,他不像以往的 VPN 什麼需要開開關關的,基本上我很少在開關他,通常都是我不想用,或者是手機重開機的時候吧!

先下載手機版

Surge Apple Store 載點

請先在電腦端下載 Nic 調整過的 SS file

這隻檔案可以正常使用 Line XDD

下載回來之後,請用編輯器打開他

編輯 [Proxy] 下方的 ##IP位置##, ##password## 請把他取代掉成你自己機器的
IP與密碼,至於 port 號和 加密方式我已經替你填寫好了

填好之後,請放入自己的 Apple iClound 裡面

接著打開手機的 Surge

依照下圖載入剛剛編輯過的檔案(如果找不到檔案,應該是你設備在同步中,請稍候幾分鐘)

P.S 如果你打開是英文版的,可以透過設定調整,這部份就不在贅述

點擊左上角的漢堡

從其他程序導入

點擊剛剛載入的檔案

基本上就完成了,可以回 Surger 首頁將通道開啟(最下排最左邊的開關 Tab)

順利翻牆 / 科學上網吧!

Shadowrocket(第二方案,約台幣九十元)

Shadowrocket Apple Store 載點

點開Add Configuration

依照上一篇架設的 SS server 的詳細資訊填入(在上一篇文章文末有教你怎麼看機器資訊)

第一次使用會有權限警示,點擇Allow即可。

只要之前設定的端口、密碼、加密方式等內容都正確的話,你就可以翻牆上網了。

電腦版呢?

這又是另一個故事了,電腦版也有 Surge ,不過又是另一個費用了,對你沒看錯,手機跟電腦版的付費竟然是分開的!!,不過我還是都買了,基本上會手機版的,電腦版的也差不多了XDD,這邊就不繼續教了

參考資源

 
almost 2 years ago

建立 VPS

Google cloud platform 有免費 12 個月 300USD 的額度可以使用

  • 註冊 Google Clund Platform 服務
  • 點擊左上角三條線漢堡,下拉選單到 「Compute Engine」 -> 「VM 執行個體」

  • 點擊 「建立執行個體」

名稱:自定義,這是你的機器名稱
區域:請選 asia-east1-c 或是 asia-northeast1-b (亞洲區)
機器類型:由於做個人翻牆工具,可以只選「微型」就好,規格選越高錢扣越快
開機磁碟:請選作業系統 Ubuntu 14.04 LTS
防火牆:將「允許 HTTP 流量」和「允許 HTTPS 流量」皆設為開啟

建立後,用瀏覽器打開 SSH 並輸入以下指令

每筆指令輸入後記得按 Enter

先安裝 BBR 加速

BBR 是 Google 官方開源的擁塞算法來加速 TCP

也因為裝這個要重開機,所以先裝XD

依序輸入

wget –no-check-certificate https://github.com/teddysun/across/raw/master/bbr.sh

chmod +x bbr.sh

sudo ./bbr.sh

輸入任意鍵執行

過程會需要一些時間約三分鐘,最後他會問你要不要重啟機器(Do you want to reboot?)輸入 y 同意

之後會斷開連線,請在重複之前打開這個視窗的動作「SSH」-> 「在瀏覽器視窗中開啟」

輸入 sysctl net.ipv4.tcp_available_congestion_control

如果有出現 bbr 字眼,就是安裝成功了

安裝 ShadowSocks(SS)

sudo apt-get install python-pip -y

sudo pip install shadowsocks

重要!!! 以下指令中的 password 請務必改成自己要使用的

sudo ssserver -p 443 -k password -m aes-256-cfb --user nobody -d start

將服務停止

sudo ssserver -d stop

再次運行

sudo ssserver -p 443 -k password -m aes-256-cfb --user nobody -d start

大功告成,你已經有一台 SS + BBR 來做翻牆的機器了!

機器詳細資訊

  • IP 位置是你架設機器在上面寫的「外部 IP」
  • 密碼是剛剛你自行替換的「password」
  • 加密規則是 aes-256-cfb
  • port 通道 443

翻牆工具

我這邊是用 Surge ,不管手機跟電腦都是,要付費就是了,聽說也有免費的可以用,這部份就在另一篇教程: 在 iphone 上透過 Shadowsocks(SS) 在大陸翻牆講吧!

結果

這是用 surge 跑 benchmark 出來的結果,圖中的 GoogleTest 是邊敲本文章邊隨著架設的機器,可以看出速度還OK

Youtube 1080P Test

參考資源

 
almost 2 years ago

最近常常在專案裡面看到令人很頭痛的 CSS,沒有設計模式,有大量不明白的階層跟命名,完全無法複用的元素XDD,心中是滿滿的 fuck。

於是我就開始對 Sass 產生好奇了,也從 BEM 的開發模式思考了一些命名和規則

BEM由Yandex團隊提出來的一種創新命名Class名稱的設計模式,BEM的意思是區塊(Block)、元素(Element)、修飾符(Modifier)

其中最好玩的就是,我們可以從瀏覽 class 名稱就能知道這些元素彼此之間的關係

讓我們來讀一下一般最常見到的結構吧

<div class="product">
  <ul class="menu">
      <li class="item active"><a href="#"></a></li>
      <li class="item"><a href="#"></a></li>
  </ul>
</div>

然後就會開始思考一些問題了

  • product 與 menu 的關聯? .product .menu
  • active 是否只侷限於 list 的範圍域?
  • menu 是可以獨立拆分出來的 Modular 嗎?還是其實是綁在 product 下面?

在使用 BEM 的命名原則之後

<div class="product">
    <div class="menu">
        <li class="menu__item menu__item--active"><a href="#"></a></li>
        <li class="menu__item"><a href="#"></a></li>
    </div>
</div>

別急,第一句你心理想的一定是一些髒話,這什麼鬼東西,這麼長?

我們了解了 BEM 的設計原則之後,你會發現這些東西一目了然,而且更具維護性與邏輯性,就像在 CSS style 裡面寫了 logic

.block{} //區塊 (Block)

.block__element{} //元素 (Element)

.block--modifier{}//修飾符(Modifier

區塊

一般網頁設計師在設計網站的時候,會把比較大的部分切出來,是一整個區塊,比方說

  • 搜尋欄的 search block
  • 用戶登入的 auth block
  • 網頁 logo 的 logo block
  • 導航欄的 menu block

然後包住以上所有的 block 的大區塊, head block

元素

在區塊下的元素,如果說這些 element 是綁定在這個 block 下方,在這個設計原則規範裡面就會用雙下底線來標記他是元素,例如 menu__item

修飾符

當元素或是區塊的狀態改變了,比方說我們最常見的 active ,在設計原則裡我們會用 double dash 來連接,例如 menu__item--active

所以當一個 menu item 發生狀態變化後,他的 class 會是這樣的

<div class="menu__item menu__item--active">

這樣一來雖然看起來很冗長(但又不是你在看XD,是瀏覽器啊啊),不過卻可以清楚知道這個 active 是修飾符,同時依賴在 menu__item 這個元素下層

如此一來我們只要透過 double 底線,或是 double dash 就可以知道這個 class 裡面擁有什麼行為或階層上的邏輯

然而修飾符能夠使用的時機並不少,除了狀態更動以外,也可以用在樣式設計上

比方說原本滿版的 post 在這個頁面變成 1/3 就可以這樣寫 post post--1of3

看看範例

<form class="site-search  site-search--full">
    <input type="text" class="site-search__field">
    <input type="Submit" class="site-search__button" value ="Search" >
</form>

耶,知道 BEM 設計原則後,我能透過這個例子知道

  • 一個區塊 site-search
  • 兩個元素 site-search__fieldsite-search__button
  • 一個修飾符 site-search--full

Jesus Fuck !! 怎麼這麼明瞭

那麼再來一個例子

<div class="media">
    <img src="logo.png" alt="Foo Corp logo" class="img-rev">
    <div class="body">
        <h3 class="alpha">Welcome to Foo Corp</h3>
        <p class="lede">Foo Corp is the best, seriously!</p>
    </div>
</div>

嗯我不知道誰與誰有關聯,可能要去讀 source code,也不知道什麼可以複用

然我將同樣的 code 套上了 BEM 設計原則就會如下

<div class="media">
    <img src="logo.png" alt="Foo Corp logo" class="media__img--rev">
    <div class="media__body">
        <h3 class="alpha">Welcome to Foo Corp</h3>
        <p class="lede">Foo Corp is the best, seriously!</p>
    </div>
</div>

Jesus Fuck again !!!

我馬上又清楚了

  • 有一個 .media 區塊
  • 有一個元素 media__body
  • 有修飾符 media__img--rev
  • h3, p 裡面的 class 是獨立可以複用的

什麼時機不會需要用到BEM

  • 可以獨立且具高複用性

例如常見的

  • 置左 .pull-left
  • 置右 .pull-light
  • 清除浮動 .clearfix
  • 文字置中 text-center

因為使用率很高,而且可以用在不同的 tag 上,所以會建議把他獨立成一個 class

SASS 3.3 支援 BEM 設計模式

嗯...最令人頭疼的可能就是在 naming 部分,我必須先想到第一個 class name 在「區塊」上才有辦法繼續寫下去我的「元素」「修飾符」

而且在以往的設計模式會打非常多的字

.media{}

.media--full{}

.media__submit{}

.media__img{}

.media__img--border{}

但 sass 3.3 之後支援使用 &字符連接後,完美的提升 BEM 的實用性啊,針對以上的例子你只需要寫成

.media{

  &--full {}

  &__submit {}

  &__img {

    &--border {}

  }

}

如此一來,就不用打一堆字卻又能夠完美實現 BEM 設計原則了!!

在這裡我曾經好奇過 & 連接符,我原本以為

.father {

}

.son {

}

.father {

  &.son {

  }

}

會是一樣的結果,不過如果要做出更好維護且不打架的命名,用連接字符的差別很明顯就能夠顯現,我們舉個例子

如果你的 son 要能夠搭配不同的 father ,比方說 father-US(爸爸是美國人), father-TW(爸爸是台灣人)

那就可以先把 son 與 father 先模組化起來,然後用 BEM 的設計原則來達到客製化各種國家的爸爸與兒子產生出不撞名又好懂得 naming(也不用額外想一些奇怪的命名)

.us-father {

    .us-father__son {  # 這裡是為了易讀,其實可以直接寫 &__son

      這爸爸底下兒子會有的屬性

    }

}



.son {

  基本做兒子該有的屬性

}

也因為 son 模組化了,所以我們可以寫出這樣的 html code

<div class="us-father">
    <div class="son us-father__son"></div>
</div>

這樣的架構打好了,就有最基本的 son 可以使用,也可以再去利用各別客製化不同國家的 son ,也沒有所謂奇怪命名來達到世界和平,雖然會讓 class 命名更長,不過也將 _- 發揮到極致,變成有高度可讀性及維護性的標誌

針對 & 連接字符有更詳細的說明請至 痞客邦 Jimmy 的初探Sass,秒懂文章裡學習哦

小結

BEM 這個設計原則並沒有一定要使用,不過裡面有很多可以值得學習的地方,這些命名原則都是能夠讓團隊有更好的協作默契,不會有一堆狗屎要整理,或是根本整理不動也不敢刪,變成你要去讀懂他的個人 style ,所以可以嘗試精讀這樣的內容,來規劃出能夠複用也能讓同事一目了然的 CSS 架構唄

參考來源

 
almost 2 years ago

常見的情境有,搜索用戶資料、搜尋文章等等

而在 Rails 裡面有一個做基礎搜尋功能的 gem 他叫 Ransack,之所以會說只能做基本搜尋是因為,他是在下搜尋關鍵字的時候,從 database 裡面做模糊搜索,使用 like 去做 query

例如我們要搜尋 User 的 first name,名叫 Nic 的資料

在 ransack 裡面的寫法是

User.ransack(first_name_cont: 'Nic')

而他轉成 SQL 就會變成

SELECT "users".* FROM "users" WHERE ("users"."first_name" LIKE '%Nic%')

除了「模糊搜索」以外,需要更精準的搜索就需要用到「全文搜索」了

這部份就可以參考全文搜索的引擎(需要額外架設)

  • solr
  • elasticsearch

不過這篇就只說使用 ransack 在 Rails App 裡面實作搜尋吧

設計目標

  • 單獨的 Search Controller
  • 漂亮的路徑 example.com/search?#{search_key_word}

安裝 ransack

Gemfile
gem 'ransack'

設定 controller 與 routes

創建 search controller

rails g controller search

routes.rb
get "/search" => "search#index", :as => "search"

接下來我們要處理透過 form 送進來的 params,濾掉一些不必要的字符,這部份可以在 controller 裡面做 validation

def validate_search_key
  @query_string = params[:q].gsub(/\\|\'|\/|\?/, "") if params[:q].present?

  @search_criteria = search_criteria(@query_string)
end

然後告訴 controller 我們要搜尋的範圍

def search_criteria(query_string)
  { :title_or_description_cont => query_string } 
end

title_or_description_cont 是ransack提供的方法 意思是說,搜尋這個 model 裡面的 title 或是 description 欄位

接下來我們完成 index action

def index
  if @query_string.present?
    @search = Topic.search(@search_criteria).result(:distinct => true)  
  end
end

接下來只要將其搜尋結果,用 view 呈現出來,搜尋功能就完成了。

在這邊我們看到的 web url 會是

http://example.com/search?utf8=✓&q=test

其中的 q=test 就是我們從 form 送進去的搜尋內容

utf8=✓ 則是為了符合古老的瀏覽器 ie6, Rails 在 form 裡面幫我們自帶的 attribute

附上最後拼湊完成的 Search Controller

search_controller.rb
class SearchController < ApplicationController
  before_action :validate_search_key

  def index
    if @query_string.present?
      @search = Topic.search(@search_criteria).result(:distinct => true)  
    end
  end
  
  protected
  
  def validate_search_key
    @query_string = params[:q].gsub(/\\|\'|\/|\?/, "") if params[:q].present?
    @search_criteria = search_criteria(@query_string)
  end

  def search_criteria(query_string)
    { :title_or_content_cont => query_string }
  end

end