almost 7 years ago

github_hexo.png

搬家了!

  從即日起,本站將不再作筆記的更新喔!

  雖然一直以來都是隨手記記學習上的小筆記,但這次搬家後,除了原本的攻城獅日誌外,會開始寫一些旅遊、生活和資訊的分享!畢竟除了程式語言外,本人也非常喜歡人類語言,哈哈哈!

  新網站是以 HEXO 搭配 Github Page 的部落格,我會在新網站補上如何實作,歡迎來街口駐足!

嘿!我在這呢!

新網站「新樂街口的三角窗」: https://oawan.me




p.s. 這邊(舊站)資訊將會保留,不會移除。

 
almost 7 years ago

pic by Facebookcoke

目的

  在 Devise 下,使用 Facebook 註冊與登入。

前言

  相信大家都很熟練 Devise 這個 Gem 的使用者機制,當然除了一般的註冊申請帳號外,最方便是要能與各大社交平台有所連結;而 Devise 中透過 OmniAuth 快速地串接 Facebook、google、github... 等,以下就筆記一下如何使用 Facebook 註冊登入!

開發版本與環境

  Ruby  : v2.3.0
  Rails : v4.2.5
  IDE   : Cloud 9

筆記

1. 什麼是 OmniAuth ?

  OmniAuth 是一個提供多方認證,且不被限定於特定框架的認證方案,主要架構如下:

Provider

  OmniAuth 將所有的認證方視為不同的 Provider,而每種 Provider 都有各自的 Strategy。

Strategy

  每個 Strategy 分為兩個 phase,對應 OmniAuth 提供的 url:

  • equest phase => /auth/:provider
  • callback phase => /auth/:provider/callback

  而它的流程是,透過 /auth/:provider 重導去認證,認證成功後 redirect 到 /auth/:provider/callback 作 session create 動作 ( 透過拿回來的資料 find_or_create user )

註: :provider 就是 facebook、github,所以 url 看起來會是 /auth/facebook、/auth/facebook/callback。

2. 一起來實作

步驟零、 Install gem
Gemfile
gem 'omniauth'
gem 'omniauth-facebook'
gem 'koala', "~-> 2.2"  # 為了讓授權失敗時,刪除 permission session 使用
步驟一、 建立 Facebook Application

  使用 Facebook 應用服務前,需先到 Facebook developer 建立應用程式,只要按照步驟就可以順利建立完成。

步驟二、 於 devise.rb 中設定 config.omniauth
devise.rb
config.omniauth :facebook, ENB['FB_KEY'], ENV['FB_SECRET'],
                scope: 'email',
                info_fields: 'email, name, link',
                secure_image_url: true,
                image_size:  'large'

這邊使用 Rails_ENV 確保我們的資料隱密性,可參考 這篇的 Rails_ENV 小筆記

  • FB_KEY:Facebook 應用程式編號
  • FB_SECRET:Facebook 應用程式密鑰
  • scope:表示將取得使用者的什麼權限,email 為預設,即取得使用者公開的資訊 ( public_profile ) 權限
  • info_fields:表示在擁有權限下,取得使用者的什麼資訊,這邊我們取得使用者的 Email、名稱和 FB 連結
  • secure_image_url:為 true,表示我們透過 https 取得使用者大頭貼
  • image_size:此為取得大頭貼的 size,square(50x50)、small(50 px width, ariable height)、normal(100 px width, variable height)、large(約 200 px wide, variable height)

以上設定,我們可以從 omniauth-facebook 清楚的了解。
若是想取得其他的權限與資訊,可以先到 Facebook developer permissions 查看。

步驟三、 於 user.rb 中設定 omniauth
user.rb
devise :omniauthable, :omniauth_providers => [:facebook]

  定義認證後的創建行為,會在取得使用者 Facebook 權限與資訊後執行:

user.rb
def self.from_omniauth(auth)
  where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
    user.provider = auth.provider
    user.uid      = auth.uid
    user.email    = auth.info.email
    user.name     = auth.info.name
    user.facebook = auth.info.urls.Facebook
    user.password = Devise.friendly_token[0,20]
    user.remote_avatar_url   = auth.info.image
    user.skip_confirmation!  # 如果 devise 有使用 confirmable,記得 skip!

  end
end

  授權失敗時,透過 koala 刪除使用者授權的 session,以便重新授權。

user.rb
def delete_access_token(auth)
  @graph ||= Koala::Facebook::API.new(auth.credentials.token)
  @graph.delete_connections(auth.uid, "permissions")
