使用 Javascript 取得元素的座標

前言

由於使用 Javasript 偵測元素尺寸的方式並不直覺,每個元素有 6 個 DOM 的尺寸的屬性 offsetWidth, offsetHight, clientWidth, clientHeight, scrollWidth, scrollHeight
再加上 offset[Top | Left], scroll[Top | Left], client[Top | Left] 等方向距離的屬性導致這件事變得異常複雜,外加它們都是整數因此在一些操作上會有些誤差。
在開始之前對於那些急性子的人我先提供他們一些對於座標屬性的整理,您可以先大略看過這些整理,後面我們將針對一個實際的例子來練習,這些整理應該可以讓一些老手快速的回復記憶,其實就是因為這樣所以我才紀錄了這篇文章。

繼續閱讀

Psql 學習記錄

何謂 psql

psql 是讓我們可以操作 Postgres SQL 的指令介面。其包含著一大票的參數設定,不過這篇記錄只會紀錄我工作時比較常用到的部分

OK! 首先是當我們安裝好 Postgres 之後怎麼連線

繼續閱讀

rake db 常用指令備註

1
2
3
4
5
6
7
8
9
10
11
12
$ rake db:create          # Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all dbs in the config)
$ rake db:drop # Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)
$ rake db:fixtures:load # Load fixtures into the current environment's database
$ rake db:migrate # Migrate the database (options: VERSION=x, VERBOSE=false)
$ rake db:migrate:status # Display status of migrations
$ rake db:rollback # Rolls the schema back to the previous version (specify steps w/ STEP=n)
$ rake db:schema:dump # Create a db/schema.rb file that can be portably used against any DB supported by AR
$ rake db:schema:load # Load a schema.rb file into the database
$ rake db:seed # Load the seed data from db/seeds.rb
$ rake db:setup # Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)
$ rake db:structure:dump # Dump the database structure to db/structure.sql
$ rake db:version # Retrieves the current schema version number

Jekyll - Psych::Nodes (NameError)

Jekyll 遇到 uninitialized constant Psych::Nodes (NameError)

當 jekyll 的環境遇到 Ruby 2.1.x Psych 2.0.10 會出現不相容問題,所以請

繼續閱讀

Devise 快速上手

devise 使用筆記

Devise 是一套彈性的驗證機制解決方案,它是根據 Warden 為架構的基礎延伸的。Devise 本身具備

  • 支援 rake
  • 架構在 Rails 之上提供完整的 MVC 方案
  • 提供您可以使用多個 Model 在同一時間登入
  • 模組化,您只需要採用您需要的部份
繼續閱讀

ActionPack 雜記

Action Pack

Action Pack 是整個 Rails 的核心部份,由 ActionDispatch, ActionController, ActionView 組成
ActionDispatch 處理接收到的請求(Requests),即網址的部分,ActionController 負責把請求對應轉換成回應(Responses)
接著 ActionController 調用 ActionView 來處理回應的格式(html, js, json, xml) 等

繼續閱讀

Rails 精簡練習

指令練習

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ rails generate scaffold User name:string bio:text birthday:date
$ rails destroy scaffold User
$ rails g scaffold Post name --skip-assets
$ rails db
$ rails console
$ rails g controller posts # 複數
$ rails g controller posts index new create # 後面可以接 action
$ rails g model Post title body:text
$ rails d model post

$ rake db:setup
$ rake db:migrate
$ rake db:migrate:status
$ rake routes

手刻練習

  1. 建立文章控制器 posts_controller.rb
  2. 加入 resources :posts 路由
  3. 加入 列表 action index
  4. 建立 index view
  5. 加入 新增 action new
  6. 加入 new view 重點在 form_for 表單
  7. 新增 post model rails g model post title body:text
  8. 建立 create action 來儲存 model,記得補上驗證
  9. 建立 show view 顯示單筆資料
  10. Model 可以建立之後把 index view 補上 each do
  11. 把 new view 抽出 _form 然後建立 edit action and update
  12. 補上 destroy action
  13. 加上關聯的 comment 留言 model
  14. 補上 model 關聯的設定
  15. 路由設定
  16. 建立 comments_controller.rb
  17. comments 這個 controller 是依附在 posts 底下,加上巢狀 resources 會得到 /posts/:post_id/comments 所以注意表單 form_for([@post, @post.comments.build]) 的部分
  18. 建立 create action 重點在 @post = Post.find(params[:post_id])@comment = @post.comments.create(params[:comment].permit(:name, :body))
  19. 注意 view 的 _form 部分
  20. view 總共會建立 3 個 _form, _comment, 再修改 posts/show 使用 render “comments/form”
  21. 實作 comments_controller 的 destroy action ,記得要從 @post.commetns.find() -> destroy
  22. models/post.rb 記得 dependent: :destroy

