2016年7月29日 星期五

[C++]用function pointer 來取代switch case pattern

引入設計模式的目的是要將程式中變動的部份抽離,這裡的例子用以說明。

應用情境是當我們對一支程式下指令時,這個程式會依照當下的狀態,去執行該狀態下接收到特定指令之後的行為。比較直觀的寫法是在執行command 的function中(ExecCommandMapFunction member function),寫一個switch-case 去區別接收到的指令有哪些。而這樣的寫法雖然直觀又快速,但潛在一個問題,就是程式邏輯跟處理事件的程式是混雜在一起的,如果我們希望日後增加/修改事件時,可以專注於處理事件上,而不用再被程式邏輯給分心,就可以採用以下的模式來改寫我們原有的程式。

原有的程式如下
int ExampleClass::ExecCommandMapFunction(const ControlCommandPack kcmd_pack){
switch (kcmd_pack.command){
case(KeepInspecting) : // Map Command to Controller Member function
pcurrent_state_->keep_Inspecting();
break;
case(StartScan) :
pcurrent_state_->start_Scan();
break;
case(StopScan) :
pcurrent_state_->stop_Scan();
break;
}

}

我們改寫的摘要如下
1. 用物件的方式將處理事件的程式包裝裡一個member function
2. 用function pointer 的方式去編列一個處理事件程式的array,有了這個array,我們就可以用index的方式去調用這些被編列的member function
3. 我們假設程式的邏輯在日後被修改的機會很少,而這部分也在這次改用中改用function pointer 的方式去實作,ExecCommandMapFunction這個member function是程式邏輯的部份,日後也不太會被變動了。
4. 當我們有新增一個command 時,更新CommandMapMethod array大小以及列表內容,然後新增一個member function,專門處理這個command 對應的member function (i.e. CMD2XXX member function)

改用function pointer 的pattern去改寫後:
class itriUltherapyController;
typedef int(ExampleClass::*CommandMapMethod)(ControlCommandPack* control_cmd_pack);
// 定義一個新的型別,這個型別是ExampleClass裡面的member function指標

CommandMapMethod ptable_cmd_map_method_[g_kcommand_map_method_num];
//建立一個CommandMapMethod的array

int ExampleClass::Initialize(...){
        // 略
        ptable_cmd_map_method_[ControlCommand::KeepInspecting] = &ExampleClass::CMD2Kcting;
ptable_cmd_map_method_[ControlCommand::StartScan] = &ExampleClass::CMD2StartScan;
ptable_cmd_map_method_[ControlCommand::StopScan] = &ExampleClass::CMD2StopScan;
}
//初始化array 的內容

int ExampleClass::ExecCommandMapFunction(const ControlCommandPack kcmd_pack){

/*
use function pointer to member function to rewrite the switch-case pattern
*/
fptr = ptable_cmd_map_method_[kcmd_pack.command];
(this->*(this->fptr))(&kcmd_pack);

return 0;
}

int ExampleClass::CMD2KeepInspecting(ControlCommandPack* kpabus_control_cmd_pack){
pcurrent_state_->keep_Inspecting();
return 0;
}

int ExampleClass::CMD2StartScan(ControlCommandPack* kpabus_control_cmd_pack){
pcurrent_state_->start_Scan();
return 0;
}

int ExampleClass::CMD2StopScan(const ControlCommandPack* kpabus_control_cmd_pack){
pcurrent_state_->stop_Scan();
return 0;
}

2016年7月20日 星期三

[Arduino] Debounce 處理按下按鈕瞬間的訊號彈跳行為



程式logic是:
1. 檢查輸入是否有變化
2. 若輸入有變化,檢查是否離上次有變化的時間有一定的間隔(debounceDelay)
3. 若有一定的間隔,將當下的時間點latch住(lastDebounceTime),並檢查是否由High 變為Low
4. 若由High變為Low,則為按下按鈕的事件發生