end
步驟四、 定義 omniauth callback controller

  新增一個 Users::OmniauthCallBacksController,我們在這個地方定義兩個 action:facebook 和 failure。

  • facebook method 作的三件事:

   1. 使用者授權後,依取得的資訊創建新的 user。
   2. 如果此使用者已經存在網站中,直接登入。
   3. 授權後,未取得 email 者,重新要求授權。(這是因為我的網站使用者 email 為必填項目,且為登入憑據)

  • failure:重新導向註冊頁,並告知使用者授權錯誤。
omniauth_callback.rb
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def facebook
    @user = User.from_omniauth(request.env["omniauth.auth"])  # 這邊會呼叫我們在 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 沒有提供 email 刪除 User 的 app 讓 User 重新授權

        @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 failure
    redirect_to new_user_session_path, alert: "無法獲得驗證!"
  end
end
步驟五、 到 routes.rb 設定 devise omniauth callback 該用哪個 controller

  我們要讓 omniauth callback 時使用我們定義的 controller:

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

Devise 使用 OmniAuth 時,會自動幫你在 registrations#new 產生 provider 登入的 link,所以不必自行添加,或是你也可以在想添加的地方加入:
  <%= link_to "Sign in with Facebook", user_omniauth_authorize_path(:facebook) %>

後記

  授權失敗時,需要重新授權 (Permissions revoke),這個問題看似很簡單,卻花了我不少時間;原因是不想為了解決這個小問題,而多使用一個 gem,但最終我還是選擇用 koala 解決了,如果有不多加使用 gem 的解法請來信或留言,感激不盡!

That's it, DONE!

【參考資料】

 
almost 7 years ago

picture by rvm official

目的

  有鑒於一些好朋友想知道怎麼管理 ROR 的開發環境,所以重新整理了一下在 Ubuntu 上使用 RVM 管理 Ruby 和 Rails 的小筆記!希望能幫助到需要的你唷!

開發版本與環境

  VMware Workstation 12
  Ubuntu: 15.04(64-bit)

筆記

1. 什麼是 Gem ?

  不是鄧紫棋!不是鄧紫棋!不是鄧紫棋!

  不知道大家是不是和我一樣,在什麼都還不了解的情況下,迫切的想知道 Gem 在 ROR 世界裡究竟扮演著什麼角色,一估狗 Gem,卻跑出一堆鄧紫棋XDD

  在 Ruby 的世界裡,Gem 如其名,就是一顆一顆的寶石,而 Ruby 會由很多的 Gem 點綴成你要的樣子,好比如果你想要你的 Ruby 有黃色光芒,你就得裝上會散發黃色光芒的 Gem;我把 Gem 看作是 Ruby 的套件,就像是 npm 中的 module,需要什麼樣的功能,我們就掛上什麼樣的套件。

2. 什麼是 RVM ?

  Ruby Version Manager ( RVM ),顧名思義就是一個管理 Ruby 版本的工具,這是一個 command-line 的工具,也就是說只要下下指令,就能簡單的切換和管理 Ruby 版本;而每個 Ruby 版本都會有自己的 Gemset (就是一堆的 Gem),所以透過 RVM 也可以管理每個 Ruby 版本下,該用哪一套 Gemset ( 由於 Rails 就是一個 Gem,所以我們可以由安裝的 Rails 版本來分類 Gemset)。

3. 安裝 RVM

步驟一、 在安裝 RVM 之前,你應該先裝好 curl。
  sudo apt-get update
  sudo apt-get install -y curl build-essential
步驟二、 參考 RVM 官方網站,下這兩道神奇咒語,就安裝好了!
  gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
  curl -sSL https://get.rvm.io | bash -s stable

  安裝完後記得重開或 reload Terminal。

4. 安裝 Ruby 及 RVM 指令

  透過 RVM 安裝 Ruby 很簡單:

  rvm install 2.3.0        # 安裝 Ruby 2.3.0 ( 版本號可自選 )

  下面筆記一些 RVM 的指令:

  rvm system               # 切回系統安裝的 Ruby ( 不建議使用 )
  rvm list              # 列出已經安裝的 Ruby 版本 (* 預設,=> 正在使用,=* 正在使用且為預設,)
  rvm list known           # 列出 RVM 目前所有可安裝的 Ruby 版本
  rvm use 2.3.0            # 切換至輸入的版本
  rvm use 2.3.0 --default  # 切換至輸入的版本,並設為預設
  rvm reinstall 2.3.0      # 重新安裝該版本
  rvm info                 # 目前使用的 Ruby 相關資訊

  由於,每個 Ruby 版本下,都各自有不同的 Gem,這時我們就可以透過 RVM 的 Gemset 來作管理和切換:

  rvm gemset               # 查看指令說明
  rvm gemset list          # 列出目前使用 Ruby 的 Gemset
  rvm gemset list_all      # 列出所有安裝 Ruby 的 Gemset
  rvm gemset create xxx    # 建立名為 xxx 的 gemset
  rvm gemset use xxx       # 使用 gemset xxx
  rvm gemset empty xxx     # 清空 gemset xxx
  rvm gemset delete xxx    # 刪除 gemset xxx
  rvm gemset copy xxx yyy  # 將 xxx 的內容 copy 到 yyy (兩者需都要存在)
  rvm gemset rename xxx zzz# 將 xxx 更名為 zzz