1.

1
$ rails g controller posts

2. 修改 config/routes.rb

1
2
resources :posts
root "posts#index"

3. 加入 action

1
2
3
4
5
6
class PostsController < ApplicationController

def index
end

end

4. 加入 index view

1
<h1>Hello world!</h1>

5. 實作新增 new

1
2
3
4
5
6
class PostsController < ApplicationController
#...

def new
end
end

6. 在 app/views/posts/new.html.erb 新增表單,此時沒有 Model ,因為我們在 form_for 用 symbol

1
2
3
4
5
6
7
8
9
<%= form_for :post, url: posts_path do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>

<%= f.label :body %>
<%= f.text_field :body %>

<%= f.submit %>
<% end %>

此時當我們送出 post 的時候,第一沒有 create 的 action 因為 resources :posts 幫我們加上了 8 個路由,扣掉 put, patch 功能一樣有 7 組不同功能的路由

1
2
3
4
5
6
7
8
get "posts" => "posts#index", :as => "posts" # 取得 posts_path helper
post "posts" => "posts#create", :as => "posts"
get "posts/:id" => "posts#show", :as => "post" # 注意單數,用的時候後面要帶 model 參數才能取得 :id
get "posts/new" => "posts#new", :as => "new_post"
get "posts/:id/edit" => "posts#edit", :as => "edit_post"
put "posts/:id" => "posts#update", :as => "post"
patch "posts/:id" => "posts#update", :as => "post"
delete "posts/:id" => "posts#destroy", :as => "post"

7. 新增 Post Model

1
2
$ rails g model Post title body:test
$ rake db:migrate

8. 在 controller 新增 create action

1
2
3
4
5
6
7
8
9
10
def create
@post = Post.new(post_params)
@post.save
redirect_to @post
end

private
def post_params
params.require(:post).permit(:title, :body)
end

這個時候送出會產生錯誤,因為 redirect_to @post 的關係。一般情況下此時只要有對應的 app/views/posts/show.html.erb 就能 work 了

9. 建立 show action 和 view

1
2
3
4
5
<h1><%= @post.title %></h1>

<p><%= time_ago_in_words(@post.created_at) %></p>

<p><%= @post.body %></p>

10. index 列表

1
2
3
4
<% @posts.each do |post| %>
<h2 ><%= link_to post.title, post_path(post)%></h2>
<p class="date"><%= post.created_at.strftime("%B %d, %Y") %></p>
<% end %>

11. 加入 comment

1
2
$ rails g model Comment name:string body:text post:references
$ rake db:migrate
1
2
3
4
5
6
7
8
9
10
11
12
13
# post.rb

class Post < ActiveRecord::Base
has_many :comments
validates :title, presence: true, length: { minimum: 5 }
validates :body, presence: true
end

# routes.rb

resources :posts do
resources :comments
end

因為現在 Comment model 是關聯在 Post model 下面,所以 @post = Post.find(params[:post_id]) 當我們要在 CommentsController 找 Post 需要 :post_id

1
2
3
4
5
6
7
8
class CommentsController < ApplicationController
def create
@post = Post.find(params[:post_id])
@comment = @post.comments.create(params[:comments].permit(:name, :body))

redirect_to post_path(@post)
end
end

form_for

form_for 的用途是建立一個 html 的 form 表單,讓使用者可以透過 form 把資料傳回後端,新增或者更新某個指定物件的屬性。
這個方法(method)有幾種稍微不同的用法,取決於您想從 Rails 的 Model 中自動對應多少東西,或者說自動處理掉 Model 對應的部分
針對一般情況的 Model,我們透過傳入 form_for 一個字串或 symbol 來表示我們要對應的物件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#--- erb

<%= form_for :xx do |f|%>
<%= f.text_field :oo %>
<% end %>

#--- 輸出

<form method="post" accept-charset="UTF-8" action="同一個路由">
<input name="utf8" value="✓" type="hidden">
<input name="authenticity_token" value="EsXGzijdB1Wx8f5FNtO+l8XDEpoA49Ko7nJsZw+Tb5N5BpIQPs3NCw9iO5gwZTkJgyzDm48GHDQCkPwwcFa2WA==" type="hidden">
<input id="xx_oo" name="xx[oo]" type="text">
<form >

#-- 協助記憶

<%= form_for :object do |f| %>
<%= f.text_field :attribute %>
<% end %>

#--- 您也可以單純用 text_field,在 yield 中的 f 變數是一個 FormBuilder 物件,透過與 Model 物件或者 symbol 的定義合作產生表單