void loop() {

  input_pin12 = digitalRead(12);

  if(digitalRead(12)==input_reg_pin12){ //1. 檢查輸入是否有變化
           //do nothing
  }else{
 
        if((millis()-lastDebounceTime)>debounceDelay){
        //2. 檢查是否離上次有變化的時間有一定的間隔
                  lastDebounceTime = millis();     //3-1 將當下的時間點latch住
                  if(input_reg_pin12==HIGH){    //3-2 檢查是否由High 變為Low
                             Serial.println("pin 12 triggered");    
                  }  
         }
         input_reg_pin12 = input_pin12;
  }//end of loop

reference:
https://www.arduino.cc/en/Tutorial/Debounce

2016年7月8日 星期五

[Rails]將Database 資料傳送給Javascript 使用,使用Gon

Gemfile
gem 'gon'

application.html.erb
<%= include_gon %>

pages_controller.rb
def index
     gon.db = Page.all
...

回到console
bundle install
rails s

in AWS
RAILS_ENV=production bundle install

則可以在index頁面的Console中輸入gon.db 會得到Objects array

references
[1]Passing data to Javascript

Machine Learning 筆記


這篇文章參考 這篇blog, 其中javascript implement的部份也可以 參考原文章。
內容涵蓋:
1. K-Nearest-Neibor (KNN) Algorithm
2. K-means clustering
3. Genetic algorithm (GA)



1. KNN: K-Nearest-Neibor Algorithm

feature map中最 靠近指定點的K點中,某個分類最多的話,猜測該指定點屬於該分類。

比如說,若你想知道你的所在地是德國還是奧地利的領域,則先選定一個數字K,假設K是5,則你找出離你最近的5個人,若其中有3個德國人,2個奧地利人,因為德國人比較多,所以可以猜測你身在德國。

在這個例子中,因為我們己經知道所有鄰居的國籍,所以這種方法是屬於supervised 方法。

這個方法的限制:

不同分類的data應該要彼此分開,越分開則分類的效果越好。比如說如果今天社區內德國人跟奧地利人很平均的居住在區域內,那這個方法推測的效果很不準;但如 果社區內有明 顯的德國圈、奧地利圈,那這個方法推測的分類就會很好。

另一個限制是當data很多的時候,計算量會非常的大,因為必須一個個去計算距離。可以透過資料的快篩來減少需要計算距離的資料量。


2. k-means clustering

通當我們不會知道取得的data有哪些分類,我們可能只能嘗試從data中擷取出一些feature,並將這些資料作分 群 。

演算法:

1、將資料畫在feature map中,新增k個"分類中心"(cluster centroid)任意分佈在feature map內
2、每筆資料將之分類,取決於這 筆資料離哪一個分類中心最近。
3、屬於同類的資料算出其中心,並將分類中心移到新的這個中心。
4、若中心有位移,回到步驟二

檢視演算法的結果

首先我們會面對到演算法的結果可能落在local optima,意思是演算法可能收 斂在一個相對穩定的結果,但並不是最好的結果,因為k-means clustering這個方法是data-driven的演算法,根據資料 群們本身的資料而有的結果,演算法給我們一個還不錯的答案,但未必是我們要的標準答案。要解決這個問題,其一是在每次穩定的時候,給予一個微調讓演算法再收斂一次看是否得到相同的結果。

第二個方法是組成一個"委員會",檢視若這個演算法重覆執行很多次時,是否可以得到一致的結果。但這個方法需要演算法可以快速的執行完一次收 斂才合適。

其他應用:

想像你剛開了一個電子商務的網站,你想要分析作用者的行為,作為改善網站的 依據,於是你開始收集每個使用者造網這個網站的行為,每個使用者會有每次觀看的網頁數,買的物品的價格,購買的數量等等feature, 之後就透過k-means 去作分 群,可以得到其中一個族群他的觀看網頁數很少,3-5頁(估且稱之為 window shopper),另一個族群是瀏覽了大量的分頁,但是每次下手都是單價高的產品(這個 族群叫作…估且稱作功課作足一次攻頂的消費者),有了這些資料,商家就具體的知道自己的客群是什麼了,就可以在網站上針對這不同的客群去作加強的行銷。

3. Genetic Algorithm

這個演算法是受生物演化論的啟發, 這個演 算法的概念是要給machine 一個演進的方法,朝我們希望的方向。

直接用例子來說明:
我們想要讓電腦印出 Hello,World,但是我們不告訴電腦要確切的打出哪些字,所以我們要給電腦一些限制以及提示。 限制是電腦印出的字要 5個字元,然後逗號,然後再5個字元。而提示是我們會 訂一個標準(cost function)去檢示電腦提出的字串得到幾分,如果字母越接 近,cost function就越接近零,越靠近標準答案。比如說,Hello, Worlf 會得到 2分,因為worlf的最後一個字f 跟標準答案 d 在字母表上差了 2個位置。

演化與淘汰 (物競天澤)

Genetic Algorithm 每一次的iteration都叫作一個世代(Generation),而這個世代中的成員會被淘汰(取cost function表現最差的)。而其他成員會"交配"(Mate),並產生下一個世代。比如說以下兩個成員若進行交配,估且將交配這個行為定義為前 5個字元與後5個字元作交換
  • Parent
    • Hello, wprld (cost function = 1)
    • Iello, world (1)
  • Child
    • Hello, world (0)
    • Iello, wprld (2)
可以得到演化的結果,可能會產生cost function較好或較差的下一世代。

變異(Mutex)

跟自然界的演化一樣,會有變異/突變產生,而變異的產生在演化中的角色非常的重要,因為變異很可能產生有能力適應自然的下一代。在程式中的實現,就是每幾個世代中隨機產生突變。比如說aellp, wprld (9)的下一個世代突變為 Hello, wprld(1)。變異的產生有機會將世代脫離local optima的情況。

2016年6月23日 星期四

[python]照片批次縮小的小工具

應用情境:相機拍完的照片大小都大約是6~7MB,6000X4000解析度,但是自家用的螢幕解析度是1920X1080,我們在螢幕上看到的照片其實都是壓縮後的照片,換句話說如果只是在螢幕上觀看,或社群網站上觀看,是不需要那麼大的照片。所以我們可以撰寫一個小程式讓照片的長邊大小調整到1200,所以一張6000X4000的照片(6~7MB)可以調整成1200X800的解析度(~150kB),資料量整整少了25倍以上,但是視覺效果又看不出來,這對動輒上傳半天的活動照片而言,時間減少太多了,真的是很划得來。

使用到的函式庫:Image
程式說明:將原本照片存放到/original/這個新資料夾,然後將照片壓式寬度1200的照片

source code: resize.py

2016年6月22日 星期三

[arduino]製作Atmega32u4-based電路並搭載arduino,使用Arduino Duemilanove 作為bootloader 燒錄器

這篇文章的目的如同文章題目,參考了arduino uno的電路自己設計製作了一個arduino-base的簡單電路。

Outlines:
1. 上件:Atmega32u4, Oscillator, USB port,測試Atmega32u4 是否運作
2. 使用Arduino Duemilanove 作為bootloader 燒錄器

Materials:
1. 自己設計的電路板:Atmega32u4以及週邊電子材料
2. Arduino Duemilanove



2016年6月17日 星期五

Websocket的解決方案:Client Browser使用 Javascript,Server使用python或node.js


Websocket 是一種Internet上資料傳送與接收的一個新技術。只要能掌握這個技術,就可以實現許多需要透過網路傳送資料的應用程式。

Server: python(tornado), node.js(socket.io, ), ...
Client(Browser): javascript (socket.io)

是否可以將websocket Client連上 websocket server,而不論server是用哪一種語言實現的。其實理論上是必然可行的,因為websocket只是一個傳輸資料的protocol,因此python, node.js等等畢竟都只是開發的工具而已,只要傳送/接收的資料符合protocol,那資料理論上是可以傳輸無阻的。

這篇文章是一個學習記錄的過程,目的就是要實驗這個理論,並且更熟悉實務上會用到哪些函式庫,以及這此函式庫是否有什麼使用上的限制。網路上有非常多的資源說明怎麼架設websocket server/client,但是通常都是單一方法的說明。

環境說明:Server端架設在AWS EC2 上

1. 在server端使用python 搭配tornado library架設websocket server
2. 在client端建立websocket client
3. 在server端改使用node.js 架設websocket server
4. 在server, client端使用node.js中使用socket.io library

1. 在server端使用python 搭配tornado library架設websocket server
[source code]websocket server written in python using tornado module

2. 在client端建立websocket client
[source code]websocket client written in html/javascript 

3. 在server端改使用node.js 架設websocket server
[source code]websocket server written in node.js
[source code]修改後的websocket client written in html/javascript

4. 在server, client端使用node.js中使用socket.io library
[source code]websocket server written in node.js with socket.io module
[source code]websocket client using socket.io module

references: [1]使用 Node.js 與 Socket.IO 建立即時性(Realtime)網頁應用程式 App

2016年5月26日 星期四

研究Amazon AWS Lambda: (三)Lambda function如何被喚醒


pull events v.s. push events

Lambda是用什麼機制去偵測特定的事件是否發生的呢?比如說Lambda要怎麼知道Amazon S3 bucket中的資料是否變動?

答案是AWS Lambda採用了pull(拉)與push(推)這兩個方式(event modle),可以分別類比成主動與被動的行為。

push(Lambda是被動的)

有的事件產生者會主動將event 的發生push給AWS Lambda知道並使lambda function被執行,在這個情形下,AWS Lambda是處於一個被動的狀態;

pull(Lambda是主動的)

而另一種模式則是事件產生者只發佈目前有event發生,但需要AWS Lambda主動去將該event pull回來並使lambda function執行,這就是這兩種event model的基本概念。

當AWS Lambda需要使用AWS 其他資源(比如S3 bucket, 或讀取DynamoDB table),Lambda必須獲得該有的授權。更多資訊參考AWS Lambda Permissions Model

Event source mapping

因為我們不只會有一個event 與 Lambda function,因此他們之間需要存在一個對應關係

for "pull" case

在AWS Lambda,用相關的AWS Lambda API產生相關的mapping

for "push"case

在事件的產生者端,由事件產生者提供的APIs來產生相關的mapping

More About Pull Event Model

當event sources 是AWS Kinesis, AWS DynamoDB Streams時使用。
如下圖,這個例子[1]是一個App將資料寫入Amazon Kinesis,而Lambda會去偵測是否有資料增減的事件產生,若有,則呼叫Lambda function執行,這個主動將溪件取回(pull)來處理的動作就像圖示中的"手"一樣。

More About Push Event Model

有些event sources可以主動將event 送(i.e. "Push")給AWS Lambda,換句話說,AWS Lambda是一個被動的角色。所以Lambda function是由event source所直接喚醒的。

有push功能的event souces有Amazon S3, Amazon SNS, Amazon Cognito, Amazon Echo等。

舉其中Amazon S3的例子而言,Amazon S3有提供一個API讓使用者可以設定當特定的事件發生時,要引發哪一個Lambda function。更多說明請參考Configuring Amazon S3 Event Notifications.

如下圖,這個例子的使用情境是使用者在Amazon S3 Bucket中產生一個物件,當Amazon S3 偵測到這個事件時引發指定的Lambda function(i.e. Amazon S3 "push" event and "invoke" AWS Lambda function)。



references:
[1]http://docs.aws.amazon.com/lambda/latest/dg/intro-invocation-modes.html

2016年5月24日 星期二

研究Amazon AWS Lambda: (二)新手資源的筆記

AWS Lambda官網建議新手先閱讀的資料,在這篇做些筆記。

References
[1] AWS Lambda webpage: Landing page of official AWS Lambda,基本概念
[2] AWS Lambda: How It Works.:工作原理
[3] programming-model:與實作語言(node.js, JAVA, python)相關的程式結構


Lambda的基本概念[1]

首先是lambda的操作流程

  1. 使用者上傳程式碼到AWS Lambda
  2. 設定Lambda在什麼情況下被喚醒
  3. Lambda開始運作,使用者可以轉身去做其他事
  4. 使用者付費:Lambda被喚醒做事情的這段時間工資由使用者付擔




Lambda可以做什麼事?


  1. 資料處理
    1. 即時檔案處理:資料上傳後,立即被Lambda處理。比如建立圖片縮圖,影片轉碼、檔案編索、驗證內容、篩選資料。
      可以參考The Seattle Times使用Lambda來調整影像的大小,以符合手機/電腦不同的螢幕大小
    2. 即時串流處理:可以用來追蹤應用程式活動,相關應用應與社群行為分析有關。這部份關聯到AWS KINESIS這個服務,目前還不熟悉,在這篇文章先跳過這個部份。
  2. 作為後端程式:處理web, 行動、物聯網及第三方api的request
    1. 行動後端
      使用情境:當使用者在前端上傳一個近況,透過API通知Lambda,Lambda找到使用者的朋友清單,並發出SNS訊息給朋友們
    2. Web application
      使用情境:AWS S3上架設了一個天氣查詢網站,當使用者按下查詢當地天氣的按鈕時,透過API通知Lambda,Lambda在database中取得當地天氣的資料並將該資料回傳給使用者。
這些應用與使用情境其實都是遵行Serverless的概念,程式設計師沒有調校server的需求,只需要設定Lambda這個助手要做什麼事,以及什麼情況下要做這件事。


Lambda的工作原理[2]


  1. lambda的核心:lambda function and event source
  2. lambda function 可以透過 HTTPS or AWS SDKs 喚醒
  3. lambda function由code, configuration information組成。configuration information包含了你想要用多少計算資源(記憶體的量),lambda 存取AWS 資料的權限。在configuration中,使用者也需要去設定一個handler function來處理event data 
  4. Lambda使用的記憶體由programmer指定,而CPU方面,AWS Lambda會自動根據記憶體的大小去分配,基本上是等比例增加的,比如說使用256MB記憶體空間的Lambda function,比起使用128MB記憶體空間的Lambda function的CPU大小是兩倍。
  5. Event Sources: 在以下情況時,可以作為讓Lambda 執行的事件
    1. AWS services 選項非常的多,只能先選幾個介紹

      Amazon S3 是Amazon提供的雲端空間服務,使用者可以上傳檔案到這個空間中,而S3內部資料的增減,也可以作為trigger Lambda 的事件

      Amazon DynaoDB是AWS 的雲端資料庫服務,資料庫的更新也可以作為trigger event

      其他還有Amazon Kinesis, Amazon SNS, Amazon SES, Amazon Cognito, Amazon CloudWatch Logs, AWS CloudFormation, Amazon CloudWatch Events, AWS Config
    2. Scheduled Event
      AWS除了是event-driven的方式被呼叫,他同時也可以作為一個有固定行程的服務,programmer可以設定Lambda function被固定執行的頻率(比如每天執行、每小時執行,或每週執行)。
    3. Other Amazon services (Amazon Echo)
      這是一個很酷的功能,Amazon Echo是包含喇叭、mic的硬體,而Alexa是搭載在上面的語言助理軟體,Alexa有提供一個APIs,去呼叫Lambda進行客製的功能。
    4. On-Demand Lambda Function Invocation: Over HTTPS
      使用Amazon API Gateway將GET, PUT這些request傳給Amazon API Gateway service,然後喚起Lambda function,參考Amazon API gateway
    5. On-Demand Lambda Function Invocation: Build Your Own Event Sources
      使用AWS SDKs or AWS Mobile SDKs 就可以產生Lambda 可以接收的事件

Programming Model[3]

Lambda function 包含這些核心:

  1. Handler:當Lambda function被喚醒時,會馬上呼叫Handler。AWS Lambda將event data作為第一個參數給handler。
  2. The context object and how it interacts with Lambda at runtime :AWS Lambda傳送給handler的第二個參數是context object。簡單來說,context object是用來當Lambda function與AWS Lambda之間handshake的溝通介面
  3. Logs
  4. Exceptions

2016年5月23日 星期一

研究Amazon AWS Lambda: (一)Overview

前言

參加AWS Commit Taipei 2016 時第一次接觸到AWS Lambda 這個產品,直覺這個服務可以做的應用非常的多,但一切的一切有些模糊,於是想要花點時間來研究整理一下這個服務的操作流程、有哪些使用情境、跟其他服務有什麼差別,以及使用這個服務需要多少錢,有什麼限制等等這些資訊。此外想寫這個系列文章的主要動機也是發覺網路上沒有太多用中文寫的AWS Labmda介紹,於是也想要寫下並分享相關知識的記錄。

AWS Lambda是什麼?

save your time:watch Introduction to AWS Lambda@youtube first

使用者可以上傳程式碼(目前支援node.js, JAVA, 以及python),以及"Lambda function "到Lambda上,而AWS Lambda會負責管理好server端如何運作這些程式碼[1]。如下圖,Lambda在 Amazon 雲端運算服務(AWS)中是屬於"運算"服務中的其中一項。

使用者可以在以下情況中使用AWS Lambda:
1. 把Lambda當作一個事件處理的幫手(event-driven compute service):比如說當存放在AWS S3 bucket的資料有變動時,Lambda就會自動被喚起去執行使用者囑附的工作。
2. 使用者透過Amazon API Gateway或API calls等方式發送命令(HTTP requests)給Lambda,讓Lambda去執行指定的動作

Lambda只有在需要的時候才會被執行(特定事件或使用者下指令),並且會根據流量auto-scale。使用者不太需要擔心需要實作適用小流量的版本(few requests per day),或大流量的版本(thousands per second)因為auto-scale也是Lambda提功服務的feature之一。

Lambda跟EC2的差別

比較明瞭的區分方式是在於使用者需要面對什麼。

EC2,使用者面對的是一台電腦,一台重灌好的電腦,但是什麼都沒有,所以今天如果使用者想要把這台電腦當成是網站的server,那使用者知道要怎麼安裝apache或是nginx等hosting service,或是當使用者想要在這個電腦上學習怎麼用linux環境寫C的程式,那使用者就要先確認好compiler的指令、參數等。換句話說,如果使用EC2,使用者除了自己要用程式實現的功能外,也需要熟悉怎麼去架設正確的環境。另一個角度去思考這件事情呢,就是使用EC2,使用者保有很大的空間去調校系統,比如環境的語言啦,網路設定啦,儲存運算要用多少資源等等比較系統層面的設定。

Lambda,使用者面對的是怎麼用指定的語言去做自己想要的功能,並且需要閱讀相關的開發指南,去把指定的程式(lambda function, and node.js/JAVA/python file)在AWS console中設定以及上傳。所以說使用Lambda的話,在環境架設方面需要花費的心力就省去很多。總的來說,因為Lambda的使用環境是限制的,所以只要是環境限制的範圍內(在develope guide中可以找到的功能),使用者都可以專心在怎麼實作出想要的功能,而系統層面的問題,Lambda會處理掉

"AWS Lambda manages the compute fleet that offers a balance of memory, CPU, network, and other resources. "[1]

題外話,筆者很喜歡AWS Commit 中聽到的一句話,是關於Amazon的企業文化:只要一件事情被重複做了兩次,那這件事情就需要/可以被自動化。也許Lambda會被推出,也是工程師因為不停的調校系統環境搞得煩死了,索性設計出一個服務來自動調校系統環境而衍生出來的服務呢!

相關應用

以下是筆者想到使用AWS Lambda的服務(草稿中)
(1) 固定每天去擷取open-data並產出當天版本的api,相關技術可以參考将 AWS Lambda 用于计划的事件
(2) 前端每五秒拍下一張影像並上傳到AWS S3,而Lambda在AWS S3資料有新增的時候被啟動,運行讀取影像,影像辨視人臉,並將人臉存入資料庫的動作,再把資料庫的更新顯示在前端的介面上(AWS Commit 現場DEMO Case)

References:


2016年3月30日 星期三

2016年3月27日 星期日

[rails]HOW TO: 將txt格式資料匯入資料庫的頁面

參 考https://richonrails.com/articles/importing-csv-files index.html.erb

說明:
我們想要在records底下新建一個頁面([project path]/records/import/),在這個頁面使用者可以選擇想要上傳的local端檔案,然後按下import按鈕匯入資料。

首先是要處理當使用者輸入網址/import/時,由show action 處理,並 render import.html.erb
app/controller/records_controller.rb
  def show
    if valid_page?
      render params[:id]
    else
      render file:"public/404.html", status: :not_found
    end
  end
         

在指定的頁面 (在這裡是import.html.erb)中加入可以上傳檔案的html
<h1>import csv file</h1>
    <%= form_tag import_records_path, multipart: true do %>
      <%= file_field_tag :file %>
      <%= submit_tag "Import CSV" %>
    <% end %>
 

目前為止rails 還不知道import_records_path會回傳什麼path,所以需要在config/routes中定義:
resources :records do
      collection{post:import}
end

接下來當在index.html.erb頁面,使用者選擇本機端的特定檔案後,按下Import CSV 按扭,router會導引到records/import/ 這個path(並將特定 檔案的path存到變數:file),而當rails接收到records/import這個path ,會去執行records controller底下的#import action。

於是我們要定義import action. edit app/controller/record_controller.rb

def import
      Record.import(params[:file])
 end

我們希望使用import這個method來將資料匯入,目前為止還沒有定義這個method,而這個method是Record Object底下的方法,所以我們接下來要定義他
app/models/record.rb
  require 'csv'
  def self.import(file)
    CSV.foreach(file.path, headers: true) do |row|

      record_hash = row.to_hash #把csv檔的以列(row)為單位pop-out
      record = Record.where(id: record_hash["id"])

      if record.count == 1
        record.first.update_attributes(record_hash)
      else
        Record.create!(record_hash)
      end # end if !record.nil?
    end # end CSV.foreach
  end # end self.import(file)

這段程式會把csv檔的第一行作為index,存到record_hash這個變數中,找到指定的欄位
當原始資料庫中沒有值時,會初始之,若已經有資料了,就加上去。

example.csv
program_id,distance,property_id,minutes,seconds,user_id,owner_id,month,date,year
1,1,1,2,2,1,1,3,22,2016
1,1,1,2,2,1,1,3,22,2015
1,1,1,2,2,1,1,3,22,2014

圖一:加入上傳資料功能後的頁面 


圖二:選擇檔案後,會在頁面上顯示路徑

圖三:資料成功加入資料庫


延伸應用:可應用於使用者上傳檔案,運算後輸出


2016年3月23日 星期三

Growth School 的Rails 商務網站X即戰力班 學習心得

由XDite辦的rails 商務網站X即戰力班,趁著記憶猶新好好的整理一下心得分享。

這門課幫助我哪些

時間、時間、時間

至少可以說出這門課三個節省時間的模式,還有因為感受很深所以說三次。

  1.  提出問題到解決的時間 : hours -> minutes
  2.  完成一個夠水準作品的時間:months -> weeks 
  3.  達到知道怎麼發問,問什麼問題會得到解答的能力:months ->weeks

以往曾經嘗試自學過php, python+django架構,但是結果總是在完成一個最陽春功能後開始撞牆(開始嘗試約二、三個月),然後開始fade out,轉頭嘗試其他的東西。也許部份的原因是注意力不集中症後群(!?),最重要的原因也是因為沒有成就感,覺得要完成一個簡單的作品就這麼難,何況是自己腦海中刻劃的那有點複雜的作品原型呢?人的本能反應就是轉頭嘗試其他解法。

仔細想了其中的原因是什麼,一是不熟悉這種自己找答案自學的方式,網路上接收到的資訊太雜亂了,有時根本不知道問題在哪邊,以try and error方式花費了不少時間,要解決一個問題可能是以小時為單位的。二是不知道怎麼問問題,所以問題很容易一直發散。這堂課的規劃幾乎沒有讓人有以前不停撞牆的感受。

有如工具書般的教材

可讀性很高,極少有看了但不知道要加在哪的程式碼,這點很細節不容易被注意,但是非常重要。而且只要照著步驟去做,一定可以完成。自己遇到一半以上的問題,幾乎都不是教材的錯誤,而是來自於自己少做了其中哪幾個步驟。曾經嘗試寫過教學文件的人都知道要寫一個"讓九成以上的人照作就可以得到成功的結果"的教材有多麼的困難,顯示得出這份教材是非常用心在撰寫的。

經驗傳承

講師XDite傳授了許多心法,舉凡開發產品時會用到的技巧,開發者常遇到的問題QA,如果說怎麼寫rails網站是招式,那這些技巧、經驗就是內功了,這些經驗往往是經過大大小小專案才洗鍊而成的心法在這堂課也一併傳授,很受用。

此外這堂課也有非常強大的助教群,人數上也很足夠。每當實作上有任何問題的時候,助教也可以馬上協助。甚至我覺得助教在課堂上的跑步速度可能每經過一季課程都有在被檢討進步的(!?)雖然這樣說有點誇張,但總結來說我覺得是一門有用心經營的課程。(感謝強大的助教群,以極快的速度回應協助我們)

上這門課遇到的難題

ruby的文法

rails是用ruby寫的網頁框架,在語言特性的熟悉上也遇到一點點的麻煩。有寫過python的人可能也會跟我類似的經驗(因為python的文法限制較多,連空格都有嚴格規定),在讀rails的code時,會對於許多文法感到疑惑。因為rails有許多省略的寫法,但是功能、目的是一樣的,比如說引用一個函式 adder(a,b),也可以寫作adder a,b,這兩個是等效的。讀code時最常遇到在這種等效的statement上困惑的,而這種問題對我來說最不好線上發問,因為不知道怎麼問。只有當助教在的時候直接指著句子問才恍然大悟。意謂著在這個問題上會分心。也許在上這門課前的準備工作可以先準備這方面的背景知識

 rails 專屬的潛規則!

在上這門課以前,聽說rails 的學習曲線很抖,但是無法體會,但是在上了這幾次課,寫了許許多多的課程、作業時終於有所體認,因為rails有好多的潛規則(或稱rails的黑魔法)需要去理解、背誦。但也因為這些黑魔法,讓rails這個框架非常的強大,因為他在背後做了非常多的事,而因為我們不知道rails有多自動與強大,因為不知道為什麼,所以學習的時候會恐慌。

也許這堂課就是教我們不要一直想問為什麼、為什麼、為什麼,先背起來、先做起來,然後自己就會懂了。這時不禁想到以前補習班老師的座右銘:為什麼我會,因為我有背!此言不假。

如果說rails學習曲線當成是一條極度抖上的山坡,那我覺得這堂課很像是在旁邊打下的一階階樓梯。路還是一樣陡,但是至少我們可以一階一階知道自己在哪,而當自己覺得好像有點迷路的時候,只要舉手、發問就可以被引導回路徑,繼續這條陡上的路。

什麼人我覺得推荐來上這門課

腦中有想法,但是不知道怎麼實現的人

如果前面所說的這門課會節省你非常多的時間,先求有再求好,這堂課可以大大的縮減"先求有"的時間。Rails本身讓網站功能的開發大幅減少,而Rails 商務網站即戰力班則是幫助度過Rails前期學習曲線很抖的階段。

2016年3月22日 星期二

使用rails產生靜態網頁(四):加入超連結

使用rails產生靜態網頁(四):加入超連結

這一回我們要介紹的是如何在pages/test這個分頁中使用超連結連結到pages/index 這個分頁。

方法很簡單,就是在template中使用helper。所謂的helper是指在template中將資料轉換成html字串。在我們的例子中,link_to這個helper就是要產生一個超連結的html字串(i.e. <a href...(略)...>)。所謂的資料是"index"這個字串,這個字串包含了我們想要去的分頁名稱。而helper是一個函式,我們不用輸入繁雜的html,將資料作為函式的輸入,就可以產生繁雜的html。

而在這裡我們也自訂了一個函式to_page(),可以回傳完整分頁的path

app/views/pages/test.html.erb
<%= link_to "index", to_page("index") %>

vim app/helpers/pages_helper.rb (註:在產生pages controllers時,這個pages_helper.rb 自動產生)
def to_page(specific_page)
   return "http://localhost:3000/pages/#{specific_page}"
end


補充:使用named routes設定的方式,不需要自定helper,只要在config/routes.rb中修改,即可以有helper 使用:

config/routes.rb
get 'pages/:single_page' => 'pages#show', :as=> 'page'

則views中則可以改為
<%= link_to "index", page_path("index") %>

說明:named routes 命名路由,可以幫助我們產生URL helper :as=page,rails就會產生一個page_path(相對路徑,一般較常用),以及path_url(絕對路徑,特定情況下需要,比如email application) 的 URL helper

2016年3月15日 星期二

使用rails產生靜態網頁(三):用變數來統一處理大量不一樣的分頁

這一回我們要解決當特定的template不存在時,產生的error。

處理的機制就是在controller中,判 斷params[:single_page]這個內含分頁名稱的變數,是否有對應的template存在,如果有,則render該template,若否,則統一導向一個指示網址錯誤的提醒網頁。


controller改寫為
class PagesController < ApplicationController
 def show if valid_page?
       render template: "pages/#{params[:single_page]}"
 else render file: "public/404.html", status: :not_found end end private

 def valid_page?
       File.exist?(Pathname.new(Rails.root + "app/views/pages/#{params[:single_page]}.html.erb")) end

 end

值得一提的是,ruby要完成一件事有許多種作法,在這裡render template的方式是
render template:"page#{params[:single_page]}" 將path作為值傳給template這個變數,另外還有一個作法是
render params[:single_page] 這樣也可以達到一樣的作法喔,而且看起來乾淨多了。

使用rails產生靜態網頁(二)

 在上一回中,我們產生了一頁靜態網頁,現在我們要繼續產生更多筆的靜態網頁。
我們產生一個靜態網頁常常產生的分頁:about page, contact page

首先是跟router說明在接收到不同網址的要求時,應該怎麼辦。在config/routes.rb 中加入兩行
  get "/pages/about" =>; "pages#about"
  get "/pages/contact" =>; "pages#contact"

意思是當 瀏 覽器輸入 localhost:3000/pages/about/ 以及localhost:3000/pages/contact/時會分別對應到 pages Controller底下的about, 以及 contact action

再來就是設定Controller, 以及 View這二位一體的部分了。
(原本是三位一體,但是靜態網頁沒有model的部份)


a)Controller
app/controllers/pages_controller.rb 加入contact, about action的定義
def contact
end
def about
end