5. 安裝 Rails

  只要選定好要使用的 Gemset 後,下 gem install rails 安裝 rails 即可。

  這邊稍微提一下,我們可以也利用安裝的 Rails 版本來區分 Gemset,例如:

  rvm gemset create rails_4.2.6  # 建立一個 rails_4.2.6 Gemset
  rvm gemset use rails_4.2.6     # 使用 rails_4.2.6
  gem install rails 4.2.6        # 在此 Gemset 下安裝 Rails 4.2.6

  這樣我們就很清楚的知道,這個 Gemset 的 Rails 開發環境是版本 4.2.6。

註:其實 Ruby 版控上,除了 RVM 外,也很多人使用 rbenv,教學請參考 Ruby China 這篇,由於沒用過,就不多提了!
  而我會選擇 RVM,除了簡單易學易用外,還有一點就是,目前我在使用的 Cloud IDE Cloud 9 上預設的就是 RVM,有興趣也可以使用 Cloud 9 作開發喔!

That's it, DONE!

【參考資料】

 
almost 7 years ago

pic by foundation.zurb.com

目的

  在很多情況下都需要透過 E-mail 的功能,而我目前是需要寄送註冊認證信給使用者,我們使用 ActionMailer 來整合 E-mail!

開發版本與環境

    Ruby :v2.3.0
    Rails:v4.2.5
    IDE  :Cloud 9

筆記

1. Devise 設定

  這邊我們只講寄信時,devise 需注意的設定,最簡單的方式,就是開啟 Devise::Mailer

devise.rb
config.mailer_sender = 'yourname <whateveryouwant@domain.com>'
 # 設定 mailer_sender 會顯示在寄件人 (任何格式都可,但這種格式較不會被當作垃圾信件處理)

config.mailer = 'Devise::Mailer'
 # 將註解拿掉後,就會由 devise 預設的 Mailer 寄信 (從 Devise::Mailer 作 deliver 調用 ActionMailer)

2. ActionMailer 設定

development.rb
config.action_mailer.raise_delivery_errors = false
 # rails_delivery_errors 設定為 false 可讓寄信時的錯誤被忽略,如果要 debug 就設 true


config.action_mailer.default_url_options = { host: 'http://localhost:3000' }
 # 這邊填入的網址須要注意一下,他必須是絕對網址,且會被預設為 mail 中的 resource link

 # 像我是用 devise 寄發驗證信,所以 confirmation_url(@resource, confirmation_token: @token)

 # 中的 @resource 就會是 http://localhost:3000


config.action_mailer.delivery_method = :smtp
 # delivery_method 有三種寄信方式 :test、:sendmail 和 :smtp

 # sendmail 須搭配 server 的 /user/bin/sendmail application

 # 而這邊我是透過 mailgun(and gmail) 的 smtp 協定作寄信


config.action_mailer.smtp_settings = {
  :address => "smtp.gmail.com",
   :port => "587",
    :domain => "yourdomain.com",
    :authentication => "plain",
    :user_name => "yourgmail@gmail.com",
    :password => "yourpassword",
    :enable_starttls_auto => true
}

  其實一般來說,我們不會將 smtp_settings 寫在程式碼裡,而是拆成另一個設定檔,便於管理在不同狀態下的設定:

config/email.yml
development:

  address: "smtp.gmail.com"

  port: "587"

  domain: <%=ENV['DOMAIN']%>

  authentication: "plain"

  user_name: <%=ENV['GMAIL_USERNAME']%>

  password: <%=ENV['GMAIL_PASSWORD']%>

  enable_starttls_auto: true

production:

  address: "smtp.mailgun.org"

  port: "587"

  domain: <%=ENV['DOMAIN']%>

  authentication: "plain"

  user_name: <%=ENV['MAILGUN_USERNAME']%>

  password: <%=ENV['MAILGUN_PASSWORD']%>

  enable_starttls_auto: true

註:cloud 9 中,多數 port 預設是 blocked,但依我自己的測試, mailgun 可透過 2525、587 來寄出信件,而 gmail 只能由 587 寄信。

  smtp_settings 的部分就可以修改成:

config.action_mailer.smtp_settings = config_for(:email).symbolize_keys
 # config_for 會讀取 config 目錄下的 YAML 設定檔,由於 smtp_settings 需帶入 Symbol key

 # 所以透過 .symbolize_keys 將 Hash 中的 String key 轉成 Symbol key

3. 關於 email.yml

  這邊來聊聊上面的 email.yml。

  ROR 環境預設有三個,developmentproductiontest,一般 rails server 開啟環境預設是 development,我們可以透過 -e 來變更啟動環境,例如 rails s -e test

  範例 email.yml 中,則表示:
    development 時,使用 gmail 寄信。
    production 時,使用 mailgun 寄信。

  在帳號(user_name)及密碼(password)這邊需要注意的是,如果你是將密碼寫死在設定中,記得一定要將 email.yml 加到 .gitignore 中,避免上傳到 github 自曝帳密;比較好的方法是使用 RAILS_ENV 來取得環境設的變數,由於是寫在 yaml 中,所以我們寫成 <%=ENV['GMAIL_USERNAME']%>,如果是寫在 .rb 中,則使用 ENV['GMAIL_USERNAME'] 即可。這樣我們就可以透過環境設的值作設定。

4. 設定 RAILS_ENV 變數

  • 在 Ubuntu 下,可以編輯 ~/.bashrc 並加入以下作環境變數的設定 (在 Mac 下,則編輯 ~/.profile):

      export GMAIL_USERNAME="yourgmail@gmail.com"
    

      以此類推,自行添加。

  • Heroku 的話,可直接在 Settings > Config Variables 中添加,或用以下指令作設定:

      heroku config:add GMAIL_USERNAME="yourgmail@gmail.com"
    
  • Cloud 9 的話,在 Runner 中有 ENV 可直接添加,或用 Ubuntu 的方式添加亦可 (因為 c9 的 ROR 環境是 Ubuntu )

5. Gmail 密碼問題

  其實上述四個步驟後,就可以成功的寄出信件,但還是補充一下關於 gmail 有可能寄不出信的設定問題。
  請先到 gmail 中作安全性設定,並取得密碼: 設定安全性二階段驗證應用程式密碼
  而取得的應用程式密碼就是要填入的密碼囉!

後記

  一開始我是想用 sendgrid 來作 smtp 送信,但真的試不出來也不知道什麼問題,有興趣的人可看這篇文章,雖然是阿本仔寫的,但也是很清楚明瞭!

That's it, DONE!

【參考資料】

 
about 7 years ago

pic by thenextweb.com

目的

  在之前我有筆記一篇透過 friendly_id 讓 url 不顯示 id,除了美化 url 外,我們也可以透過設定 meta data 讓網站達到 SEO 的目的!

前言

  meta tag 及 data 的設定很重要,通常網站要能快速得被找到,或是透過分享時顯示重要(正確)的資訊,我們都得在這個部分下點功夫,本篇筆記一下在專案中所學到的相關知識和方法。

筆記

1. 什麼是 Meta Tags 及 Meta Data

  Meta Data,在全球資訊網上,是指讓機器看得懂的、描述資訊內容的所有相關資訊。

  • Title Tag:該頁標題,英文長度最好是 70 字元內,中文大約 40 字內,關鍵字盡量往前放,而每一頁的 Title Tag 不要重覆為佳。如:XXX美食部落格 :: 痞○幫XXX | Facebook徵傳單小弟 | oPartTime 讓打工變得更美好!等。
  • Description Tag:該頁的敘述,包含標點符號英文約 150 字內,中文 12 字內為佳,此 Description Tag 會出現在 Google 搜尋結果中,所以關鍵字的使用很重要,盡量往前擺。
  • Canonical Url:告訴搜尋引擎關於這個頁面的標準網址。
  • Facebook OG此標籤中的資訊是專門給 Facebook 的,可透過設定 OG (Open Graph) 的內容敘述,調整分享時的縮圖、標題、敘述等。
  • Twitter Cards此標籤中的資訊則是專門給 Twitter 的,透過設定 Cards 的內容敘述,調整分享的資訊顯示。

2. Gem

  在 rails 中,除了直接 code html 外,我們可以透過 meta-tags 這個 gem 來對 Meta Data 作設定。

Gemfile
gem 'meta-tags'

then, bundle it.

3. 設定 Meta Data

  簡單的設定來說,可以透過 set_meta_tags 作設定,如下:

page.html.haml
= set_meta_tags title: '此頁的抬頭'
-# <title>此頁的抬頭</title>
= set_meta_tags site: 'GeorgioWan's Note', title: '此頁的抬頭'
-# <title>GeorgioWan's Note | 此頁的抬頭</title>
= set_meta_tags site: 'GeorgioWan's Note', title: '此頁的抬頭', reverse: true
-# <title>此頁的抬頭 | GeorgioWan's Note</title> <= 較佳的作法