#--- erb
<%= text_field :a, :b %>
<%= text_field :person, :name %>
#--- 輸出
<input type="text" id="a_b" name="a[b]">
<input type="text" id="person_name" name="person[name]">

#--- 當 submit 的時候會收到 params[:a][:b], params[:pserson][:name]


#--- 此時如果有一個 @a 的變數傳進來預設就會初始化帶入這些欄位
#--- 範例
#--- view/
<input type="text" id="a_b" name="a[b]">

#--- controller/
class SomeController < ApplicationController
class A
attr_accessor :b
end

def action
@a = A.new
@a.b = "What happen?"
end
end

#--- 如此 view 的 a[b] 就會自動帶入 What happen?

在 form_for 右邊還可以帶入參數

  • url 可以修改 submit 的網址即 action=”url here”
  • namespace 替內部的 input id 再加上特殊的前綴字例如上面本來是 a_b 如果加上 namespce: ‘x’ 就會變成 x_a_b
  • html 其他原生 html 的屬性 e.g. :html => { :multipart => true }
  • 針對表單還有 FormOptionHelper 和 DateHelper 可以針對下拉式選單或日期做處理

注意 form_for 本身不會建立一個獨立的 scope ,意味著您看同時混搭 FormHelper 和 FormTagHelper

1
2
3
4
5
6
<%= form_for :person do |f| %>
<%= f.text_field :first_name %>
<%= text_area :last_name %>
<%= check_box_tag "person[admin]", "1", @person.company.admin? %>
<%= f.submit %>
<% end %>

form_for 搭配 Model

在上面的範例,會根據傳入 form_for 的 symbol 去產生對應的 from 表單 name 屬性,如此一來可以被對應為一個物件,如果您傳入的是字串那麼意思也是一樣的。
我們也可以把 Model 物件本身當作參數傳入,如果 @person 存在且您想編輯它那麼您可以直接使用如下

1
2
3
<%= form_for @person do |f| %>
...
<% end %>

這麼寫的行為幾乎跟您使用 symbol 一樣,不過有些微不同,首先是表單的前綴字即用 model 的 class name , 且 form 會有 id 且會根據 new 或 edit 不同
當然如果不想被綁死您也可以修改

1
2
<%= form_for(@person, as: :client) %>
<% end %>

其次是當該物件已經被初始化或者說有值的時候對應 attributes 的欄位會自動帶入該值,因此如果該 view 已經有個變數 post
也可以這樣寫

1
2
<%= form_for post do |f| %>
<% end %>

在剛剛的範例中,雖然我們沒有明確的指定,但我們還是要使用 :url 來指定 post 的目標。
然而如果我們的物件有透過 resources 指定路由的話路徑就會自動處理
例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<%= form_for @post do |f| %>
<% end %>

# 會等於
<%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post"} do |f| %>

# 如果 @post 是剛初始化的話
<%= form_for(Post.new) do |f| %>
...
<% end %>

<%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
...
<% end %>

# 當然也可以覆寫

<%= form_for(@post, url: super_post_path) do |f| %>

# 或者設定回應的格式
<%= form_for(@post, format: :json) dp |f| %>

# 針對 namespace 路由 e.g. admin_post_url
<%= form_for([:admin, @post]) do |f|>

# 如果是關聯的子物件屬性
<%= form_for([@post, @comment]) do |f| %>

關掉 id

1
2
3
4
5
<%= form_for(@post) do |f| %>
<%= f.fields_for(:comments, include_id: false) do |cf| %>
...
<% end %>
<% end %>

也可以改用別的 FormBuilder

1
2
3
4
<%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
# 照上面的範例如果我們用
<%= render f %>
# 則會 render people/_labelling_form 的樣板

redirect_to 的用法