b)View
app/views/pages/contact.html.erb 編輯contact 靜態網頁模板

<h1>contact</h1>

app/views/pages/about.html.erb 編輯about 靜態網頁模板

<h1>about</h1>


這樣我們就有多筆靜態網頁了!但是應該有更省力的方式…

2016年3月14日 星期一

使用rails 產生靜態網頁(一)

keywords: static website

"在學習rails之前,我會javascript, css, html等 靜態網頁,而學習rails之後,我知道怎麼使用rails快速的開發一個網站,但是反而不知道該如何利用rails完成一個再簡單不過的靜態網站…"

如果您有這樣的想法,那這一篇文章就是逐步的解說如何使用rails建立一個簡單的靜態網頁。rails是個功能強大的框架,但是有時候我們想要做的反而是一個簡單的功能。對於從靜態網 頁開發者跨入 "網站"開發領域的開發者而言,也許會有困擾是如何使用這一個功能強大的工具,來完成自己之前熟悉的功能

2016年2月29日 星期一

[rails] 把資料庫先清空再繼續

把資料庫先清空再繼續
rake db:purge
rake db:migrate

2016年2月26日 星期五

[rails]將新的欄位加入model

rails generate migration add_[column]_to_[model name] [column]:[type]

rails generate migration add_title_to_groups title:string
rails generate migration add_description_to_groups description:text

2015年1月11日 星期日

獅子頭X小白菜

家裡寄來了獅子頭,自己再去買了小白菜、金針菇、魚餃,用醬油、鹽調味,用小火燉到白菜軟嫩,便是鮮甜的清燉獅子頭。為了增加飽足感,再加上冬粉即可