= set_meta_tags description: '此頁的敘述喔!'
-# <meta name="description" content="此頁的敘述喔!" />
-# 而這句"此頁的敘述喔!"會在 Google 搜尋找到網頁時,顯時於搜尋結果中。
-# 如下,搜尋 Facebook 時所出現的資訊範例。

  在 Facebook OG 的簡單設定如下:

Facebook Open Graph
= set_meta_tags og: {
    title: '此頁的抬頭',
    site_name: 'GeorgioWan's Note'
    type: 'website',
    url: 'http://georgiowan-note.logdown.com/',
    image: 'https://test.jpg',
}
-# <meta property="og:title" content="教你怎麼在Rails裡做好基本的SEO"/>
-# <meta property="og:type" content="website"/>
-# <meta property="og:site_name" content="好麻煩部落格"/>
-# <meta property="og:url" content="http://georgiowan-note.logdown.com/"/>
-# <meta property="og:image" content="https://test.jpg"/>

  由於目前沒有用到 Twitter Cards 所以也不方便多加敘述,詳情請至 Twitter dev 欣賞 XDD

4. Rails 設定 meta tags 小技巧

  如果每一頁都要手刻 meta tag 的話,就違背了 DRY 的原則了!為了符合此原則,我們可以透過定義一個設定動作的 Action,而後,在需要設定 meta data 的 Controller 中作 before_action 即可,好像在繞口令,詳情見下文。

  (1) 先在 Application Controller 中定義一個 Action:

application_controller.rb
class ApplicationController < ActionController::Base
 before_action :prepare_meta_tags, if: "request.get?"
 
 protected
  def prepare_meta_tags(options={})
    site_name   = "GeorgioWan's Note"
    title       = "此頁的抬頭"
    description = "此頁的敘述"
    image       = options[:image] || "your-default-image-url"
    current_url = request.url
    
    defaults = {
      site:        site_name,
      title:       title,
      image:       image,
      description: description,
      canonical:   current_url,
      keywords:    %w[這邊可以打些關鍵字],
      og:          {url: current_url,  # 設定 Facebook OG 預設資訊內容

                    site_name: site_name,
                    title: title,
                    image: image,
                    description: description,
                    type: 'website'}
    } # 此為預設資訊內容

    options.reverse_merge!(defaults) 
  # 透過 options.reverse_merge! 讓其他 controller 中可作設定(若沒有額外設定就是預設)  

    set_meta_tags options
    # 最後透過 set_meta_tags 設定 meta data 資訊

  end
end

  (2) 接著,在 layout 的 head 中使用 display_meta_tags 設定每頁的 meta data:

application.html.haml
%head
  = display_meta_tags
  %meta(http-equiv="X-UA-Compatible" content="IE=edge,chrome=1")
  -# 上面此段 meta 是設定 IE 的兼容性。

  (3) 現在每一頁都會有 meta data 了,但內容都是預設的資訊,我們可以在 Controller 中作設定:

test_controller.rb
class TestsController < ApplicationController
 def index
  prepare_meta_tags title: "HERE IS TEST INDEX", description: "TEST INDEX DESCRIPTION."
   # 透過 prepare_meta_tags 傳入想變更的 options

   @page_title       = "HERE IS TEST INDEX"
   @page_description = "TEST INDEX DESCRIPTION."
   # or 我們可以透過 @page_* 作 meta data 設定

 end
 
 def show
  @page_title       = "HERE IS TEST SHOW"
   @page_description = "TEST SHOW DESCRIPTION."
  prepare_meta_tags( og: { title: @page_title,
                             description: @page_description})
    # 也可以對 og 作設定

 end
end

  以上,就簡單的 SEO meta data 設定就完成了。

That's it, DONE!

【參考資料】

 
about 7 years ago

pic by  Ingrid Hong

目的

  在開發的作品中,每篇貼文都希望能透過 fb share 的功能,達到分享該文資訊和宣傳本作品的作用,所以決定在貼文中加入此一功能,讓使用者在宣傳自己的貼文時,也能夠替我們宣傳!

前言

  凡事與社群網站接軌,有好無壞!

筆記

1. FB plugin

  其實要使用這個功能非常簡單,只要到 facebook developer 按部就班的作,就可以完成此功能。