將瀏覽器重新定向到參數(options)中指定的目標,這個參數可以用下面格式:
(Hash, Model Record, String 搭配 protocol://, String, :back) 總結來說是三種格式

  • Hash - 這種格式是透過搭配 url_for 產生的
1
2
redirect_to url_for(controller: 'posts', action: 'new')
redirect_to :action => "new" # 就算你不加 url_for 預設也會幫您呼叫
  • Record - 一筆紀錄其實就是您取出來的 model 本質上當您 redirct_to @model 時總結來說就是轉址到 model_path(@model)
    對應的路徑即 /posts/:id 就是 “posts#show” (PostsController > show action)。所以雖然我們上面沒有定義 show action 但只要有 view (show.html.erb)就能夠執行是因為 Rails 預設當找不到 action 的時候會直接去找對應 action 的 view。

傳入紀錄的方式在內部是透過 polymorphic_url 來處理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 當我們使用 RESTful 路由時內部的各種狀況範例如下:
#
# # 呼叫 post_url(post)
# polymorphic_url(post) # => "http://example.com/posts/1"
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
# polymorphic_url(Comment) # => "http://example.com/comments"
#
#
# ==== 其他範例
#
# # an Article record
# polymorphic_url(record) # same as article_url(record)
#
# # a Comment record
# polymorphic_url(record) # same as comment_url(record)
#
# # it recognizes new records and maps to the collection
# record = Comment.new
# polymorphic_url(record) # same as comments_url()
#
# # the class of a record will also map to the collection
# polymorphic_url(Comment) # same as comments_url()
  • 字串 - 有使用 protocol:// 例如: http:// 就是直接傳入網址
  • 字串不搭配 protocol 就是 //example.com 會用當前的通訊協定
  • :back - 簡單的說就是 redirect_to(request.env[&quot;HTTP_REFERER&quot;]) 的縮寫
1
2
3
4
5
6
7
8
9
10
11
12
# 完整範例
redirect_to :action => "show", :id => 5
redirect_to post
redirect_to "http://www.rubyonrails.org"
redirect_to "/images/screenshot.jpg"
redirect_to articles_url
redirect_to :back
# 搭配狀態
redirect_to post_url(@post), :status => :found
redirect_to :action=>'atom', :status => :moved_permanently
redirect_to post_url(@post), :status => 301
redirect_to :action=>'atom', :status => 302

routes

routing 模組使 Ruby 具有 rewrite URL 網址的能力,這是一種處理 Request 對應到 controller 及 action 的方式。
主要是用來取代像是 apache 中 mod_rewrite 規則的功能。而在 Rails 中您可以不用動到 Server 的設定,只要設定 config/routes.rb 即可。

建立路由的核心概念就像是列出一張對應表,對應 Requests 。這張對應表告訴系統該怎麼執行,其中必須要遵循一些規則樣式。

1
2
3
4
Rails.application.routes.draw do
# Pattern 1 指定對應的 request 路徑到 controller
# Pattern 2 告訴他們去別的地方
end

其中路由的功能還包含 helper 來協助生成網址

最基本的 Pattern 1 類似於下面範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
get '/products/:id', to: 'products#show' # 把 :id 傳入 params 然後把 request 交給 products_controller 的 show action 處理
get '/products/:id', to: 'products#show', as: 'product' # 對應單數如此一來會產生 product_path helper
# 使用 resources 方式一口氣產出 7 種不同的路由(實際上 patch, put 功能一樣總數有8個)
resources :products # 複數

# 這種 resources 的方式是透過 http method 搭配 url 組成一系列的路由,把 4 組 url + 5 個 http verbs = 7 組功能
# 例如 DELETE /products/17 當 Rails 收到這組 Request 時,就會呼叫 products 的 destroy 並把 params 帶入
get '/photos', to: 'photos#index'
get '/photos' => 'photos#index' # 除了用 :to 還可以直接用 =>
get '/photos/new', to: 'photos#new'
get '/photos/:id', to: 'photos#show'
post '/photos', to: 'photos#create'
get '/photos/:id/edit', to: 'photos#edit'
put '/photos/:id', to: 'photos#update'
patch '/photos/:id', to: 'photos#update'
delete '/photos/:id', to: 'photos#destroy'
# 省略的寫法
match 'messages/show' # 相等於 match 'messages/show' => 'messages#show'
match 'messages' => 'messages#index', :as => 'index'
match "/messages/show/:id" => "messages#show", :constraints => {:id => /\d/} # 限制參數

# 單數的 resource
resources :post
> new_post_path
> edit_post_path
> post_path

# 命名空間的用法
namespace :admin do
resources :photos
end
# 這樣的路徑會加上 admin -> /admin/posts

# 如果單純想把前面沒有 /admin 的路由對應到 Admin::PostsController 則
scope module: 'admin' do
resources :posts
end

# 或者

resources :posts, module: 'admin'

# 當某個 model 底下有子 model 時如
class User < ActiveRecord::Base
has_many :order
end

class Order < ActiveRecord::Base
belongs_to :user
end

# 路由得設法
resources :users do
resources :orders
end

render 方法 文件

渲染回應給瀏覽器的內容

  • 渲染 action (rendering an action)
1
2
3
render :action => "goal"
render :action => "short_goal", :layout => false
render :action => "long_goal", :layout => "spectacular"

Carrier Wave 使用筆記

Carrier Wave

這套 gem 提供一個簡單且非常彈性的方式來協助 Ruby 程式上傳圖片,同時也能夠跟 Rake base 的程式整合的很好 e.g. Ruby on Rails

繼續閱讀