2. Turbolinks + FB plugin

  在開發 rails 中常遇到 turbolinks 導致 header 中 js 沒有 reload 的問題,我們可以透過一些方法解決:

  • 使用 jquery-turbolinks gem 解決 jquery 部份
  • 而 FB plugin 的解決方法則如下:
    fb_plugin.coffee
    $ ->
     # 讀取 FB sdk.js
     loadFacebookSDK()
     # 檢查是否 bind 過 fb-root,若否則執行
     bindFacebookEvents() unless window.fbEventsBound
    
    
    

    bindFacebookEvents = ->
     $(document)
      # 將 fb-root detach 起來
      .on('page:fetch', saveFacebookRoot)
      # 放回 fb-root
      .on('page:change', restoreFacebookRoot)
      # 用 FB.XFBML.parse() re-parse all of the XFBML on a page
      .on('page:load', -> FB?.XFBML.parse())
     @fbEventsBound = true

    saveFacebookRoot = ->
     if $('#fb-root').length
      @fbRoot = $('#fb-root').detach()

    restoreFacebookRoot = ->
     if @fbRoot?
      if $('#fb-root').length
       $('#fb-root').replaceWith @fbRoot
      else
       $('body').append @fbRoot

    loadFacebookSDK = ->
     window.fbAsyncInit = initializeFacebookSDK
     $.getScript("//connect.facebook.net/zh_TW/all.js#xfbml=1&version=v2.5")

    initializeFacebookSDK = ->
     FB.init
      appId : 'YOUR_APP_ID'
      status : true
      cookie : true
      xfbml : true


    3. Modal(popup) + FB plugin

      然而,我遇到的問題是,當我使用 Bootstrap modal 作文章 preview 時,FB share button 只有在第一次 modal show 會出現,而檢視其他 modal 時並沒有觸發更新,導致 FB plugin 失效了!目前我的解決辦法就是在 modal show 時,利用 FB.XFBML.parse() 刷新,以以上的程式碼為範例,於 $ -> 中加入:

    fb_plugin.coffee
    $ ->
     loadFacebookSDK()
     bindFacebookEvents() unless window.fbEventsBound
     $('#myModal').on 'show.bs.modal', ->
      FB.XFBML.parse()
      
      ...
    

    4. 偷吃步?

      當然,如果想快速的達到這個目的,也不用額外寫一支 script,可以在需要用到此 plugin 的頁面直接嵌入 script 就好。

    <script>
     // 官方 plugin
    
     (function(d, s, id) {
      var js, fjs = d.getElementsByTagName(s)[0];
      if (d.getElementById(id)) return;
      js = d.createElement(s); js.id = id;
      js.src = "//connect.facebook.net/zh_TW/sdk.js#xfbml=1&version=v2.5";
      fjs.parentNode.insertBefore(js, fjs);
     }(document, 'script', 'facebook-jssdk'));
    
     // 讀取此頁時直接利用 FB.XFBML.parse() 刷新
    
     $(FB.XFBML.parse());
    </script>
    

    That's it, DONE!

    【參考資料】

 
about 7 years ago

pic by iTarget

目的

  很多地方都會需要用到 render 的技巧,在這邊由於要實作 endless page(或稱infinite scrolling),所以需要動態的透過 ender ujs 來新增頁面中的資料。

前言

  使用 RoR 開發即將步入第四個月,對於 ujs 懵懵懂懂,所以在這邊小筆記一下專案中所學習的!

筆記

  在 controller 中,我們可以透過撰寫 respond_to 指定要 render 的內容,如:

examples_controller.rb
class ExamplesController < ApplicationController
    def example
    respond_to do |format|
      format.js # 如果 client ask for js -> 就 return |format|.js

    end
  end
end

  Controller method(action) 透過 Default Rails mime-types 回傳相對格式作 render,上面的範例中,就會找到 /app/views/examples/example.js.erb 作 render。我們也可以透過block {}來作動作或檔案的 render,如下:

respond_to do |format|
    format.js {
        # 在 client 端執行 alert (javascript)

        "alert('Hello Rails');",
    
        # page not fount

        status: 404,
    
        # render /app/views/your-controller/test_action.js.erb

        action: "test_action",
    
        # 將 test_item 以 json 格式 render 

        json:      @test_item,
    
        # 找到指定的檔案作 render

        file:      filename,
    
        # 直接 render text: Hello

        text:      "Hello"
    }
end

That's it, DONE!

【參考資料】

 
over 7 years ago

pic by mashable.com

目的

  為了讓自己寫的網站擁有編輯器的功能,可以使用一些現成的編輯器來實現,如 ckeditorckeditor-railspagedown 諸如此類,由於已經喜歡上寫 markdown 的感覺,所以選了 pagedown-bootstrap-rails 來實現我的功能。

前言

  了解 Markdown 怎麼寫之後,就習慣使用支援 markdown 的編輯器了嗎?

筆記

1. Gem

  在使用 pagedown-bootstrap-rails 這個 gem 時,我們也得裝 Bootstrap3(Sass)Font Awesome

gem 'bootstrap-sass'
gem 'font-awesome-rails'
gem 'pagedown-bootstrap-rails'

2. scss、js 設定

  scss 的設定為:

app/assets/stylesheets/application.scss
@import "bootstrap-sprockets";
@import "bootstrap";
@import "font-awesome";
@import "pagedown_bootstrap";

  js 中加入已下:

app/assets/stylesheets/application.js
//= require bootstrap-sprockets
//= require pagedown_bootstrap
//= require pagedown_init

NOTE: bootstrap-sprockets 加在 //= require jquery 後,pagedown_init 加在 pagedown_bootstrap 後

3. SimpleForm 中使用 pagedown

  先在 app 中加入 input 與 output 設定:

app/inputs/pagedown_input.rb
class PagedownInput < SimpleForm::Inputs::TextInput
  def input
    out = "<div id=\"wmd-button-bar-#{attribute_name}\"></div>#{wmd_input}"

    if input_html_options[:preview]
      out << "<div id=\"wmd-preview-#{attribute_name}\" class=\"wmd-preview\"></div>"
    end

    out.html_safe
  end

  private

  def wmd_input
    @builder.text_area(
      attribute_name,
      input_html_options.merge(
        class: 'wmd-input form-control', id: "wmd-input-#{attribute_name}"
      )
    )
  end
end

  如此一來,在 simple form 中就可以透過 as: :pagedown 調用 pagedown editor

app/views/examples/_form.html.haml
= f.input :description, as: :pagedown, input_html: { preview: true, rows: 10 }

  以上,就可以使用 Pagedown Editor 了!精美如下圖:

4. 顯示 markdown 型態資料

  在使用 pagedown editor 輸入完文章或內容後,我們呈現時需要把內容由 Markdown 轉成 HTML,我們利用 javascript 將內容透過 Mardown.Converter 轉成 html 即可!主要有兩個步驟:

(1) 於 js 中撰寫 .wmd-output 設定

  • 如果你使用 javascript
    app/javascripts/examples.js
    $(function() {
     $('.wmd-output').each(function(i) {
      var converter = new Markdown.Converter();
      var content = $(this).html();
      $(this).html(converter.makeHtml(content));
     });
    });
    
  • 如果你使用 coffeescript
    app/javascripts/examples.coffee
    $ ->
     $('.wmd-output').each (i) ->
      converter = new Markdown.Converter
      content = $(this).html()
      $(this).html converter.makeHtml(content)
    

注意!以上程式碼縮排請自行重新縮排,以免有全形半形錯誤。

(2) 於 view 中調用 .wmd-output
  這樣我們就可以在 view 中調用 .wmd-output 來呈現囉!

app/views/examples.html.haml
 .wmd-output
  :preserve
    #{@example.description}

為使輸出為安全性之 html,我們使用 sanitize 作逸出(更新 2015.12.16)

app/views/examples.html.haml
 .wmd-output
  :preserve
    #{sanitize(@example.description)}

以上,就完成了在 rails 中使用 pagedown editor 和顯示 Markdown 內容囉!

That's it, DONE!

【參考資料】

 
over 7 years ago

pic by Stanko Krtalić Rusendić

目的

  一個網站在許多地方都需要上傳圖片或照片的功能,例如大頭貼(Avatar)或是商品照片等,而在 rails 中我們有現成的 gem可以使用,你可以使用 paperclipCarrierWave 來實作這個功能,這邊就筆記一下怎麼使用 CarrierWave。

前言

  從完全沒寫過 web app 直接學習 Ruby on Rails,說實在真有點勉強...但接觸 RoR 後我發現這真的是我喜歡的東西,希望能保持這個初衷和熱情!也期待自己能有更多的作品 ccc

筆記

1. Gem

 先添加 gem 到 Gemfile 中

Gemfile
gem 'carrierwave', '~> 0.10.0'
gem 'rmagick'    , '~> 2.15' # 使用 rmagick 作縮放圖片功能

then,

bundle install

【提醒】安裝 rmagick 前得先安裝 libmagickwand-dev。
    由於我的環境是 Ubuntu 15.04,所以透過 sudo apt-get install libmagickwand-dev 安裝

2. 生成 Uploader

 bundle install 後,可以在 rails g -h 看到新增了 uploader,接著

rails g uploader Iamge # 會生成 app/uploaders/image_uploader.rb

3. 添加欄位

 在想要上傳圖片的資料表中添加對應的欄位

rails g migration add_image_to_users image:string # 此例為添加一欄位(image)至 User 中

then, 記得 migrate

rake db:migrate

4. 實作上傳及顯示功能

 首先在 model 中掛載 uploader

app/models/user.rb
mount_uploader :image, ImageUploader

 接著 view 即可作上傳跟顯示,以下為上傳、快取、刪除跟顯示的作法

app/views/users/new.html.erb
<%= simple_form_for @user do |f| %>
 <%= f.input :image %> <!-- 上傳 -->
 <%= f.check_box :remove_image %> <!-- 刪除 -->
 <%= f.hidden_field :image_cache %> <!-- 快取 _cache (讓填表時若有誤,不用重新選擇圖片) -->
 <%= image_tag @user.image if @user.image? %> <!-- 如果 user 已有圖片的話,就顯示 -->
<% end %>

 最後,controller 中記得加入 strong params

app/controllers/users_controller.rb
params.require(:user).permit( :image, :image_cache, :remove_image)

 以上,就完成用 CarrierWave 上傳圖片及顯示的功能囉!但,上傳後圖片大小為原始大小,所以我們可以透過 rmagick 來作 resize。

5. 使用 rmagick

 首先在 image_uploader.rb 中取消註解:

app/uploaders/image_uploader.rb
include CarrierWave::RMagick # 取消註解

version :thumb do
  process :resize_to_fit => [200, 200]
end

 上面透過 thumb,讓上傳時保存原圖,並且產生一個 resize version,這樣想用原圖縮放後的版本都很方便!

app/views/users/show.html.erb
<!-- 原圖尺寸 -->
<%= image_tag @user.image %> 
<!-- 縮放後尺寸 -->
<%= image_tag @user.image_url(:thumb) %>

 從上面可看到,從 _url(:thumb) 即可取得縮放後的圖片了!

That's it, DONE!

【參考資料】

 
over 7 years ago

friendly.png

目的

  本篇來記錄一下怎麼使用 Friendly_id 以及使用 Babosa 這兩個 gem 來達到美化 URL 的效果。

前言

  目前學習 Ruby on Rails 大約一個月半了,從學習的開始,打了一篇安裝文後,就被別的有趣的事情吸引去了,例如追韓劇、Running man、康熙、中國好聲音4、爸爸去哪兒3... 跑題了:P
  總而言之!現在開始使用 Logdown 作筆記跟分享,希望能幫助到需要的人。

筆記

1. Gem :

 先添加 gem 到 Gemfile 中

Gemfile
   gem 'friendly_id', '~> 5.1.0'
   gem 'babosa'

 記得在 console bundle 一下

  bundle install

2. 生成資料表

 在 console 下

   rails g friendly_id
   rails g migration add_slug_to_users slug:string:uniq
   rake db:migrate

 上面的 add_slug_to_users 意思是,加入一個 slug 欄位到資料表 User 中。

3. 撰寫 Model

app/models/user.rb
   extend FriendlyId
   friendly_id :name, use: :slugged

 以上,就完成單純使用 name 來當作路徑,若想自訂路徑格式,就來覆寫 normalize_friendly_id :

app/models/user.rb
   def normalize_friendly_id(input)
     input.to_s.to_slug.normalize.to_s
   end

如果有使用 babosa gem 的話,就可以透過覆寫 normalize_friendly_id 支援中文囉!

4. Friendly.find

 這邊有兩個方法來讓 id 被 friendly 查找:

  • 修改 controller 中的 find
    app/controller/users_controller.rb
       @user = User.friendly.find(params[:id])
    # @user = User.find(params[:id])
    
  • 或是,將 config 中的 finder 功能打開:
    config/initializers/friendly_id.rb
       config.use :finders # 把註解取消,啟動自動查找功能
    
    以上,就完成功能囉! 如果已經有資料了,想直接更新 slug,就直接在 rails console 中執行:
       User.find_each(&:save)
    
    這樣就會自動把 slug 填上了!

2015.12.31 新增

若想讓 slug 隨著資料變更時作 update,我們可以透過定義 should_generate_new_friendly_id? 達到目的

app/models/user.rb
  def should_generate_new_friendly_id?
   slug.blank? || name_changed? # slug 為 nil 或 name column 變更時更新

  end

5. 補充

 如果想符合 SEO,可以在 normalize_friendly_id 裡定義路徑名稱,例如:

app/models/user.rb
   def normalize_friendly_id(input)
     "#{id}-#{input.to_s.to_slug.normalize.to_s}" # what ever you want

   end

 以上面程式碼為例,路徑名稱就變成類似像 xxx/users/1-georgio
 另外,除了透過 friendly_id 去改變 url path 外,其實 rails 本身的解法是覆寫 model to_params method:

app/models/user.rb
def to_params
  "#{id}-#{name}"
end

 就端看想用什麼方案解決囉 :D
 
That's it, DONE!

【參考資料】